diff --git a/backends/postgres/include/postgres_result_reader.hpp b/backends/postgres/include/postgres_result_reader.hpp index 926e81e..63db4d7 100644 --- a/backends/postgres/include/postgres_result_reader.hpp +++ b/backends/postgres/include/postgres_result_reader.hpp @@ -32,14 +32,13 @@ public: void read_value(const char *id, size_t index, char *value, size_t s) override; void read_value(const char *id, size_t index, std::string &value) override; void read_value(const char *id, size_t index, std::string &value, size_t s) override; - void read_value(const char *id, size_t index, sql::any_type &value, sql::data_type_t type, size_t size) override; private: PGresult *result_{}; size_t row_count_{}; size_t column_count_{}; - size_t row_index_{0}; + int row_index_{-1}; }; } diff --git a/backends/postgres/src/postgres_connection.cpp b/backends/postgres/src/postgres_connection.cpp index 364e2a0..4054f19 100644 --- a/backends/postgres/src/postgres_connection.cpp +++ b/backends/postgres/src/postgres_connection.cpp @@ -1,5 +1,10 @@ #include "postgres_connection.hpp" #include "postgres_error.hpp" +#include "postgres_result_reader.hpp" + +#include "matador/sql/record.hpp" + +#include namespace matador::backends::postgres { @@ -41,7 +46,16 @@ std::unique_ptr postgres_connection::fetch(const std::st throw_postgres_error(res, conn_, "postgres", stmt); - return {}; + sql::record prototype; + auto ncol = PQnfields(res); + for (int i = 0; i < ncol; ++i) { + const char *col_name = PQfname(res, i); + auto type = PQftype(res, i); + auto size = PQfmod(res, i); + std::cout << "column " << col_name << ", type " << type << " (size: " << size << ")\n"; + prototype.append({col_name}); + } + return std::move(std::make_unique(std::make_unique(res), std::move(prototype))); } void postgres_connection::prepare(const std::string &stmt) @@ -55,12 +69,67 @@ size_t postgres_connection::execute(const std::string &stmt) throw_postgres_error(res, conn_, "postgres", stmt); - return 0; + return sql::to_long_long(PQcmdTuples(res)); +} + +sql::data_type_t string2type(const char *type) +{ + if (strcmp(type, "int2") == 0) { + return sql::data_type_t::type_short; + } else if (strcmp(type, "int4") == 0) { + return sql::data_type_t::type_int; + } else if (strcmp(type, "int8") == 0) { + return sql::data_type_t::type_long_long; + } else if (strncmp(type, "int8", 6) == 0) { + return sql::data_type_t::type_long_long; + } else if (strcmp(type, "date") == 0) { + return sql::data_type_t::type_date; + } else if (strncmp(type, "timestamp", 8) == 0) { + return sql::data_type_t::type_time; + } else if (strcmp(type, "float4") == 0) { + return sql::data_type_t::type_float; + } else if (strcmp(type, "float8") == 0) { + return sql::data_type_t::type_double; + } else if (strncmp(type, "varchar", 7) == 0) { + return sql::data_type_t::type_varchar; + } else if (strncmp(type, "character varying", 7) == 0) { + return sql::data_type_t::type_varchar; + } else if (strncmp(type, "text", 0) == 0) { + return sql::data_type_t::type_text; + } else { + return sql::data_type_t::type_unknown; + } } sql::record postgres_connection::describe(const std::string &table) { - return {}; + std::string stmt( + "SELECT ordinal_position, column_name, udt_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_schema='public' AND table_name='" + table + "'"); + + PGresult *res = PQexec(conn_, stmt.c_str()); + + throw_postgres_error(res, conn_, "postgres", stmt); + + postgres_result_reader reader(res); + sql::record prototype; + while (reader.fetch()) { + char *end = nullptr; + // Todo: Handle error + auto index = strtoul(reader.column(0), &end, 10); + std::string name = reader.column(1); + + // Todo: extract size + auto type = (string2type(reader.column(2))); + end = nullptr; + utils::constraints options{}; + if (strtoul(reader.column(4), &end, 10) == 0) { + options = utils::constraints::NOT_NULL; + } + // f.default_value(res->column(4)); + prototype.append({name, type, {options}}); + } + + return std::move(prototype); } bool postgres_connection::exists(const std::string &table_name) diff --git a/backends/postgres/src/postgres_result_reader.cpp b/backends/postgres/src/postgres_result_reader.cpp index 84dfd77..5719263 100644 --- a/backends/postgres/src/postgres_result_reader.cpp +++ b/backends/postgres/src/postgres_result_reader.cpp @@ -24,7 +24,7 @@ size_t postgres_result_reader::column_count() const const char *postgres_result_reader::column(size_t index) const { - return nullptr; + return PQgetvalue(result_, static_cast(row_index_), static_cast(index)); } bool postgres_result_reader::fetch() @@ -99,22 +99,39 @@ void postgres_result_reader::read_value(const char *id, size_t index, double &va void postgres_result_reader::read_value(const char *id, size_t index, char *value, size_t s) { + auto *val = PQgetvalue(result_, static_cast(row_index_), static_cast(index)); + size_t len = strlen(value); + if (len > (size_t)s) { +#ifdef _MSC_VER + strncpy_s(val, s, value, s); +#else + strncpy(val, value, s); +#endif + val[s-1] = '\n'; + } else { +#ifdef _MSC_VER + strcpy_s(val, s, value); +#else + strcpy(val, value); +#endif + } } void postgres_result_reader::read_value(const char *id, size_t index, std::string &value) { - + auto *val = PQgetvalue(result_, static_cast(row_index_), static_cast(index)); + if (strlen(val) != 0) { + value.assign(val); + } } void postgres_result_reader::read_value(const char *id, size_t index, std::string &value, size_t s) { - + auto *val = PQgetvalue(result_, static_cast(row_index_), static_cast(index)); + if (strlen(val) != 0) { + value.assign(val); + } } -void postgres_result_reader::read_value(const char *id, size_t index, sql::any_type &value, sql::data_type_t type, - size_t size) -{ - -} } \ No newline at end of file diff --git a/backends/sqlite/include/sqlite_result_reader.hpp b/backends/sqlite/include/sqlite_result_reader.hpp index 3ff1e5b..930ead3 100644 --- a/backends/sqlite/include/sqlite_result_reader.hpp +++ b/backends/sqlite/include/sqlite_result_reader.hpp @@ -38,7 +38,6 @@ public: void read_value(const char *id, size_t index, char *value, size_t size) override; void read_value(const char *id, size_t index, std::string &value) override; void read_value(const char *id, size_t index, std::string &value, size_t s) override; - void read_value(const char *id, size_t index, sql::any_type &value, sql::data_type_t type, size_t size) override; private: rows result_; diff --git a/backends/sqlite/src/sqlite_result_reader.cpp b/backends/sqlite/src/sqlite_result_reader.cpp index 4bc395a..c361f96 100644 --- a/backends/sqlite/src/sqlite_result_reader.cpp +++ b/backends/sqlite/src/sqlite_result_reader.cpp @@ -129,86 +129,4 @@ void sqlite_result_reader::read_value(const char *id, size_t index, std::string value.assign(result_[row_index_][index]); } -template < typename Type > -void convert(const char *valstr, sql::any_type &value) -{ - Type val{}; - sql::to_value(val, valstr); - value = val; -} - -void sqlite_result_reader::read_value(const char *id, size_t index, sql::any_type &value, sql::data_type_t type, size_t size) -{ - switch (type) { - case sql::data_type_t::type_char: - convert(result_[row_index_][index], value); - break; - case sql::data_type_t::type_short: - convert(result_[row_index_][index], value); - break; - case sql::data_type_t::type_int: - convert(result_[row_index_][index], value); - break; - case sql::data_type_t::type_long: - convert(result_[row_index_][index], value); - break; - case sql::data_type_t::type_long_long: - convert(result_[row_index_][index], value); - break; - case sql::data_type_t::type_unsigned_char: - convert(result_[row_index_][index], value); - break; - case sql::data_type_t::type_unsigned_short: - convert(result_[row_index_][index], value); - break; - case sql::data_type_t::type_unsigned_int: - convert(result_[row_index_][index], value); - break; - case sql::data_type_t::type_unsigned_long: - convert(result_[row_index_][index], value); - break; - case sql::data_type_t::type_unsigned_long_long: - convert(result_[row_index_][index], value); - break; - case sql::data_type_t::type_float: - convert(result_[row_index_][index], value); - break; - case sql::data_type_t::type_double: - convert(result_[row_index_][index], value); - break; - case sql::data_type_t::type_bool: { - int val{}; - sql::to_value(val, result_[row_index_][index]); - value = val > 0; - break; - } - case sql::data_type_t::type_text: - case sql::data_type_t::type_varchar: { - value = std::string{result_[row_index_][index]}; - break; - } - case sql::data_type_t::type_char_pointer: { - value = result_[row_index_][index]; - break; - } - case sql::data_type_t::type_time: - case sql::data_type_t::type_date: { - value = std::string{result_[row_index_][index]}; - break; - } - case sql::data_type_t::type_null: { - value = nullptr_t{}; - break; - } - case sql::data_type_t::type_blob: { - throw std::logic_error("data type blob not supported"); - } - case sql::data_type_t::type_unknown: { - value = std::string(result_[row_index_][index]); - break; - } - } - -} - } \ No newline at end of file diff --git a/include/matador/sql/query_result_reader.hpp b/include/matador/sql/query_result_reader.hpp index 983d3a5..f118db6 100644 --- a/include/matador/sql/query_result_reader.hpp +++ b/include/matador/sql/query_result_reader.hpp @@ -33,7 +33,7 @@ public: virtual void read_value(const char *id, size_t index, char *value, size_t s) = 0; virtual void read_value(const char *id, size_t index, std::string &value) = 0; virtual void read_value(const char *id, size_t index, std::string &value, size_t s) = 0; - virtual void read_value(const char *id, size_t index, any_type &value, data_type_t type, size_t size) = 0; + virtual void read_value(const char *id, size_t index, any_type &value, data_type_t type, size_t size); }; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a0cdee3..7105699 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,7 +18,8 @@ set(SQL_SOURCES sql/fk_value_extractor.cpp sql/table_repository.cpp sql/any_type_to_visitor.cpp - sql/query_result.cpp) + sql/query_result.cpp + sql/query_result_reader.cpp) set(SQL_HEADER ../include/matador/sql/dialect.hpp diff --git a/src/sql/query_result_reader.cpp b/src/sql/query_result_reader.cpp new file mode 100644 index 0000000..54bfffc --- /dev/null +++ b/src/sql/query_result_reader.cpp @@ -0,0 +1,87 @@ +#include "matador/sql/query_result_reader.hpp" +#include "matador/sql/to_value.hpp" + +namespace matador::sql { + +template < typename Type > +void convert(const char *valstr, sql::any_type &value) +{ + Type val{}; + sql::to_value(val, valstr); + value = val; +} + +void query_result_reader::read_value(const char *id, size_t index, any_type &value, data_type_t type, size_t size) +{ + switch (type) { + case sql::data_type_t::type_char: + convert(column(index), value); + break; + case sql::data_type_t::type_short: + convert(column(index), value); + break; + case sql::data_type_t::type_int: + convert(column(index), value); + break; + case sql::data_type_t::type_long: + convert(column(index), value); + break; + case sql::data_type_t::type_long_long: + convert(column(index), value); + break; + case sql::data_type_t::type_unsigned_char: + convert(column(index), value); + break; + case sql::data_type_t::type_unsigned_short: + convert(column(index), value); + break; + case sql::data_type_t::type_unsigned_int: + convert(column(index), value); + break; + case sql::data_type_t::type_unsigned_long: + convert(column(index), value); + break; + case sql::data_type_t::type_unsigned_long_long: + convert(column(index), value); + break; + case sql::data_type_t::type_float: + convert(column(index), value); + break; + case sql::data_type_t::type_double: + convert(column(index), value); + break; + case sql::data_type_t::type_bool: { + int val{}; + sql::to_value(val, column(index)); + value = val > 0; + break; + } + case sql::data_type_t::type_text: + case sql::data_type_t::type_varchar: { + value = std::string{column(index)}; + break; + } + case sql::data_type_t::type_char_pointer: { + value = column(index); + break; + } + case sql::data_type_t::type_time: + case sql::data_type_t::type_date: { + value = std::string{column(index)}; + break; + } + case sql::data_type_t::type_null: { + value = nullptr_t{}; + break; + } + case sql::data_type_t::type_blob: { + throw std::logic_error("data type blob not supported"); + } + case sql::data_type_t::type_unknown: { + value = std::string(column(index)); + break; + } + } +} + +} \ No newline at end of file diff --git a/test/SessionRecordTest.cpp b/test/SessionRecordTest.cpp index f3a9b98..c298081 100644 --- a/test/SessionRecordTest.cpp +++ b/test/SessionRecordTest.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -66,7 +67,11 @@ TEST_CASE("Create and drop table statement with foreign key", "[session record]" } TEST_CASE("Execute insert record statement", "[session record]") { - connection_pool pool("sqlite://sqlite.db", 4); + auto dns = GENERATE(as{}, + "sqlite://sqlite.db", + "postgres://test:test123@127.0.0.1:5432/matador_test" ); + + connection_pool pool(dns, 4); session s(pool); auto res = s.create()