implemented prepared statement for postgres and sqlite
This commit is contained in:
@@ -3,6 +3,8 @@ set(HEADER
|
||||
include/postgres_error.hpp
|
||||
include/postgres_result_reader.hpp
|
||||
include/postgres_dialect.hpp
|
||||
include/postgres_statement.hpp
|
||||
include/postgres_parameter_binder.h
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
@@ -10,17 +12,22 @@ set(SOURCES
|
||||
src/postgres_error.cpp
|
||||
src/postgres_result_reader.cpp
|
||||
src/postgres_dialect.cpp
|
||||
src/postgres_statement.cpp
|
||||
src/postgres_parameter_binder.cpp
|
||||
)
|
||||
|
||||
add_library(matador-postgres SHARED ${SOURCES} ${HEADER})
|
||||
target_include_directories(matador-postgres PRIVATE
|
||||
set(LIBRARY_TARGET matador-postgres)
|
||||
|
||||
add_library(${LIBRARY_TARGET} MODULE ${SOURCES} ${HEADER})
|
||||
|
||||
set_target_properties(${LIBRARY_TARGET}
|
||||
PROPERTIES
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/backends"
|
||||
)
|
||||
|
||||
target_include_directories(${LIBRARY_TARGET} PRIVATE
|
||||
${PROJECT_SOURCE_DIR}/include
|
||||
${PROJECT_SOURCE_DIR}/backends/postgres/include
|
||||
${PostgreSQL_INCLUDE_DIRS})
|
||||
|
||||
target_link_libraries(matador-postgres matador ${PostgreSQL_LIBRARIES})
|
||||
|
||||
set_target_properties(matador-postgres
|
||||
PROPERTIES
|
||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/backends"
|
||||
)
|
||||
target_link_libraries(${LIBRARY_TARGET} matador ${PostgreSQL_LIBRARIES})
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
#include "matador/sql/connection_impl.hpp"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include <libpq-fe.h>
|
||||
|
||||
namespace matador::backends::postgres {
|
||||
@@ -27,7 +29,7 @@ public:
|
||||
bool is_open() override;
|
||||
|
||||
std::unique_ptr<sql::query_result_impl> fetch(const std::string &stmt) override;
|
||||
void prepare(const std::string &stmt) override;
|
||||
std::unique_ptr<sql::statement_impl> prepare(sql::query_context context) override;
|
||||
|
||||
size_t execute(const std::string &stmt) override;
|
||||
|
||||
@@ -35,8 +37,15 @@ public:
|
||||
|
||||
bool exists(const std::string &table_name) override;
|
||||
|
||||
private:
|
||||
[[nodiscard]] static std::string generate_statement_name(const sql::query_context &query) ;
|
||||
|
||||
private:
|
||||
PGconn *conn_{nullptr};
|
||||
|
||||
using string_to_int_map = std::unordered_map<std::string, unsigned long>;
|
||||
|
||||
static string_to_int_map statement_name_map_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef QUERY_POSTGRES_PARAMETER_BINDER_H
|
||||
#define QUERY_POSTGRES_PARAMETER_BINDER_H
|
||||
|
||||
#include "matador/sql/parameter_binder.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace matador::backends::postgres {
|
||||
|
||||
class postgres_parameter_binder final : public sql::parameter_binder
|
||||
{
|
||||
public:
|
||||
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;
|
||||
|
||||
const std::vector<const char*>& params() const;
|
||||
|
||||
private:
|
||||
std::vector<std::string> strings_;
|
||||
std::vector<const char*> params_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //QUERY_POSTGRES_PARAMETER_BINDER_H
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef QUERY_POSTGRES_STATEMENT_HPP
|
||||
#define QUERY_POSTGRES_STATEMENT_HPP
|
||||
|
||||
#include "matador/sql/statement_impl.hpp"
|
||||
|
||||
#include "postgres_parameter_binder.h"
|
||||
|
||||
#include <libpq-fe.h>
|
||||
|
||||
namespace matador::backends::postgres {
|
||||
|
||||
class postgres_statement final : public sql::statement_impl
|
||||
{
|
||||
public:
|
||||
postgres_statement(PGconn *db, PGresult *result, const std::string &name, const sql::query_context &query);
|
||||
|
||||
size_t execute() override;
|
||||
std::unique_ptr<sql::query_result_impl> fetch() override;
|
||||
void reset() override;
|
||||
protected:
|
||||
sql::parameter_binder& binder() override;
|
||||
|
||||
private:
|
||||
PGconn *db_{nullptr};
|
||||
PGresult *result_{nullptr};
|
||||
|
||||
std::string name_;
|
||||
|
||||
postgres_parameter_binder binder_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //QUERY_POSTGRES_STATEMENT_HPP
|
||||
@@ -1,13 +1,17 @@
|
||||
#include "postgres_connection.hpp"
|
||||
#include "postgres_error.hpp"
|
||||
#include "postgres_result_reader.hpp"
|
||||
#include "postgres_statement.hpp"
|
||||
|
||||
#include "matador/sql/record.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#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) {}
|
||||
|
||||
@@ -47,20 +51,41 @@ std::unique_ptr<sql::query_result_impl> postgres_connection::fetch(const std::st
|
||||
throw_postgres_error(res, conn_, "postgres", stmt);
|
||||
|
||||
sql::record prototype;
|
||||
auto ncol = PQnfields(res);
|
||||
for (int i = 0; i < ncol; ++i) {
|
||||
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);
|
||||
std::cout << "column " << col_name << ", type " << type << " (size: " << size << ")\n";
|
||||
// std::cout << "column " << col_name << ", type " << type << " (size: " << size << ")\n";
|
||||
prototype.append({col_name});
|
||||
}
|
||||
return std::move(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res), std::move(prototype)));
|
||||
}
|
||||
|
||||
void postgres_connection::prepare(const std::string &stmt)
|
||||
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());
|
||||
|
||||
if (result == postgres_connection::statement_name_map_.end()) {
|
||||
result = postgres_connection::statement_name_map_.insert(std::make_pair(name.str(), 0)).first;
|
||||
}
|
||||
|
||||
name << "_" << ++result->second;
|
||||
|
||||
return name.str();
|
||||
}
|
||||
|
||||
std::unique_ptr<sql::statement_impl> postgres_connection::prepare(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);
|
||||
|
||||
throw_postgres_error(result, conn_, "postgres", context.sql);
|
||||
|
||||
return std::make_unique<postgres_statement>(conn_, result, statement_name, std::move(context));
|
||||
}
|
||||
|
||||
size_t postgres_connection::execute(const std::string &stmt)
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
#include "postgres_dialect.hpp"
|
||||
|
||||
#include "matador/sql/dialect_builder.hpp"
|
||||
|
||||
[[maybe_unused]] const matador::sql::dialect* get_dialect() {
|
||||
using namespace matador::sql;
|
||||
const static dialect d{};
|
||||
const static dialect d = dialect_builder::builder()
|
||||
.create()
|
||||
.with_placeholder_func([](size_t index) {
|
||||
return "$" + std::to_string(index);
|
||||
})
|
||||
.with_default_schema_name("public")
|
||||
.build();
|
||||
return &d;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
#include "postgres_parameter_binder.h"
|
||||
|
||||
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 <>
|
||||
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(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(std::vector<std::string> &strings, std::vector<const char*> ¶ms, size_t &index, const matador::date &x)
|
||||
//{
|
||||
// strings[index] = matador::to_string(x, date_format::ISO8601);
|
||||
// params[index] = strings[index].c_str();
|
||||
// ++index;
|
||||
//}
|
||||
//
|
||||
//template <>
|
||||
//void bind_value(std::vector<std::string> &strings, std::vector<const char*> ¶ms, size_t &index, const matador::time &x)
|
||||
//{
|
||||
// strings[index] = matador::to_string(x, "%Y-%m-%d %T.%f");
|
||||
// params[index] = strings[index].c_str();
|
||||
// ++index;
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
postgres_parameter_binder::postgres_parameter_binder(size_t size)
|
||||
: strings_(size)
|
||||
, params_(size)
|
||||
{}
|
||||
|
||||
void postgres_parameter_binder::bind(size_t pos, char i)
|
||||
{
|
||||
detail::bind_value(strings_, params_, pos, i);
|
||||
}
|
||||
|
||||
void postgres_parameter_binder::bind(size_t pos, short i)
|
||||
{
|
||||
detail::bind_value(strings_, params_, pos, i);
|
||||
}
|
||||
|
||||
void postgres_parameter_binder::bind(size_t pos, int i)
|
||||
{
|
||||
detail::bind_value(strings_, params_, pos, i);
|
||||
}
|
||||
|
||||
void postgres_parameter_binder::bind(size_t pos, long i)
|
||||
{
|
||||
detail::bind_value(strings_, params_, pos, i);
|
||||
}
|
||||
|
||||
void postgres_parameter_binder::bind(size_t pos, long long int i)
|
||||
{
|
||||
detail::bind_value(strings_, params_, pos, i);
|
||||
}
|
||||
|
||||
void postgres_parameter_binder::bind(size_t pos, unsigned char i)
|
||||
{
|
||||
detail::bind_value(strings_, params_, pos, i);
|
||||
}
|
||||
|
||||
void postgres_parameter_binder::bind(size_t pos, unsigned short i)
|
||||
{
|
||||
detail::bind_value(strings_, params_, pos, i);
|
||||
}
|
||||
|
||||
void postgres_parameter_binder::bind(size_t pos, unsigned int i)
|
||||
{
|
||||
detail::bind_value(strings_, params_, pos, i);
|
||||
}
|
||||
|
||||
void postgres_parameter_binder::bind(size_t pos, unsigned long i)
|
||||
{
|
||||
detail::bind_value(strings_, params_, pos, i);
|
||||
}
|
||||
|
||||
void postgres_parameter_binder::bind(size_t pos, unsigned long long int i)
|
||||
{
|
||||
detail::bind_value(strings_, params_, pos, i);
|
||||
}
|
||||
|
||||
void postgres_parameter_binder::bind(size_t pos, bool b)
|
||||
{
|
||||
detail::bind_value(strings_, params_, pos, b);
|
||||
}
|
||||
|
||||
void postgres_parameter_binder::bind(size_t pos, float d)
|
||||
{
|
||||
detail::bind_value(strings_, params_, pos, d);
|
||||
}
|
||||
|
||||
void postgres_parameter_binder::bind(size_t pos, double d)
|
||||
{
|
||||
detail::bind_value(strings_, params_, pos, d);
|
||||
}
|
||||
|
||||
void postgres_parameter_binder::bind(size_t pos, const char *str)
|
||||
{
|
||||
params_[pos] = str;
|
||||
}
|
||||
|
||||
void postgres_parameter_binder::bind(size_t pos, const char *str, size_t size)
|
||||
{
|
||||
params_[pos] = str;
|
||||
}
|
||||
|
||||
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::bind(size_t pos, const std::string &str, size_t size)
|
||||
{
|
||||
bind(pos, str);
|
||||
}
|
||||
|
||||
const std::vector<const char *> &postgres_parameter_binder::params() const
|
||||
{
|
||||
return params_;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "postgres_statement.hpp"
|
||||
#include "postgres_error.hpp"
|
||||
#include "postgres_result_reader.hpp"
|
||||
|
||||
namespace matador::backends::postgres {
|
||||
|
||||
postgres_statement::postgres_statement(PGconn *db, PGresult *result, const std::string &name, const sql::query_context &query)
|
||||
: statement_impl(query)
|
||||
, db_(db)
|
||||
, result_(result)
|
||||
, name_(name)
|
||||
, binder_(query_.bind_vars.size())
|
||||
{}
|
||||
|
||||
size_t postgres_statement::execute()
|
||||
{
|
||||
PGresult *res = PQexecPrepared(db_, name_.c_str(), static_cast<int>(binder_.params().size()), binder_.params().data(), nullptr, nullptr, 0);
|
||||
|
||||
throw_postgres_error(res, db_, "postgres", query_.sql);
|
||||
|
||||
return std::stoul(PQcmdTuples(res));
|
||||
}
|
||||
|
||||
std::unique_ptr<sql::query_result_impl> postgres_statement::fetch()
|
||||
{
|
||||
PGresult *res = PQexecPrepared(db_, name_.c_str(), static_cast<int>(binder_.params().size()), binder_.params().data(), nullptr, nullptr, 0);
|
||||
|
||||
throw_postgres_error(res, db_, "postgres", query_.sql);
|
||||
|
||||
return std::move(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res), std::move(query_.prototype)));
|
||||
}
|
||||
|
||||
void postgres_statement::reset() {}
|
||||
|
||||
sql::parameter_binder& postgres_statement::binder()
|
||||
{
|
||||
return binder_;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user