diff --git a/backends/postgres/src/postgres_connection.cpp b/backends/postgres/src/postgres_connection.cpp index 69cff5c..da57ab2 100644 --- a/backends/postgres/src/postgres_connection.cpp +++ b/backends/postgres/src/postgres_connection.cpp @@ -10,6 +10,7 @@ #include "matador/sql/internal/query_result_impl.hpp" +#include #include namespace matador::backends::postgres { @@ -79,6 +80,8 @@ utils::result postgres_connection::server_version( }); } +utils::basic_type oid2type(Oid oid); + utils::result, utils::error> postgres_connection::fetch(const sql::query_context &context) { PGresult *res = PQexec(conn_, context.sql.c_str()); @@ -86,16 +89,28 @@ utils::result, utils::error> postgres_co return utils::failure(make_error(sql::error_code::FETCH_FAILED, res, conn_, "Failed to fetch", context.sql)); } -// std::vector prototype; -// const auto num_col = PQnfields(res); -// for (int i = 0; i < num_col; ++i) { -// const char *col_name = PQfname(res, i); -// auto type = PQftype(res, i); -// auto size = PQfmod(res, i); -// prototype.emplace_back(col_name); -// } + // const auto is_unknown = std::all_of( context.prototype.begin(), context.prototype.end(), [](const object::attribute_definition &a) {return a.is_null();} ); + // if (!is_unknown) { + // return utils::ok(std::make_unique(std::make_unique(res), context.prototype)); + // } - return utils::ok(std::make_unique(std::make_unique(res), context.prototype)); + std::vector prototype = context.prototype; + + const auto num_col = PQnfields(res); + if (prototype.size() != num_col) { + return utils::failure(make_error(sql::error_code::FETCH_FAILED, res, conn_, "Number of received columns doesn't match expected columns.", context.sql)); + } + for (int i = 0; i < num_col; ++i) { + if (!prototype.at(i).is_null()) { + continue; + } + const auto type = oid2type(PQftype(res, i)); + // const char *col_name = PQfname(res, i); + // const auto size = PQfmod(res, i); + prototype.at(i).type(type); + } + + return utils::ok(std::make_unique(std::make_unique(res), prototype)); } std::string postgres_connection::generate_statement_name(const sql::query_context &query) { @@ -141,6 +156,37 @@ utils::result postgres_connection::execute(const std::stri return utils::ok(static_cast(affected_rows)); } +utils::basic_type oid2type(const Oid oid) { + switch (oid) { + case 16: + return utils::basic_type::type_bool; + case 17: + return utils::basic_type::type_blob; + case 18: + return utils::basic_type::type_int8; + case 21: + return utils::basic_type::type_int16; + case 23: + return utils::basic_type::type_int32; + case 20: + return utils::basic_type::type_int64; + case 24: + return utils::basic_type::type_text; + case 1043: + return utils::basic_type::type_varchar; + case 700: + return utils::basic_type::type_float; + case 701: + return utils::basic_type::type_double; + case 1082: + return utils::basic_type::type_date; + case 1114: + return utils::basic_type::type_time; + default: + return utils::basic_type::type_null; + } +} + utils::basic_type string2type(const char *type) { if (strcmp(type, "int2") == 0) { return utils::basic_type::type_int16; diff --git a/backends/postgres/src/postgres_dialect.cpp b/backends/postgres/src/postgres_dialect.cpp index 7cd2503..22a33a5 100644 --- a/backends/postgres/src/postgres_dialect.cpp +++ b/backends/postgres/src/postgres_dialect.cpp @@ -13,7 +13,7 @@ return "$" + std::to_string(index); }) .with_token_replace_map({ - {dialect_token::BEGIN_BINARY_DATA, "'"} + {dialect_token::BEGIN_BINARY_DATA, "'\\x"} }) .with_data_type_replace_map({ {matador::utils::basic_type::type_int8, "SMALLINT"}, diff --git a/include/matador/utils/convert.hpp b/include/matador/utils/convert.hpp index ea3e3d0..a5d5187 100644 --- a/include/matador/utils/convert.hpp +++ b/include/matador/utils/convert.hpp @@ -131,7 +131,7 @@ result to(const std::string &source, std::enable_if_ } template < typename DestType > -result to(const std::string &source, std::enable_if_t && std::is_unsigned_v>* = nullptr) +result to(const std::string &source, std::enable_if_t && std::is_unsigned_v && !std::is_same_v>* = nullptr) { if (source.empty()) { return failure(conversion_error::MissingData/*, "failed to convert empty string"}*/); @@ -167,17 +167,27 @@ result to(const blob &source, std::enable_if_t +// result to(const std::string &source, std::enable_if_t>* = nullptr) { +// if (source.empty()) { +// return failure(conversion_error::MissingData/*, "failed to convert empty string"}*/); +// } +// +// } +// static std::unordered_map string_to_bool_map = { {"true", true}, + {"t", true}, {"on", true}, {"1", true}, {"false", false}, + {"f", false}, {"off", false}, {"0", false} }; -template < typename DestType, typename SourceType > -result to(const SourceType &source, std::enable_if_t &&std::is_same_v>* = nullptr) { +template < typename DestType > +result to(const std::string &source, std::enable_if_t>* = nullptr) { if (source.empty()) { return failure(conversion_error::MissingData); } @@ -188,6 +198,18 @@ result to(const SourceType &source, std::enable_if_t return ok(it->second); } +template < typename DestType > +result to(const char *source, std::enable_if_t>* = nullptr) { + if (strlen(source) == 0) { + return failure(conversion_error::MissingData); + } + const auto it = string_to_bool_map.find(source); + if (it == string_to_bool_map.end()) { + return failure(conversion_error::NotConvertable); + } + return ok(it->second); +} + template < typename DestType, typename SourceType > result to(const SourceType &source, std::enable_if_t &&std::is_same_v>* = nullptr) { return ok(std::string(source ? "true" : "false")); diff --git a/source/orm/query/attribute_string_writer.cpp b/source/orm/query/attribute_string_writer.cpp index e7a86cc..72d2f0e 100644 --- a/source/orm/query/attribute_string_writer.cpp +++ b/source/orm/query/attribute_string_writer.cpp @@ -1,6 +1,6 @@ #include "matador/query/attribute_string_writer.hpp" -// #include "matador/sql/connection_impl.hpp" +#include "matador/sql/interface/connection_impl.hpp" #include @@ -56,13 +56,13 @@ void attribute_string_writer::write_value(size_t /*pos*/, const bool& x ) { } void attribute_string_writer::write_value(size_t /*pos*/, const float& x ) { - if (const auto res = utils::to(x); res.is_error()) { + if (const auto res = utils::to(x); !res.is_error()) { result_ = *res; } } void attribute_string_writer::write_value(size_t /*pos*/, const double& x ) { - if (const auto res = utils::to(x); res.is_error()) { + if (const auto res = utils::to(x); !res.is_error()) { result_ = *res; } } @@ -98,7 +98,7 @@ void attribute_string_writer::write_value(size_t /*pos*/, const utils::blob& x ) // MSSQL: 0x5468697320697320612062616E617279204461746120737472696E67 // Sqlite: X'5468697320697320612062616E617279204461746120737472696E67' if (conn_.has_value()) { -// result_ = dialect_.token_at(sql::dialect_token::BEGIN_BINARY_DATA) + conn_.value().get().to_escaped_string(x) + dialect_.token_at(sql::dialect_token::END_BINARY_DATA); + result_ = dialect_.token_at(sql::dialect_token::BEGIN_BINARY_DATA) + conn_.value().get().to_escaped_string(x) + dialect_.token_at(sql::dialect_token::END_BINARY_DATA); } else { result_ = dialect_.token_at(sql::dialect_token::BEGIN_BINARY_DATA) + dialect_.to_escaped_string(x) + dialect_.token_at(sql::dialect_token::END_BINARY_DATA); } diff --git a/test/backends/QueryBasicTest.cpp b/test/backends/QueryBasicTest.cpp index 1be2b8d..42f5929 100644 --- a/test/backends/QueryBasicTest.cpp +++ b/test/backends/QueryBasicTest.cpp @@ -74,7 +74,7 @@ TEST_CASE_METHOD( QueryFixture, "Insert and select basic datatypes", "[query][da }; res = query::insert() - .into("types", matador::sql::column_generator::generate(schema, true)) + .into("types", column_generator::generate(schema, true)) .values(t) .execute(db); REQUIRE(res.is_ok()); diff --git a/test/backends/QueryRecordTest.cpp b/test/backends/QueryRecordTest.cpp index 49543b8..777a5a6 100644 --- a/test/backends/QueryRecordTest.cpp +++ b/test/backends/QueryRecordTest.cpp @@ -50,8 +50,8 @@ TEST_CASE_METHOD(QueryFixture, "Test all data types for record", "[query][record tables_to_drop.emplace("types"); auto cols = std::vector{"id", - "val_char", "val_short", "val_int", "val_long", "val_long_long", - "val_uchar", "val_ushort", "val_uint", "val_ulong", "val_ulong_long", + "val_char", "val_short", "val_int", "val_long_long", + "val_uchar", "val_ushort", "val_uint", "val_ulong_long", "val_bool", "val_float", "val_double", "val_string", "val_varchar", @@ -426,8 +426,7 @@ TEST_CASE_METHOD(QueryFixture, "Execute select statement with order by", "[query REQUIRE(expected_names.empty()); } -TEST_CASE_METHOD(QueryFixture, "Execute select statement with group by and order by", "[query][record]") -{ +TEST_CASE_METHOD(QueryFixture, "Execute select statement with group by and order by", "[query][record]") { auto res = query::create() .table("person", { make_pk_column("id"),