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_;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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_;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user