initial matador ng commit
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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*> ¶ms, 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*> ¶ms, 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*> ¶ms, 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*> ¶ms, 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
|
||||
|
||||
@@ -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_;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user