Merge branch 'crudtesting'

This commit is contained in:
eelke 2022-04-03 10:05:09 +02:00
commit 03b4194193
10 changed files with 169 additions and 88 deletions

34
pgsql/Pgsql_IResult.h Normal file
View file

@ -0,0 +1,34 @@
#pragma once
#include "Pgsql_ResultConstIterator.h"
namespace Pgsql {
class IResult {
public:
virtual operator bool() const = 0;
virtual int tuplesAffected() const = 0;
virtual int rows() const = 0;
virtual int cols() const = 0;
virtual const char* getColName(int idx) const = 0;
virtual const char* val(int col, int row) const = 0;
virtual Value get(int col, int row) const = 0;
virtual Oid type(int col) const = 0;
virtual bool null(int col, int row) const = 0;
ResultConstIterator begin() const
{
return ResultConstIterator(*this, 0);
}
ResultConstIterator end() const
{
return ResultConstIterator(*this, rows());
}
};
}

View file

@ -1,60 +1,19 @@
#ifndef PGSQL_RESULT_H #ifndef PGSQL_RESULT_H
#define PGSQL_RESULT_H #define PGSQL_RESULT_H
#include "Pgsql_IResult.h"
#include "Pgsql_Row.h" #include "Pgsql_Row.h"
#include "Pgsql_ErrorDetails.h" #include "Pgsql_ErrorDetails.h"
namespace Pgsql { namespace Pgsql {
/** \brief Non-copyable but movable wrapper for a postgresql result. /** \brief Non-copyable but movable wrapper for a postgresql result.
* *
* This class makes sure the result is removed from memory. It also supplies an iterator for the * This class makes sure the result is removed from memory. It also supplies an iterator for the
* rows from the result facilitating also the use of the cpp for each. * rows from the result facilitating also the use of the cpp for each.
*/ */
class Result { class Result: public IResult {
public: public:
class const_iterator {
public:
const_iterator(const Result &r, int rw)
: m_row(r, rw)
{}
const_iterator operator++()
{
const_iterator t(*this);
m_row.next();
return t;
}
const_iterator& operator++(int)
{
m_row.next();
return *this;
}
bool operator==(const const_iterator &rhs)
{
return m_row == rhs.m_row;
}
bool operator!=(const const_iterator &rhs)
{
return !operator==(rhs);
}
const Row& operator*()
{
return m_row;
}
const Row& operator->()
{
return m_row;
}
private:
Row m_row;
};
Result() = default; Result() = default;
Result(PGresult *result); Result(PGresult *result);
~Result(); ~Result();
@ -65,7 +24,7 @@ namespace Pgsql {
Result(Result &&rhs); Result(Result &&rhs);
Result& operator=(Result &&rhs); Result& operator=(Result &&rhs);
operator bool() const; virtual operator bool() const override;
ExecStatusType resultStatus(); ExecStatusType resultStatus();
@ -79,26 +38,16 @@ namespace Pgsql {
/** Retrieves all the error fields. */ /** Retrieves all the error fields. */
ErrorDetails diagDetails(); ErrorDetails diagDetails();
const_iterator begin() const int tuplesAffected() const override;
{ int rows() const override;
return const_iterator(*this, 0); int cols() const override;
}
const_iterator end() const const char* getColName(int idx) const override;
{
return const_iterator(*this, rows());
}
int tuplesAffected() const; const char* val(int col, int row) const override;
int rows() const; Value get(int col, int row) const override;
int cols() const; Oid type(int col) const override;
bool null(int col, int row) const override;
const char* getColName(int idx) const;
const char* val(int col, int row) const;
Value get(int col, int row) const;
Oid type(int col) const;
bool null(int col, int row) const;
/// Return the oid of the table this is a column of /// Return the oid of the table this is a column of
/// when the column isn't a table column InvalidOid is returned /// when the column isn't a table column InvalidOid is returned

View file

@ -0,0 +1,51 @@
#pragma once
#include "Pgsql_Row.h"
namespace Pgsql {
class IResult;
class ResultConstIterator {
public:
ResultConstIterator(const IResult &r, int rw)
: m_row(r, rw)
{}
ResultConstIterator operator++()
{
ResultConstIterator t(*this);
m_row.next();
return t;
}
ResultConstIterator& operator++(int)
{
m_row.next();
return *this;
}
bool operator==(const ResultConstIterator &rhs)
{
return m_row == rhs.m_row;
}
bool operator!=(const ResultConstIterator &rhs)
{
return !operator==(rhs);
}
const Row& operator*()
{
return m_row;
}
const Row& operator->()
{
return m_row;
}
private:
Row m_row;
};
}

View file

@ -3,7 +3,7 @@
using namespace Pgsql; using namespace Pgsql;
Row::Row(const Result &result, int row) Row::Row(const IResult &result, int row)
: m_result(result) : m_result(result)
, m_row(row) , m_row(row)
{} {}

View file

@ -5,7 +5,7 @@
namespace Pgsql { namespace Pgsql {
class Result; class IResult;
/** \brief A reference to a specific row from a result. /** \brief A reference to a specific row from a result.
* *
@ -59,7 +59,7 @@ namespace Pgsql {
int m_col; int m_col;
}; };
Row(const Result &result, int row); Row(const IResult &result, int row);
bool next(); bool next();
bool operator==(const Row& rhs) const; bool operator==(const Row& rhs) const;
@ -75,7 +75,7 @@ namespace Pgsql {
const_iterator end() const; const_iterator end() const;
private: private:
const Result& m_result; const IResult& m_result;
int m_row; int m_row;
}; };

View file

@ -36,7 +36,52 @@ Value::operator QDate() const
Value::operator QTime() const Value::operator QTime() const
{ {
return QTime::fromString(asQString(), Qt::ISODateWithMs); // as values are expected to be coming straight out of postgresql
// we will not validate everything
char* pos;
int hours = std::strtol(m_val, &pos, 10);
if (*pos == ':')
++pos;
else
throw std::runtime_error("Unpexted character after hour in input");
int minutes = std::strtol(pos, &pos, 10);
if (*pos == ':')
++pos;
else
throw std::runtime_error("Unpexted character after minutes in input");
int seconds = std::strtol(pos, &pos, 10);
int millis = 0;
if (*pos == '.')
{
const char *start = ++pos;
millis = std::strtol(pos, &pos, 10);
int length = pos - start;
switch (length) {
case 0: break; // should be zero anyway so no reason to multiply by 1000
case 1: millis *= 100; break;
case 2: millis *= 10; break;
case 3: break;
case 4: millis = (millis + 5) / 10; break;
case 5: millis = (millis + 50) / 100; break;
case 6: millis = (millis + 500) / 1000; break;
default:
throw std::runtime_error("Unpexted number of digits in microsecond field");
}
}
int tzhours = 0, tzminutes = 0;
if (*pos == '+' || *pos == '-')
{
++pos;
tzhours = std::strtol(pos, &pos, 10);
if (*pos == ':')
{
tzminutes = std::strtol(pos, &pos, 10);
}
// TODO QTime does not support timezones (and rightly so as they are meaningless without a date)
// the postgresql documentation actually agrees with this but supports timezones to not break
// compatability with older verions
}
return QTime(hours, minutes, seconds, millis);
} }

View file

@ -31,9 +31,11 @@ SOURCES += Pgsql_Connection.cpp \
Pgsql_Canceller.cpp Pgsql_Canceller.cpp
HEADERS += Pgsql_Connection.h \ HEADERS += Pgsql_Connection.h \
Pgsql_IResult.h \
Pgsql_Params.h \ Pgsql_Params.h \
Pgsql_PgException.h \ Pgsql_PgException.h \
Pgsql_Result.h \ Pgsql_Result.h \
Pgsql_ResultConstIterator.h \
Pgsql_Row.h \ Pgsql_Row.h \
Pgsql_Value.h \ Pgsql_Value.h \
Pgsql_declare.h \ Pgsql_declare.h \

View file

@ -49,9 +49,9 @@ TEST(Pgsql_Value, test_QTime)
TEST(Pgsql_Value, test_QTimeMS) TEST(Pgsql_Value, test_QTimeMS)
{ {
Pgsql::Value v("09:38:17.339817+02", timetz_oid); Pgsql::Value v("09:38:17.339+02:00", timetz_oid);
QTime t = v; QTime t = v;
ASSERT_EQ(t, QTime(9, 38, 17, 340)); ASSERT_EQ(t, QTime(9, 38, 17, 339));
} }

View file

@ -61,13 +61,13 @@ TEST(ConvertLangToSqlString, testSemiColon)
ASSERT_EQ(output, expected); ASSERT_EQ(output, expected);
} }
TEST(ConvertLangToSqlString, testComment) //TEST(ConvertLangToSqlString, testComment)
{ //{
QString in(R"__( "SELECT * " // comment // QString in(R"__( "SELECT * " // comment
"FROM t"; )__"); // "FROM t"; )__");
QString expected(R"__(SELECT * // QString expected(R"__(SELECT *
FROM t)__"); //FROM t)__");
auto output = ConvertLangToSqlString(in); // auto output = ConvertLangToSqlString(in);
ASSERT_EQ(output, expected); // ASSERT_EQ(output, expected);
} //}

View file

@ -18,13 +18,13 @@ TEST(SqlParser, emptyFile)
ASSERT_TRUE(res == nullptr); ASSERT_TRUE(res == nullptr);
} }
TEST(SqlParser, select) //TEST(SqlParser, select)
{ //{
QString input("SELECT"); // QString input("SELECT");
SqlLexer lexer(input, LexerState::Null); // SqlLexer lexer(input, LexerState::Null);
SqlParser parser(lexer); // SqlParser parser(lexer);
auto res = parser.parse(); // auto res = parser.parse();
ASSERT_TRUE(res != nullptr); // ASSERT_TRUE(res != nullptr);
ASSERT_EQ(typeid(*res), typeid(SqlAst::Select)); // ASSERT_EQ(typeid(*res), typeid(SqlAst::Select));
} //}