initial matador ng commit

This commit is contained in:
2025-02-02 20:37:12 +01:00
parent 19de5714f4
commit ded3daceb3
378 changed files with 14913 additions and 13431 deletions
+8 -2
View File
@@ -18,7 +18,7 @@ set(SOURCES
set(LIBRARY_TARGET matador-postgres)
add_subdirectory(test)
#add_subdirectory(test)
add_library(${LIBRARY_TARGET} MODULE ${SOURCES} ${HEADER})
@@ -32,4 +32,10 @@ target_include_directories(${LIBRARY_TARGET} PRIVATE
${PROJECT_SOURCE_DIR}/backends/postgres/include
${PostgreSQL_INCLUDE_DIRS})
target_link_libraries(${LIBRARY_TARGET} matador ${PostgreSQL_LIBRARIES})
target_link_libraries(${LIBRARY_TARGET}
matador-core
matador-orm
${CMAKE_DL_LIBS}
${CMAKE_THREAD_LIBS_INIT}
${PostgreSQL_LIBRARIES}
)
@@ -12,7 +12,7 @@
#define MATADOR_POSTGRES_API
#endif
#include "matador/sql/connection_impl.hpp"
#include "matador/sql/interface/connection_impl.hpp"
#include <unordered_map>
@@ -20,22 +20,25 @@
namespace matador::backends::postgres {
class postgres_connection : public matador::sql::connection_impl
class postgres_connection : public sql::connection_impl
{
public:
explicit postgres_connection(const sql::connection_info &info);
void open() override;
void close() override;
bool is_open() override;
utils::result<void, utils::error> open() override;
utils::result<void, utils::error> close() override;
[[nodiscard]] utils::result<bool, utils::error> is_open() const override;
[[nodiscard]] utils::result<bool, utils::error> is_valid() const override;
[[nodiscard]] utils::result<utils::version, utils::error> client_version() const override;
[[nodiscard]] utils::result<utils::version, utils::error> server_version() const override;
std::unique_ptr<sql::query_result_impl> fetch(const std::string &stmt) override;
std::unique_ptr<sql::statement_impl> prepare(sql::query_context context) override;
utils::result<size_t, utils::error> execute(const std::string &stmt) override;
utils::result<std::unique_ptr<sql::statement_impl>, utils::error> prepare(const sql::query_context &context) override;
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> fetch(const sql::query_context &context) override;
size_t execute(const std::string &stmt) override;
utils::result<std::vector<sql::column_definition>, utils::error> describe(const std::string& table) override;
utils::result<bool, utils::error> exists(const std::string &schema_name, const std::string &table_name) override;
std::vector<sql::column_definition> describe(const std::string& table) override;
bool exists(const std::string &schema_name, const std::string &table_name) override;
[[nodiscard]] std::string to_escaped_string( const utils::blob& value ) const override;
private:
[[nodiscard]] static std::string generate_statement_name(const sql::query_context &query) ;
+15 -3
View File
@@ -1,15 +1,27 @@
#ifndef QUERY_POSTGRES_ERROR_HPP
#define QUERY_POSTGRES_ERROR_HPP
#include "matador/utils/error.hpp"
#include "matador/sql/error_code.hpp"
#include <libpq-fe.h>
#include <string>
namespace matador::backends::postgres {
void throw_postgres_error(const char *what, const std::string &source);
void throw_postgres_error(PGconn *db, const std::string &source);
void throw_postgres_error(PGresult *res, PGconn *db, const std::string &source, const std::string &sql);
utils::error make_error(sql::error_code ec,
const PGresult *res,
const PGconn *db,
const std::string &msg,
const std::string &sql = {});
bool is_result_error(const PGresult *res);
// void throw_postgres_error(const char *what, const std::string &source);
// void throw_postgres_error(PGconn *db, const std::string &source);
// void throw_postgres_error(PGresult *res, PGconn *db, const std::string &source, const std::string &sql);
}
@@ -1,42 +1,52 @@
#ifndef QUERY_POSTGRES_PARAMETER_BINDER_H
#define QUERY_POSTGRES_PARAMETER_BINDER_H
#include "matador/sql/parameter_binder.hpp"
#include "matador/utils/attribute_writer.hpp"
#include <vector>
namespace matador::backends::postgres {
class postgres_parameter_binder final : public sql::parameter_binder
class postgres_parameter_binder final : public utils::attribute_writer
{
public:
struct bind_data {
explicit bind_data(size_t size);
std::vector<std::string> strings;
std::vector<std::vector<unsigned char>> bytes;
std::vector<const char*> values;
std::vector<int> lengths;
std::vector<int> formats;
};
explicit postgres_parameter_binder(size_t size);
void bind(size_t pos, char i) override;
void bind(size_t pos, short i) override;
void bind(size_t pos, int i) override;
void bind(size_t pos, long i) override;
void bind(size_t pos, long long int i) override;
void bind(size_t pos, unsigned char i) override;
void bind(size_t pos, unsigned short i) override;
void bind(size_t pos, unsigned int i) override;
void bind(size_t pos, unsigned long i) override;
void bind(size_t pos, unsigned long long int i) override;
void bind(size_t pos, bool b) override;
void bind(size_t pos, float d) override;
void bind(size_t pos, double d) override;
void bind(size_t pos, const char *string) override;
void bind(size_t pos, const char *string, size_t size) override;
void bind(size_t pos, const std::string &string) override;
void bind(size_t pos, const std::string &x, size_t size) override;
void write_value(size_t pos, const uint8_t &x) override;
void write_value(size_t pos, const uint16_t &x) override;
void write_value(size_t pos, const uint32_t &x) override;
void write_value(size_t pos, const uint64_t &x) override;
void write_value(size_t pos, const int8_t &x) override;
void write_value(size_t pos, const int16_t &x) override;
void write_value(size_t pos, const int32_t &x) override;
void write_value(size_t pos, const int64_t &x) override;
void write_value(size_t pos, const bool &x) override;
void write_value(size_t pos, const float &x) override;
void write_value(size_t pos, const double &x) override;
void write_value(size_t pos, const time &x ) override;
void write_value(size_t pos, const date &x ) override;
void write_value(size_t pos, const char *x) override;
void write_value(size_t pos, const char *x, size_t size) override;
void write_value(size_t pos, const std::string &x) override;
void write_value(size_t pos, const std::string &x, size_t size) override;
void write_value(size_t pos, const utils::blob &x) override;
void write_value(size_t pos, const utils::value &x, size_t size) override;
void bind(size_t pos, const utils::blob &blob) override;
[[nodiscard]] const std::vector<const char*>& params() const;
[[nodiscard]] const bind_data& params() const;
private:
std::vector<std::string> strings_;
std::vector<const char*> params_;
bind_data bind_data_;
// std::vector<std::string> strings_;
// std::vector<const char*> params_;
};
}
@@ -1,12 +1,40 @@
#ifndef QUERY_POSTGRES_RESULT_READER_HPP
#define QUERY_POSTGRES_RESULT_READER_HPP
#include "matador/sql/interface/query_result_reader.hpp"
#include <libpq-fe.h>
#include "matador/sql/query_result_reader.hpp"
namespace matador::backends::postgres {
class postgres_result_reader : public sql::query_result_reader
namespace detail {
class empty_binder final : public utils::attribute_reader
{
public:
void read_value(const char *, size_t, int8_t &) override {}
void read_value(const char *, size_t, int16_t &) override {}
void read_value(const char *, size_t, int32_t &) override {}
void read_value(const char *, size_t, int64_t &) override {}
void read_value(const char *, size_t, uint8_t &) override {}
void read_value(const char *, size_t, uint16_t &) override {}
void read_value(const char *, size_t, uint32_t &) override {}
void read_value(const char *, size_t, uint64_t &) override {}
void read_value(const char *, size_t, bool &) override {}
void read_value(const char *, size_t, float &) override {}
void read_value(const char *, size_t, double &) override {}
void read_value(const char *, size_t, time &) override {}
void read_value(const char *, size_t, date &) override {}
void read_value(const char *, size_t, char *, size_t) override {}
void read_value(const char *, size_t, std::string &) override {}
void read_value(const char *, size_t, std::string &, size_t) override {}
void read_value(const char *, size_t, utils::blob &) override {}
void read_value(const char *, size_t, utils::value &, size_t) override {}
};
}
class postgres_result_reader final : public sql::query_result_reader
{
public:
explicit postgres_result_reader(PGresult *result);
@@ -14,7 +42,30 @@ public:
[[nodiscard]] size_t column_count() const override;
[[nodiscard]] const char *column(size_t index) const override;
bool fetch() override;
utils::result<bool, utils::error> fetch() override;
[[nodiscard]] size_t start_column_index() const override;
void read_value(const char *id, size_t index, int8_t &value) override;
void read_value(const char *id, size_t index, int16_t &value) override;
void read_value(const char *id, size_t index, int32_t &value) override;
void read_value(const char *id, size_t index, int64_t &value) override;
void read_value(const char *id, size_t index, uint8_t &value) override;
void read_value(const char *id, size_t index, uint16_t &value) override;
void read_value(const char *id, size_t index, uint32_t &value) override;
void read_value(const char *id, size_t index, uint64_t &value) override;
void read_value(const char *id, size_t index, bool &value) override;
void read_value(const char *id, size_t index, float &value) override;
void read_value(const char *id, size_t index, double &value) override;
void read_value(const char *id, size_t index, matador::time &value) override;
void read_value(const char *id, size_t index, matador::date &value) override;
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 size) override;
void read_value(const char *id, size_t index, utils::blob &value) override;
void read_value(const char *id, size_t index, utils::value &val, size_t size) override;
protected:
attribute_reader &result_binder() override;
private:
PGresult *result_{};
@@ -22,6 +73,8 @@ private:
size_t row_count_{};
size_t column_count_{};
int row_index_{-1};
detail::empty_binder empty_binder_;
};
}
@@ -1,7 +1,7 @@
#ifndef QUERY_POSTGRES_STATEMENT_HPP
#define QUERY_POSTGRES_STATEMENT_HPP
#include "matador/sql/statement_impl.hpp"
#include "matador/sql/interface/statement_impl.hpp"
#include "postgres_parameter_binder.h"
@@ -14,11 +14,11 @@ class postgres_statement final : public sql::statement_impl
public:
postgres_statement(PGconn *db, PGresult *result, std::string name, const sql::query_context &query);
size_t execute() override;
std::unique_ptr<sql::query_result_impl> fetch() override;
utils::result<size_t, utils::error> execute() override;
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> fetch() override;
void reset() override;
protected:
sql::parameter_binder& binder() override;
utils::attribute_writer& binder() override;
private:
PGconn *db_{nullptr};
+129 -73
View File
@@ -3,67 +3,101 @@
#include "postgres_result_reader.hpp"
#include "postgres_statement.hpp"
#include "matador/sql/error_code.hpp"
#include "matador/sql/record.hpp"
#include <iostream>
#include "matador/sql/internal/query_result_impl.hpp"
#include <sstream>
namespace matador::backends::postgres {
postgres_connection::string_to_int_map postgres_connection::statement_name_map_{};
postgres_connection::postgres_connection(const sql::connection_info &info)
: connection_impl(info) {}
: connection_impl(info) {
}
void postgres_connection::open()
{
utils::result<void, utils::error> postgres_connection::open() {
if (is_open()) {
return;
return utils::ok<void>();
}
std::string connection("user=" + info().user + " password=" + info().password + " host=" + info().hostname + " dbname=" + info().database + " port=" + std::to_string(info().port));
const std::string connection(
"user=" + info().user + " password=" + info().password + " host=" + info().hostname + " dbname=" + info().database +
" port=" + std::to_string(info().port));
conn_ = PQconnectdb(connection.c_str());
if (PQstatus(conn_) == CONNECTION_BAD) {
const std::string msg = PQerrorMessage(conn_);
PQfinish(conn_);
conn_ = nullptr;
throw_postgres_error(msg.c_str(), "postgres");
return utils::failure(make_error(sql::error_code::OPEN_ERROR, nullptr, conn_, "Failed to connect"));
}
return utils::ok<void>();
}
void postgres_connection::close()
{
utils::result<void, utils::error> postgres_connection::close() {
if (conn_) {
PQfinish(conn_);
conn_ = nullptr;
}
return utils::ok<void>();
}
bool postgres_connection::is_open()
{
return conn_ != nullptr;
utils::result<bool, utils::error> postgres_connection::is_open() const {
return utils::ok(conn_ != nullptr);
}
std::unique_ptr<sql::query_result_impl> postgres_connection::fetch(const std::string &stmt)
{
PGresult *res = PQexec(conn_, stmt.c_str());
utils::result<bool, utils::error> postgres_connection::is_valid() const {
return utils::ok(PQstatus(conn_) == CONNECTION_OK);
}
throw_postgres_error(res, conn_, "postgres", stmt);
utils::result<utils::version, utils::error> postgres_connection::client_version() const {
const auto client_version = PQlibVersion();
return utils::ok(utils::version{
static_cast<unsigned int>(client_version / 10000),
static_cast<unsigned int>((client_version % 10000) / 100),
static_cast<unsigned int>(client_version % 100)
});
}
std::vector<sql::column_definition> prototype;
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);
utils::result<utils::version, utils::error> postgres_connection::server_version() const {
const auto server_version = PQserverVersion(conn_);
if (server_version == 0) {
return utils::failure(make_error(sql::error_code::FAILURE, nullptr, conn_, "Failed to get server version"));
}
return std::move(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res), std::move(prototype)));
return utils::ok(utils::version{
static_cast<unsigned int>(server_version / 10000),
static_cast<unsigned int>((server_version % 10000) / 100),
static_cast<unsigned int>(server_version % 100)
});
}
std::string postgres_connection::generate_statement_name(const sql::query_context &query)
{
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> postgres_connection::fetch(const sql::query_context &context) {
PGresult *res = PQexec(conn_, context.sql.c_str());
if (is_result_error(res)) {
return utils::failure(make_error(sql::error_code::FETCH_FAILED, res, conn_, "Failed to fetch", context.sql));
}
// std::vector<sql::column_definition> 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);
// }
return utils::ok(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res), context.prototype));
}
std::string postgres_connection::generate_statement_name(const sql::query_context &query) {
std::stringstream name;
name << query.table.name << "_" << query.command_name;
auto result = postgres_connection::statement_name_map_.find(name.str());
@@ -77,71 +111,85 @@ std::string postgres_connection::generate_statement_name(const sql::query_contex
return name.str();
}
std::unique_ptr<sql::statement_impl> postgres_connection::prepare(sql::query_context context)
{
utils::result<std::unique_ptr<sql::statement_impl>, utils::error> postgres_connection::prepare(const sql::query_context &context) {
auto statement_name = postgres_connection::generate_statement_name(context);
PGresult *result = PQprepare(conn_, statement_name.c_str(), context.sql.c_str(), static_cast<int>(context.bind_vars.size()), nullptr);
PGresult *result = PQprepare(conn_, statement_name.c_str(), context.sql.c_str(),
static_cast<int>(context.bind_vars.size()), nullptr);
throw_postgres_error(result, conn_, "postgres", context.sql);
if (is_result_error(result)) {
return utils::failure(make_error(sql::error_code::PREPARE_FAILED, result, conn_, "Failed to prepare", context.sql));
}
return std::make_unique<postgres_statement>(conn_, result, statement_name, std::move(context));
std::unique_ptr<sql::statement_impl> s(std::make_unique<postgres_statement>(conn_, result, statement_name, context));
return utils::ok(std::move(s));
}
size_t postgres_connection::execute(const std::string &stmt)
{
utils::result<size_t, utils::error> postgres_connection::execute(const std::string &stmt) {
PGresult *res = PQexec(conn_, stmt.c_str());
throw_postgres_error(res, conn_, "postgres", stmt);
if (const auto status = PQresultStatus(res); status != PGRES_COMMAND_OK &&
status != PGRES_TUPLES_OK) {
return utils::failure(make_error(sql::error_code::FAILURE, res, conn_, "Failed to execute", stmt));
}
const auto affected_rows = sql::to_long_long(PQcmdTuples(res));
const auto affected_rows = utils::to<size_t>(PQcmdTuples(res));
PQclear(res);
return affected_rows;
return utils::ok(static_cast<size_t>(affected_rows));
}
sql::data_type_t string2type(const char *type)
{
utils::basic_type string2type(const char *type) {
if (strcmp(type, "int2") == 0) {
return sql::data_type_t::type_short;
return utils::basic_type::type_int16;
} else if (strcmp(type, "int4") == 0) {
return sql::data_type_t::type_int;
return utils::basic_type::type_int32;
} 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;
return utils::basic_type::type_int64;
} else if (strcmp(type, "bool") == 0) {
return utils::basic_type::type_bool;
} 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;
return utils::basic_type::type_date;
} else if (strcmp(type, "timestamp") == 0) {
return utils::basic_type::type_time;
} else if (strcmp(type, "float4") == 0) {
return sql::data_type_t::type_float;
return utils::basic_type::type_float;
} else if (strcmp(type, "float8") == 0) {
return sql::data_type_t::type_double;
return utils::basic_type::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;
return utils::basic_type::type_varchar;
} else if (strcmp(type, "character varying") == 0) {
return utils::basic_type::type_varchar;
} else if (strcmp(type, "text") == 0) {
return utils::basic_type::type_text;
} else if (strcmp(type, "bytea") == 0) {
return utils::basic_type::type_blob;
} else {
return sql::data_type_t::type_unknown;
return utils::basic_type::type_null;
}
}
std::vector<sql::column_definition> postgres_connection::describe(const std::string &table)
{
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 + "'");
utils::result<std::vector<sql::column_definition>, utils::error> postgres_connection::describe(const std::string &table) {
const 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);
if (is_result_error(res)) {
return utils::failure(make_error(sql::error_code::DESCRIBE_FAILED, res, conn_, "Failed to describe", stmt));
}
postgres_result_reader reader(res);
std::vector<sql::column_definition> prototype;
while (reader.fetch()) {
while (auto fetched = reader.fetch()) {
if (!fetched.is_ok()) {
return utils::failure(fetched.release_error());
}
if (!*fetched) {
break;
}
char *end = nullptr;
// Todo: Handle error
auto index = strtoul(reader.column(0), &end, 10) - 1;
@@ -158,31 +206,39 @@ std::vector<sql::column_definition> postgres_connection::describe(const std::str
prototype.emplace_back(name, type, utils::null_attributes, null_opt, index);
}
return std::move(prototype);
return utils::ok(prototype);
}
bool postgres_connection::exists(const std::string &schema_name, const std::string &table_name)
{
std::string stmt("SELECT 1 FROM information_schema.tables WHERE table_schema = '" + schema_name + "' AND table_name = '" + table_name + "'");
utils::result<bool, utils::error> postgres_connection::exists(const std::string &schema_name, const std::string &table_name) {
const std::string stmt(
"SELECT 1 FROM information_schema.tables WHERE table_schema = '" + schema_name + "' AND table_name = '" + table_name
+ "'");
PGresult *res = PQexec(conn_, stmt.c_str());
throw_postgres_error(res, conn_, "postgres", stmt);
if (is_result_error(res)) {
return utils::failure(make_error(sql::error_code::TABLE_EXISTS_FAILED, res, conn_, "Failed check if table exists", stmt));
}
return sql::to_long_long(PQcmdTuples(res)) == 1;
return utils::ok(utils::to<size_t>(PQcmdTuples(res)) == 1);
}
std::string postgres_connection::to_escaped_string(const utils::blob& value) const
{
size_t escapedDataLength;
unsigned char *escapedData = PQescapeByteaConn(conn_, value.data(), value.size(), &escapedDataLength);
return {reinterpret_cast<char*>(escapedData), escapedDataLength-1};
}
}
extern "C"
{
MATADOR_POSTGRES_API matador::sql::connection_impl *create_database(const matador::sql::connection_info &info)
{
extern "C" {
MATADOR_POSTGRES_API matador::sql::connection_impl *create_database(const matador::sql::connection_info &info) {
return new matador::backends::postgres::postgres_connection(info);
}
MATADOR_POSTGRES_API void destroy_database(matador::sql::connection_impl *db)
{
MATADOR_POSTGRES_API void destroy_database(matador::sql::connection_impl *db) {
delete db;
}
}
+12 -4
View File
@@ -2,20 +2,28 @@
#include "matador/sql/dialect_builder.hpp"
#include "matador/utils/basic_types.hpp"
[[maybe_unused]] const matador::sql::dialect *get_dialect()
{
using namespace matador::sql;
const static dialect d = dialect_builder::builder()
.create()
.with_placeholder_func([](size_t index) {
.with_placeholder_func([](const size_t index) {
return "$" + std::to_string(index);
})
.with_token_replace_map({
{dialect::token_t::BEGIN_BINARY_DATA, "E'\\"}
{dialect_token::BEGIN_BINARY_DATA, "'"}
})
.with_data_type_replace_map({
{data_type_t::type_blob, "BYTEA"}
})
{matador::utils::basic_type::type_int8, "SMALLINT"},
{matador::utils::basic_type::type_uint8, "SMALLINT"},
{matador::utils::basic_type::type_float, "REAL"},
{matador::utils::basic_type::type_double, "DOUBLE PRECISION"},
{matador::utils::basic_type::type_time, "TIMESTAMP"},
{matador::utils::basic_type::type_blob, "BYTEA"}
})
.with_bool_strings("TRUE", "FALSE")
.with_default_schema_name("public")
.build();
return &d;
+27 -2
View File
@@ -6,6 +6,30 @@
namespace matador::backends::postgres {
utils::error make_error(const sql::error_code ec, const PGresult *res, const PGconn *db, const std::string &msg,
const std::string &sql) {
utils::error err(ec, msg);
err.add_error_info("dbms", "postgres");
if (!sql.empty()) {
err.add_error_info("sql", sql);
}
if (res == nullptr) {
err.add_error_info("message", PQerrorMessage(db));
} else if (const auto status = PQresultStatus(res); status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
err.add_error_info("message", PQresultErrorField(res, PG_DIAG_SQLSTATE));
}
return err;
}
bool is_result_error(const PGresult *res) {
if (res == nullptr) {
return true;
}
const auto status = PQresultStatus(res);
return status != PGRES_TUPLES_OK && status == PGRES_COMMAND_OK;
}
void throw_postgres_error(const char *what, const std::string &source)
{
std::stringstream msg;
@@ -26,8 +50,9 @@ void throw_postgres_error(PGresult *res, PGconn *db, const std::string &source,
std::stringstream msg;
msg << "postgres error (" << source << ", " << PQerrorMessage(db) << ": " << sql;
throw std::logic_error(msg.str());
} else if ((PQresultStatus(res) != PGRES_COMMAND_OK &&
PQresultStatus(res) != PGRES_TUPLES_OK)) {
}
if (const auto status = PQresultStatus(res); status != PGRES_COMMAND_OK &&
status != PGRES_TUPLES_OK) {
std::stringstream msg;
msg << "postgres error (" << source << ", " << PQresultErrorField(res, PG_DIAG_SQLSTATE) << ") " << PQerrorMessage(db) << ": " << sql;
throw std::logic_error(msg.str());
@@ -1,29 +1,32 @@
#include "postgres_parameter_binder.h"
#include "matador/utils/string.hpp"
#include <cstring>
namespace matador::backends::postgres {
namespace detail {
template < class T >
void bind_value(std::vector<std::string> &strings, std::vector<const char*> &params, size_t index, T &x)
{
strings[index] = std::to_string(x);
params[index] = strings[index].c_str();
template<class T>
void bind_value(postgres_parameter_binder::bind_data &data, const size_t index, const T &x) {
data.strings[index] = std::to_string(x);
data.values[index] = data.strings[index].c_str();
data.lengths[index] = static_cast<int>(data.strings[index].size());
data.formats[index] = 0;
}
template <>
void bind_value(std::vector<std::string> &strings, std::vector<const char*> &params, size_t index, char &x)
{
strings[index] = std::to_string(x);
params[index] = strings[index].data();
}
// template<>
// void bind_value(postgres_parameter_binder::bind_data &data, size_t index, const char &x) {
// data.strings[index] = std::to_string(x);
// data.values[index] = data.strings[index].data();
// data.formats[index] = 0;
// }
template <>
void bind_value(std::vector<std::string> &strings, std::vector<const char*> &params, size_t index, unsigned char &x)
{
strings[index] = std::to_string(x);
params[index] = strings[index].data();
}
// template<>
// void bind_value(postgres_parameter_binder::bind_data &data, size_t index, const unsigned char &x) {
// data.strings[index] = std::to_string(x);
// data.values[index] = data.strings[index].data();
// data.formats[index] = 0;
// }
//template <>
//void bind_value(std::vector<std::string> &strings, std::vector<const char*> &params, size_t &index, const matador::date &x)
@@ -40,108 +43,111 @@ void bind_value(std::vector<std::string> &strings, std::vector<const char*> &par
// params[index] = strings[index].c_str();
// ++index;
//}
}
postgres_parameter_binder::postgres_parameter_binder(size_t size)
: strings_(size)
, params_(size)
postgres_parameter_binder::bind_data::bind_data(const size_t size)
: strings(size)
, bytes(size)
, values(size)
, lengths(size)
, formats(size)
{}
void postgres_parameter_binder::bind(size_t pos, char i)
{
detail::bind_value(strings_, params_, pos, i);
postgres_parameter_binder::postgres_parameter_binder(size_t size)
: bind_data_(size)
{}
void postgres_parameter_binder::write_value(const size_t pos, const int8_t &x) {
detail::bind_value(bind_data_, pos, x);
}
void postgres_parameter_binder::bind(size_t pos, short i)
{
detail::bind_value(strings_, params_, pos, i);
void postgres_parameter_binder::write_value(const size_t pos, const int16_t &x) {
detail::bind_value(bind_data_, pos, x);
}
void postgres_parameter_binder::bind(size_t pos, int i)
{
detail::bind_value(strings_, params_, pos, i);
void postgres_parameter_binder::write_value(const size_t pos, const int32_t &x) {
detail::bind_value(bind_data_, pos, x);
}
void postgres_parameter_binder::bind(size_t pos, long i)
{
detail::bind_value(strings_, params_, pos, i);
void postgres_parameter_binder::write_value(const size_t pos, const int64_t &x) {
detail::bind_value(bind_data_, pos, x);
}
void postgres_parameter_binder::bind(size_t pos, long long int i)
{
detail::bind_value(strings_, params_, pos, i);
void postgres_parameter_binder::write_value(const size_t pos, const uint8_t &x) {
detail::bind_value(bind_data_, pos, x);
}
void postgres_parameter_binder::bind(size_t pos, unsigned char i)
{
detail::bind_value(strings_, params_, pos, i);
void postgres_parameter_binder::write_value(const size_t pos, const uint16_t &x) {
detail::bind_value(bind_data_, pos, x);
}
void postgres_parameter_binder::bind(size_t pos, unsigned short i)
{
detail::bind_value(strings_, params_, pos, i);
void postgres_parameter_binder::write_value(const size_t pos, const uint32_t &x) {
detail::bind_value(bind_data_, pos, x);
}
void postgres_parameter_binder::bind(size_t pos, unsigned int i)
{
detail::bind_value(strings_, params_, pos, i);
void postgres_parameter_binder::write_value(const size_t pos, const uint64_t &x) {
detail::bind_value(bind_data_, pos, x);
}
void postgres_parameter_binder::bind(size_t pos, unsigned long i)
{
detail::bind_value(strings_, params_, pos, i);
void postgres_parameter_binder::write_value(const size_t pos, const bool &x) {
detail::bind_value(bind_data_, pos, x);
}
void postgres_parameter_binder::bind(size_t pos, unsigned long long int i)
{
detail::bind_value(strings_, params_, pos, i);
void postgres_parameter_binder::write_value(const size_t pos, const float &x) {
detail::bind_value(bind_data_, pos, x);
}
void postgres_parameter_binder::bind(size_t pos, bool b)
{
detail::bind_value(strings_, params_, pos, b);
void postgres_parameter_binder::write_value(const size_t pos, const double &x) {
detail::bind_value(bind_data_, pos, x);
}
void postgres_parameter_binder::bind(size_t pos, float d)
{
detail::bind_value(strings_, params_, pos, d);
void postgres_parameter_binder::write_value(const size_t pos, const char *x) {
bind_data_.values[pos] = x;
bind_data_.lengths[pos] = static_cast<int>(std::strlen(x));
bind_data_.formats[pos] = 0;
}
void postgres_parameter_binder::bind(size_t pos, double d)
{
detail::bind_value(strings_, params_, pos, d);
void postgres_parameter_binder::write_value(const size_t pos, const char *x, size_t /*size*/) {
bind_data_.values[pos] = x;
bind_data_.lengths[pos] = static_cast<int>(std::strlen(x));
bind_data_.formats[pos] = 0;
}
void postgres_parameter_binder::bind(size_t pos, const char *str)
{
params_[pos] = str;
void postgres_parameter_binder::write_value(const size_t pos, const std::string &x) {
bind_data_.values[pos] = x.data();
bind_data_.lengths[pos] = static_cast<int>(x.size());
bind_data_.formats[pos] = 0;
}
void postgres_parameter_binder::bind(size_t pos, const char *str, size_t size)
{
params_[pos] = str;
void postgres_parameter_binder::write_value(const size_t pos, const std::string &x, size_t /*size*/) {
write_value(pos, x);
}
void postgres_parameter_binder::bind(size_t pos, const std::string &str)
{
strings_[pos] = str;
params_[pos] = strings_[pos].c_str();
void postgres_parameter_binder::write_value(const size_t pos, const time &/*x*/) {
// bind_data_.strings[pos] = utils::to_string(x, "%Y-%m-%d %T.%f");
bind_data_.values[pos] = bind_data_.strings[pos].data();
bind_data_.lengths[pos] = static_cast<int>(bind_data_.strings[pos].size());
bind_data_.formats[pos] = 0;
}
void postgres_parameter_binder::bind(size_t pos, const std::string &str, size_t size)
{
bind(pos, str);
void postgres_parameter_binder::write_value(const size_t pos, const date &/*x*/) {
// bind_data_.strings[pos] = utils::to_string(x, utils::date_format::ISO8601);
bind_data_.values[pos] = bind_data_.strings[pos].data();
bind_data_.lengths[pos] = static_cast<int>(bind_data_.strings[pos].size());
bind_data_.formats[pos] = 0;
}
void postgres_parameter_binder::bind(size_t pos, const utils::blob &blob)
{
params_[pos] = "";
void postgres_parameter_binder::write_value(const size_t pos, const utils::blob &x) {
bind_data_.bytes[pos] = x;
bind_data_.values[pos] = reinterpret_cast<char*>(bind_data_.bytes[pos].data());
bind_data_.lengths[pos] = static_cast<int>(bind_data_.bytes[pos].size());
bind_data_.formats[pos] = 1;
}
const std::vector<const char *> &postgres_parameter_binder::params() const
{
return params_;
void postgres_parameter_binder::write_value(const size_t /*pos*/, const utils::value &/*x*/, size_t /*size*/) {}
const postgres_parameter_binder::bind_data &postgres_parameter_binder::params() const {
return bind_data_;
}
}
}
@@ -1,6 +1,7 @@
#include "postgres_result_reader.hpp"
#include "matador/sql/to_value.hpp"
#include "matador/utils/convert.hpp"
#include "matador/utils/value.hpp"
namespace matador::backends::postgres {
@@ -22,14 +23,209 @@ size_t postgres_result_reader::column_count() const
return column_count_;
}
const char *postgres_result_reader::column(size_t index) const
const char *postgres_result_reader::column( const size_t index) const
{
return PQgetvalue(result_, static_cast<int>(row_index_), static_cast<int>(index));
}
bool postgres_result_reader::fetch()
{
return ++row_index_ < row_count_;
utils::result<bool, utils::error> postgres_result_reader::fetch() { return utils::ok(++row_index_ < row_count_); }
size_t postgres_result_reader::start_column_index() const {
return 0;
}
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, int8_t &value) {
if (auto res = utils::to<int8_t>(column(index)); res.is_ok()) {
value = res.value();
}
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, int16_t &value) {
if (auto res = utils::to<int16_t>(column(index)); res.is_ok()) {
value = res.value();
}
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, int32_t &value) {
if (auto res = utils::to<int32_t>(column(index)); res.is_ok()) {
value = res.value();
}
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, int64_t &value) {
if (auto res = utils::to<int64_t>(column(index)); res.is_ok()) {
value = res.value();
}
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, uint8_t &value) {
if (auto res = utils::to<uint8_t>(column(index)); res.is_ok()) {
value = res.value();
}
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, uint16_t &value) {
if (auto res = utils::to<uint16_t>(column(index)); res.is_ok()) {
value = res.value();
}
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, uint32_t &value) {
if (auto res = utils::to<uint32_t>(column(index)); res.is_ok()) {
value = res.value();
}
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, uint64_t &value) {
if (auto res = utils::to<uint64_t>(column(index)); res.is_ok()) {
value = res.value();
}
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, bool &value) {
if (auto res = utils::to<bool>(column(index)); res.is_ok()) {
value = res.value();
}
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, float &value) {
if (auto res = utils::to<float>(column(index)); res.is_ok()) {
value = res.value();
}
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, double &value) {
if (auto res = utils::to<double>(column(index)); res.is_ok()) {
value = res.value();
}
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, time &value) {
// if (const auto val = column(index); strlen(val) > 0) {
// value = time::parse(val, "%Y-%m-%d %T.%f");
// }
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, date &value) {
// if (const auto val = column(index); strlen(val) > 0) {
// value.set(val, matador::utils::date_format::ISO8601);
// }
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, char *value, size_t size) {
auto val = column(index);
if (const size_t len = strlen(val); len > size) {
#ifdef _MSC_VER
strncpy_s(value, size, val, len);
#else
strncpy(value, val, size);
#endif
value[size-1] = '\n';
} else {
#ifdef _MSC_VER
strcpy_s(value, size, val);
#else
strcpy(value, val);
#endif
}
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, std::string &value) {
value.assign(column(index));
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, std::string &value, size_t /*s*/) {
value.assign(column(index));
}
void postgres_result_reader::read_value( const char* id, const size_t index, utils::blob& value )
{
const auto *data = reinterpret_cast<const unsigned char*>(column(index));
// auto length = PQgetlength(result_, row_index_, static_cast<int>(index));
size_t length;
unsigned char* unescaped = PQunescapeBytea(data, &length);
value.assign(unescaped, unescaped+length);
}
template <typename Type>
void set_value(const char* str, utils::value& value) {
if (const auto res = utils::to<Type>(str); res.is_ok()) {
value = res.value();
}
}
template <>
void set_value<utils::blob>(const char* str, utils::value& value) {
size_t length;
unsigned char* unescaped = PQunescapeBytea(reinterpret_cast<const unsigned char*>(str), &length);
value = utils::blob(unescaped, unescaped+length);
}
void postgres_result_reader::read_value(const char * /*id*/, const size_t index, utils::value &val, size_t) {
switch (val.type()) {
case utils::basic_type::type_int8:
set_value<int8_t>(column(index), val);
break;
case utils::basic_type::type_int16:
set_value<int16_t>(column(index), val);
break;
case utils::basic_type::type_int32:
set_value<int32_t>(column(index), val);
break;
case utils::basic_type::type_int64:
set_value<int64_t>(column(index), val);
break;
case utils::basic_type::type_uint8:
set_value<uint8_t>(column(index), val);
break;
case utils::basic_type::type_uint16:
set_value<uint16_t>(column(index), val);
break;
case utils::basic_type::type_uint32:
set_value<uint32_t>(column(index), val);
break;
case utils::basic_type::type_uint64:
set_value<uint64_t>(column(index), val);
break;
case utils::basic_type::type_float:
set_value<float>(column(index), val);
break;
case utils::basic_type::type_double:
set_value<double>(column(index), val);
break;
case utils::basic_type::type_bool:
set_value<bool>(column(index), val);
break;
case utils::basic_type::type_text:
case utils::basic_type::type_varchar: {
if (const auto *column_value = column(index); column_value == nullptr) {
val = std::string{};
} else {
val = std::string{column_value};
}
break;
}
case utils::basic_type::type_time:
case utils::basic_type::type_date: {
val = std::string{column(index)};
break;
}
case utils::basic_type::type_null: {
val = nullptr_t{};
break;
}
case utils::basic_type::type_blob: {
set_value<utils::blob>(column(index), val);
break;
}
}
}
utils::attribute_reader &postgres_result_reader::result_binder() {
return empty_binder_;
}
} // namespace matador::backends::postgres
+30 -9
View File
@@ -12,27 +12,48 @@ postgres_statement::postgres_statement(PGconn *db, PGresult *result, std::string
, binder_(query_.bind_vars.size())
{}
size_t postgres_statement::execute()
utils::result<size_t, utils::error> postgres_statement::execute()
{
PGresult *res = PQexecPrepared(db_, name_.c_str(), static_cast<int>(binder_.params().size()), binder_.params().data(), nullptr, nullptr, 0);
PGresult *res = PQexecPrepared(db_,
name_.c_str(),
static_cast<int>(binder_.params().values.size()),
binder_.params().values.data(),
binder_.params().lengths.data(),
binder_.params().formats.data(),
0);
throw_postgres_error(res, db_, "postgres", query_.sql);
if (is_result_error(res)) {
return utils::failure(make_error(sql::error_code::EXECUTE_FAILED, res, db_, "Failed to execute statement", query_.sql));
}
return std::stoul(PQcmdTuples(res));
const auto *tuples = PQcmdTuples(res);
if (strlen(tuples) == 0) {
return utils::ok(static_cast<size_t>(0));
}
return utils::ok(static_cast<size_t>(std::stoul(tuples)));
}
std::unique_ptr<sql::query_result_impl> postgres_statement::fetch()
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> postgres_statement::fetch()
{
PGresult *res = PQexecPrepared(db_, name_.c_str(), static_cast<int>(binder_.params().size()), binder_.params().data(), nullptr, nullptr, 0);
PGresult *res = PQexecPrepared(db_,
name_.c_str(),
static_cast<int>(binder_.params().values.size()),
binder_.params().values.data(),
binder_.params().lengths.data(),
binder_.params().formats.data(),
0);
throw_postgres_error(res, db_, "postgres", query_.sql);
if (is_result_error(res)) {
return utils::failure(make_error(sql::error_code::FETCH_FAILED, res, db_, "Failed to fetch statement", query_.sql));
}
return std::move(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res), std::move(query_.prototype)));
return utils::ok(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res), query_.prototype));
}
void postgres_statement::reset() {}
sql::parameter_binder& postgres_statement::binder()
utils::attribute_writer& postgres_statement::binder()
{
return binder_;
}
+27 -22
View File
@@ -1,45 +1,50 @@
Include(FetchContent)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.5.4 # or a later release
)
FetchContent_MakeAvailable(Catch2)
CPMAddPackage("gh:catchorg/Catch2@3.7.1")
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
include(CTest)
include(Catch)
set(POSTGRES_CONNECTION_STRING "postgres://test:test123@127.0.0.1:15432/test")
set(POSTGRES_CONNECTION_STRING "postgres://test:test123@localhost:15432/testdb")
configure_file(Connection.hpp.in ${PROJECT_BINARY_DIR}/backends/postgres/test/connection.hpp @ONLY IMMEDIATE)
message(STATUS "postgresql connection string: ${POSTGRES_CONNECTION_STRING}")
set(TEST_SOURCES
../../tests/QueryTest.cpp
../../tests/ConnectionTest.cpp
../../tests/QueryRecordTest.cpp
../../tests/StatementTest.cpp
../../tests/TypeTraitsTest.cpp
../../tests/StatementCacheTest.cpp
../../tests/SessionTest.cpp)
../../../test/models/coordinate.hpp
../../../test/models/location.hpp
../../../test/models/types.hpp
../../../test/backends/ColorEnumTraits.cpp
../../../test/backends/ColorEnumTraits.hpp
../../../test/backends/ConnectionTest.cpp
../../../test/backends/QueryBasicTest.cpp
../../../test/backends/QueryFixture.cpp
../../../test/backends/QueryFixture.hpp
../../../test/backends/QueryRecordTest.cpp
../../../test/backends/QueryStatementTests.cpp
../../../test/backends/QueryTest.cpp
../../../test/backends/SessionFixture.cpp
../../../test/backends/SessionFixture.hpp
../../../test/backends/SessionTest.cpp
../../../test/backends/StatementCacheTest.cpp
../../../test/backends/StatementTest.cpp
../../../test/backends/TypeTraitsTest.cpp
)
set(LIBRARY_TEST_TARGET postgres_tests)
set(LIBRARY_TEST_TARGET PostgresTests)
add_executable(${LIBRARY_TEST_TARGET} ${TEST_SOURCES})
target_link_libraries(${LIBRARY_TEST_TARGET} PRIVATE
Catch2::Catch2WithMain
matador
matador-utils
matador-query
${CMAKE_DL_LIBS}
${PostgreSQL_LIBRARY})
add_dependencies(${LIBRARY_TEST_TARGET} matador-postgres)
target_include_directories(${LIBRARY_TEST_TARGET}
PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>/include
PRIVATE $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>/test
PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
catch_discover_tests(${LIBRARY_TEST_TARGET} TEST_SUFFIX " (PostgreSQL)")
#catch_discover_tests(${LIBRARY_TEST_TARGET} TEST_SUFFIX " (PostgreSQL)")