implemented prepared statement for postgres and sqlite

This commit is contained in:
2023-12-09 11:08:03 +01:00
parent 8ba70cc79e
commit da423ea8bb
59 changed files with 1769 additions and 314 deletions
+17 -7
View File
@@ -3,6 +3,9 @@ set(HEADER
include/sqlite_error.hpp
include/sqlite_dialect.hpp
include/sqlite_result_reader.hpp
include/sqlite_statement.hpp
include/sqlite_parameter_binder.h
include/sqlite_prepared_result_reader.hpp
)
set(SOURCES
@@ -10,16 +13,23 @@ set(SOURCES
src/sqlite_error.cpp
src/sqlite_dialect.cpp
src/sqlite_result_reader.cpp
src/sqlite_statement.cpp
src/sqlite_parameter_binder.cpp
src/sqlite_prepared_result_reader.cpp
)
add_library(matador-sqlite SHARED ${SOURCES} ${HEADER})
target_include_directories(matador-sqlite PRIVATE
set(LIBRARY_TARGET matador-sqlite)
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/sqlite/include
${SQLite3_INCLUDE_DIRS})
target_link_libraries(matador-sqlite matador ${SQLite3_LIBRARIES})
set_target_properties(matador-sqlite
PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/backends"
)
target_link_libraries(${LIBRARY_TARGET} matador ${SQLite3_LIBRARIES})
@@ -29,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 query) override;
size_t execute(const std::string &stmt) override;
@@ -50,7 +50,7 @@ private:
fetch_context fetch_internal(const std::string &stmt);
private:
sqlite3 *sqlite_db_{};
sqlite3 *db_{};
};
}
@@ -0,0 +1,44 @@
#ifndef QUERY_SQLITE_PARAMETER_BINDER_H
#define QUERY_SQLITE_PARAMETER_BINDER_H
#include "matador/sql/parameter_binder.hpp"
#include <sqlite3.h>
#include <memory>
#include <vector>
namespace matador::backends::sqlite {
class sqlite_parameter_binder final : public sql::parameter_binder
{
public:
explicit sqlite_parameter_binder(sqlite3 *db, sqlite3_stmt *stmt);
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 *str, size_t size) override;
void bind(size_t pos, const std::string &str) override;
void bind(size_t pos, const std::string &str, size_t size) override;
private:
sqlite3 *db_{nullptr};
sqlite3_stmt *stmt_{nullptr};
std::vector<std::shared_ptr<std::string> > host_strings_;
};
}
#endif //QUERY_SQLITE_PARAMETER_BINDER_H
@@ -0,0 +1,42 @@
#ifndef QUERY_SQLITE_PREPARED_RESULT_READER_HPP
#define QUERY_SQLITE_PREPARED_RESULT_READER_HPP
#include "matador/sql/query_result_reader.hpp"
#include <sqlite3.h>
namespace matador::backends::sqlite {
class sqlite_prepared_result_reader final : public sql::query_result_reader
{
public:
sqlite_prepared_result_reader(sqlite3 *db, sqlite3_stmt *stmt);
[[nodiscard]] size_t column_count() const override;
[[nodiscard]] const char *column(size_t index) const override;
bool fetch() override;
void read_value(const char *id, size_t index, char &value) override;
void read_value(const char *id, size_t index, short &value) override;
void read_value(const char *id, size_t index, int &value) override;
void read_value(const char *id, size_t index, long &value) override;
void read_value(const char *id, size_t index, long long int &value) override;
void read_value(const char *id, size_t index, unsigned char &value) override;
void read_value(const char *id, size_t index, unsigned short &value) override;
void read_value(const char *id, size_t index, unsigned int &value) override;
void read_value(const char *id, size_t index, unsigned long &value) override;
void read_value(const char *id, size_t index, unsigned long long int &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, char *value, size_t s) override;
void read_value(const char *id, size_t index, std::string &value) override;
void read_value(const char *id, size_t index, std::string &value, size_t s) override;
void read_value(const char *id, size_t index, sql::any_type &value, sql::data_type_t type, size_t size) override;
private:
sqlite3 *db_{nullptr};
sqlite3_stmt *stmt_{nullptr};
};
}
#endif //QUERY_SQLITE_PREPARED_RESULT_READER_HPP
@@ -7,7 +7,7 @@
namespace matador::backends::sqlite {
class sqlite_result_reader : public sql::query_result_reader
class sqlite_result_reader final : public sql::query_result_reader
{
public:
using columns = std::vector<char*>;
@@ -0,0 +1,31 @@
#ifndef QUERY_SQLITE_STATEMENT_HPP
#define QUERY_SQLITE_STATEMENT_HPP
#include "matador/sql/statement_impl.hpp"
#include "sqlite_parameter_binder.h"
namespace matador::backends::sqlite {
class sqlite_statement final : public sql::statement_impl
{
public:
sqlite_statement(sqlite3 *db, sqlite3_stmt *stmt, const sql::query_context &query);
~sqlite_statement();
size_t execute() override;
std::unique_ptr<sql::query_result_impl> fetch() override;
void reset() override;
protected:
sql::parameter_binder& binder() override;
private:
sqlite3 *db_{nullptr};
sqlite3_stmt *stmt_{nullptr};
sqlite_parameter_binder binder_;
};
}
#endif //QUERY_SQLITE_STATEMENT_HPP
+19 -13
View File
@@ -1,6 +1,7 @@
#include "sqlite_connection.hpp"
#include "sqlite_error.hpp"
#include "sqlite_result_reader.hpp"
#include "sqlite_statement.hpp"
#include "matador/sql/record.hpp"
@@ -20,25 +21,25 @@ void sqlite_connection::open()
return;
}
const auto ret = sqlite3_open(info().database.c_str(), &sqlite_db_);
const auto ret = sqlite3_open(info().database.c_str(), &db_);
if (ret != SQLITE_OK) {
throw_sqlite_error(ret, sqlite_db_, "open");
throw_sqlite_error(ret, db_, "open");
}
}
void sqlite_connection::close()
{
int ret = sqlite3_close(sqlite_db_);
int ret = sqlite3_close(db_);
throw_sqlite_error(ret, sqlite_db_, "close");
throw_sqlite_error(ret, db_, "close");
sqlite_db_ = nullptr;
db_ = nullptr;
}
bool sqlite_connection::is_open()
{
return sqlite_db_ != nullptr;
return db_ != nullptr;
}
int sqlite_connection::parse_result(void* param, int column_count, char** values, char** columns)
@@ -75,9 +76,9 @@ sqlite_connection::fetch_context sqlite_connection::fetch_internal(const std::st
{
fetch_context context;
char *errmsg = nullptr;
const int ret = sqlite3_exec(sqlite_db_, stmt.c_str(), parse_result, &context, &errmsg);
const int ret = sqlite3_exec(db_, stmt.c_str(), parse_result, &context, &errmsg);
throw_sqlite_error(ret, sqlite_db_, "sqlite", stmt);
throw_sqlite_error(ret, db_, "sqlite", stmt);
return context;
}
@@ -85,11 +86,11 @@ sqlite_connection::fetch_context sqlite_connection::fetch_internal(const std::st
size_t sqlite_connection::execute(const std::string &stmt)
{
char *errmsg = nullptr;
int ret = sqlite3_exec(sqlite_db_, stmt.c_str(), nullptr, nullptr, &errmsg);
int ret = sqlite3_exec(db_, stmt.c_str(), nullptr, nullptr, &errmsg);
throw_sqlite_error(ret, sqlite_db_, "sqlite", stmt);
throw_sqlite_error(ret, db_, "sqlite", stmt);
return sqlite3_changes(sqlite_db_);
return sqlite3_changes(db_);
}
std::unique_ptr<sql::query_result_impl> sqlite_connection::fetch(const std::string &stmt)
@@ -99,8 +100,13 @@ std::unique_ptr<sql::query_result_impl> sqlite_connection::fetch(const std::stri
return std::move(std::make_unique<sql::query_result_impl>(std::make_unique<sqlite_result_reader>(std::move(context.rows), context.prototype.size()), std::move(context.prototype)));
}
void sqlite_connection::prepare(const std::string &stmt)
std::unique_ptr<sql::statement_impl> sqlite_connection::prepare(sql::query_context query)
{
sqlite3_stmt *stmt{};
int ret = sqlite3_prepare_v2(db_, query.sql.c_str(), static_cast<int>(query.sql.size()), &stmt, nullptr);
throw_sqlite_error(ret, db_, "sqlite3_prepare_v2", query.sql);
return std::make_unique<sqlite_statement>(db_, stmt, query);
}
sql::data_type_t string2type(const char *type)
@@ -142,7 +148,7 @@ sql::record sqlite_connection::describe(const std::string& table)
{
const auto result = fetch_internal("PRAGMA table_info(" + table + ")");
sqlite_result_reader reader(std::move(result.rows), result.prototype.size());
sqlite_result_reader reader(result.rows, result.prototype.size());
sql::record prototype;
while (reader.fetch()) {
char *end = nullptr;
+14 -6
View File
@@ -1,11 +1,19 @@
#include "sqlite_dialect.hpp"
[[maybe_unused]] const matador::sql::dialect* get_dialect() {
#include "matador/sql/dialect_builder.hpp"
[[maybe_unused]] const matador::sql::dialect *get_dialect()
{
using namespace matador::sql;
const static dialect d{{
{ dialect::token_t::BEGIN, "BEGIN TRANSACTION"},
{ dialect::token_t::COMMIT, "COMMIT TRANSACTION"},
{ dialect::token_t::ROLLBACK, "ROLLBACK TRANSACTION"}
}};
const static dialect d = dialect_builder::builder()
.create()
.with_token_replace_map({
{dialect::token_t::BEGIN, "BEGIN TRANSACTION"},
{dialect::token_t::COMMIT, "COMMIT TRANSACTION"},
{dialect::token_t::ROLLBACK, "ROLLBACK TRANSACTION"}
})
.with_default_schema_name("main")
.build();
return &d;
}
+2 -2
View File
@@ -9,7 +9,7 @@ namespace matador::backends::sqlite {
void throw_sqlite_error(int ec, sqlite3 *db, const std::string &source)
{
if (ec != SQLITE_OK) {
if (ec != SQLITE_OK && ec != SQLITE_DONE) {
std::stringstream msg;
msg << "sqlite error (" << source << "): " << sqlite3_errmsg(db);
throw std::logic_error(msg.str());
@@ -18,7 +18,7 @@ void throw_sqlite_error(int ec, sqlite3 *db, const std::string &source)
void throw_sqlite_error(int ec, sqlite3 *db, const std::string &source, const std::string &sql)
{
if (ec != SQLITE_OK) {
if (ec != SQLITE_OK&& ec != SQLITE_DONE) {
std::stringstream msg;
msg << "sqlite error (" << source << ", sql: " << sql << "): " << sqlite3_errmsg(db);
throw std::logic_error(msg.str());
@@ -0,0 +1,121 @@
#include "sqlite_parameter_binder.h"
#include "sqlite_error.hpp"
namespace matador::backends::sqlite {
sqlite_parameter_binder::sqlite_parameter_binder(sqlite3 *db, sqlite3_stmt *stmt)
: db_(db)
, stmt_(stmt)
{}
void sqlite_parameter_binder::bind(size_t pos, char i)
{
int ret = sqlite3_bind_int(stmt_, static_cast<int>(++pos), i);
throw_sqlite_error(ret, db_, "sqlite3_bind_int");
}
void sqlite_parameter_binder::bind(size_t pos, short i)
{
int ret = sqlite3_bind_int(stmt_, static_cast<int>(++pos), i);
throw_sqlite_error(ret, db_, "sqlite3_bind_int");
}
void sqlite_parameter_binder::bind(size_t pos, int i)
{
int ret = sqlite3_bind_int(stmt_, static_cast<int>(++pos), i);
throw_sqlite_error(ret, db_, "sqlite3_bind_int");
}
void sqlite_parameter_binder::bind(size_t pos, long i)
{
int ret = sqlite3_bind_int(stmt_, static_cast<int>(++pos), i);
throw_sqlite_error(ret, db_, "sqlite3_bind_int");
}
void sqlite_parameter_binder::bind(size_t pos, long long int i)
{
int ret = sqlite3_bind_int64(stmt_, static_cast<int>(++pos), i);
throw_sqlite_error(ret, db_, "sqlite3_bind_int");
}
void sqlite_parameter_binder::bind(size_t pos, unsigned char i)
{
int ret = sqlite3_bind_int(stmt_, static_cast<int>(++pos), i);
throw_sqlite_error(ret, db_, "sqlite3_bind_int");
}
void sqlite_parameter_binder::bind(size_t pos, unsigned short i)
{
int ret = sqlite3_bind_int(stmt_, static_cast<int>(++pos), i);
throw_sqlite_error(ret, db_, "sqlite3_bind_int");
}
void sqlite_parameter_binder::bind(size_t pos, unsigned int i)
{
int ret = sqlite3_bind_int64(stmt_, static_cast<int>(++pos), i);
throw_sqlite_error(ret, db_, "sqlite3_bind_int");
}
void sqlite_parameter_binder::bind(size_t pos, unsigned long i)
{
int ret = sqlite3_bind_int64(stmt_, static_cast<int>(++pos), i);
throw_sqlite_error(ret, db_, "sqlite3_bind_int");
}
void sqlite_parameter_binder::bind(size_t pos, unsigned long long int i)
{
int ret = sqlite3_bind_int64(stmt_, static_cast<int>(++pos), i);
throw_sqlite_error(ret, db_, "sqlite3_bind_int");
}
void sqlite_parameter_binder::bind(size_t pos, bool b)
{
int ret = sqlite3_bind_int(stmt_, static_cast<int>(++pos), b);
throw_sqlite_error(ret, db_, "sqlite3_bind_int");
}
void sqlite_parameter_binder::bind(size_t pos, float d)
{
int ret = sqlite3_bind_double(stmt_, static_cast<int>(++pos), d);
throw_sqlite_error(ret, db_, "sqlite3_bind_int");
}
void sqlite_parameter_binder::bind(size_t pos, double d)
{
int ret = sqlite3_bind_double(stmt_, static_cast<int>(++pos), d);
throw_sqlite_error(ret, db_, "sqlite3_bind_int");
}
void sqlite_parameter_binder::bind(size_t pos, const char *str)
{
int ret = sqlite3_bind_text(stmt_, static_cast<int>(++pos), str, static_cast<int>(strlen(str)), nullptr);
throw_sqlite_error(ret, db_, "sqlite3_bind_text");
}
void sqlite_parameter_binder::bind(size_t pos, const char *x, size_t size)
{
auto len = strlen(x);
size = (len > size) ? size : len;
int ret = sqlite3_bind_text(stmt_, static_cast<int>(++pos), x, static_cast<int>(size), nullptr);
throw_sqlite_error(ret, db_, "sqlite3_bind_text");
}
void sqlite_parameter_binder::bind(size_t pos, const std::string &str)
{
int ret = sqlite3_bind_text(stmt_, static_cast<int>(++pos), str.c_str(), static_cast<int>(str.size()), nullptr);
throw_sqlite_error(ret, db_, "sqlite3_bind_text");
}
void sqlite_parameter_binder::bind(size_t pos, const std::string &x, size_t size)
{
auto len = x.size();
if (size == 0) {
size = len;
} else {
size = (len > size) ? size : len;
}
int ret = sqlite3_bind_text(stmt_, static_cast<int>(++pos), x.data(), static_cast<int>(size), nullptr);
throw_sqlite_error(ret, db_, "sqlite3_bind_text");
}
}
@@ -0,0 +1,114 @@
#include "sqlite_prepared_result_reader.hpp"
#include "sqlite_error.hpp"
namespace matador::backends::sqlite {
sqlite_prepared_result_reader::sqlite_prepared_result_reader(sqlite3 *db, sqlite3_stmt *stmt)
: db_(db)
, stmt_(stmt)
{}
size_t sqlite_prepared_result_reader::column_count() const
{
return sqlite3_column_count(stmt_);
}
const char *sqlite_prepared_result_reader::column(size_t index) const
{
return reinterpret_cast<const char*>(sqlite3_column_text(stmt_, static_cast<int>(index)));
}
bool sqlite_prepared_result_reader::fetch()
{
int ret = sqlite3_step(stmt_);
if (ret != SQLITE_ROW) {
throw_sqlite_error(ret, db_, "sqlite3_step");
}
return ret != SQLITE_DONE;
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, char &value)
{
value = static_cast<char>(sqlite3_column_int(stmt_, static_cast<int>(index)));
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, short &value)
{
query_result_reader::read_value(id, index, value);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, int &value)
{
query_result_reader::read_value(id, index, value);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, long &value)
{
query_result_reader::read_value(id, index, value);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, long long int &value)
{
query_result_reader::read_value(id, index, value);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, unsigned char &value)
{
query_result_reader::read_value(id, index, value);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, unsigned short &value)
{
query_result_reader::read_value(id, index, value);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, unsigned int &value)
{
query_result_reader::read_value(id, index, value);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, unsigned long &value)
{
query_result_reader::read_value(id, index, value);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, unsigned long long int &value)
{
query_result_reader::read_value(id, index, value);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, bool &value)
{
query_result_reader::read_value(id, index, value);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, float &value)
{
query_result_reader::read_value(id, index, value);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, double &value)
{
query_result_reader::read_value(id, index, value);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, char *value, size_t s)
{
query_result_reader::read_value(id, index, value, s);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, std::string &value)
{
query_result_reader::read_value(id, index, value);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, std::string &value, size_t s)
{
query_result_reader::read_value(id, index, value, s);
}
void sqlite_prepared_result_reader::read_value(const char *id, size_t index, sql::any_type &value, sql::data_type_t type, size_t size)
{
query_result_reader::read_value(id, index, value, type, size);
}
}
+52
View File
@@ -0,0 +1,52 @@
#include "sqlite_statement.hpp"
#include "sqlite_prepared_result_reader.hpp"
#include "sqlite_error.hpp"
namespace matador::backends::sqlite {
sqlite_statement::sqlite_statement(sqlite3 *db, sqlite3_stmt *stmt, const sql::query_context &query)
: statement_impl(query)
, db_(db)
, stmt_(stmt)
, binder_(db, stmt)
{}
sqlite_statement::~sqlite_statement()
{
sqlite3_finalize(stmt_);
}
size_t sqlite_statement::execute()
{
// get next row
int ret = sqlite3_reset(stmt_);
throw_sqlite_error(ret, db_, "sqlite3_reset");
if (ret = sqlite3_step(stmt_); ret != SQLITE_DONE) {
throw_sqlite_error(ret, db_, "sqlite3_step");
}
return sqlite3_changes(db_);
}
std::unique_ptr<sql::query_result_impl> sqlite_statement::fetch()
{
int ret = sqlite3_reset(stmt_);
throw_sqlite_error(ret, db_, "sqlite3_reset");
auto reader = std::make_unique<sqlite_prepared_result_reader>(db_, stmt_);
return std::move(std::make_unique<sql::query_result_impl>(std::move(reader), query_.prototype));
}
void sqlite_statement::reset()
{
if (stmt_) {
sqlite3_reset(stmt_);
sqlite3_clear_bindings(stmt_);
}
}
sql::parameter_binder& sqlite_statement::binder()
{
return binder_;
}
}