implemented prepared statement for postgres and sqlite
This commit is contained in:
parent
8ba70cc79e
commit
da423ea8bb
|
|
@ -1,6 +1,6 @@
|
|||
# query
|
||||
# query_context
|
||||
|
||||
A fluent sql query builder
|
||||
A fluent sql query_context builder
|
||||
|
||||
Object definition
|
||||
```cpp
|
||||
|
|
|
|||
|
|
@ -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_;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef QUERY_ANY_TYPE_HPP
|
||||
#define QUERY_ANY_TYPE_HPP
|
||||
|
||||
#include "matador/sql/placeholder.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
|
|
@ -13,6 +15,7 @@ using any_type = std::variant<
|
|||
bool,
|
||||
const char*,
|
||||
std::string,
|
||||
placeholder,
|
||||
nullptr_t>;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
namespace matador::sql {
|
||||
|
||||
struct placeholder;
|
||||
|
||||
template < typename DestType, typename SourceType >
|
||||
void convert(DestType &dest, SourceType source, typename std::enable_if<std::is_same<DestType, SourceType>::value>::type* = nullptr)
|
||||
{
|
||||
|
|
@ -122,6 +124,7 @@ struct any_type_to_visitor
|
|||
void operator()(double &x) { convert(result, x); }
|
||||
void operator()(const char *x) { convert(result, x); }
|
||||
void operator()(std::string &x) { convert(result, x); }
|
||||
void operator()(placeholder &/*x*/) {}
|
||||
|
||||
Type result{};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
namespace matador::sql {
|
||||
|
||||
class dialect;
|
||||
class query_context;
|
||||
|
||||
class basic_condition
|
||||
{
|
||||
|
|
@ -32,7 +33,7 @@ public:
|
|||
LIKE
|
||||
};
|
||||
|
||||
virtual std::string evaluate(dialect &dialect) const = 0;
|
||||
virtual std::string evaluate(dialect &dialect, query_context &query) const = 0;
|
||||
|
||||
static std::unordered_map<operand_t, std::string> operands;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,15 +3,13 @@
|
|||
|
||||
#include "matador/sql/basic_condition.hpp"
|
||||
#include "matador/sql/dialect.hpp"
|
||||
#include "matador/sql/placeholder.hpp"
|
||||
#include "matador/sql/query_context.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace matador::sql {
|
||||
|
||||
struct placeholder {};
|
||||
|
||||
const placeholder _;
|
||||
|
||||
/**
|
||||
* @class condition
|
||||
* @brief Represents a sql query condition
|
||||
|
|
@ -27,8 +25,6 @@ const placeholder _;
|
|||
|
||||
/// @cond MATADOR_DEV
|
||||
|
||||
class query;
|
||||
|
||||
template<class L, class R, class Enabled = void>
|
||||
class condition;
|
||||
|
||||
|
|
@ -40,7 +36,7 @@ public:
|
|||
|
||||
placeholder value;
|
||||
|
||||
std::string evaluate(dialect &d) const override;
|
||||
std::string evaluate(dialect &d, query_context &query) const override;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
|
|
@ -57,9 +53,9 @@ public:
|
|||
|
||||
T value;
|
||||
|
||||
std::string evaluate(dialect &d) const override
|
||||
std::string evaluate(dialect &d, query_context &query) const override
|
||||
{
|
||||
d.add_host_var(field_.name());
|
||||
query.bind_vars.emplace_back(field_.name());
|
||||
return d.prepare_identifier(field_.name()) + " " + operand + " " + std::to_string(value);
|
||||
}
|
||||
};
|
||||
|
|
@ -77,9 +73,9 @@ public:
|
|||
|
||||
T value;
|
||||
|
||||
std::string evaluate(dialect &d) const override
|
||||
std::string evaluate(dialect &d, query_context &query) const override
|
||||
{
|
||||
d.add_host_var(field_.name());
|
||||
query.bind_vars.emplace_back(field_.name());
|
||||
return d.prepare_identifier(field_.name()) + " " + operand + " '" + value + "'";
|
||||
}
|
||||
};
|
||||
|
|
@ -98,7 +94,7 @@ public:
|
|||
|
||||
T value;
|
||||
|
||||
std::string evaluate(dialect &d) const override
|
||||
std::string evaluate(dialect &d, query_context &query) const override
|
||||
{
|
||||
return std::to_string(value) + " " + operand + " " + d.prepare_identifier(field_.name());
|
||||
}
|
||||
|
|
@ -117,7 +113,7 @@ public:
|
|||
|
||||
T value;
|
||||
|
||||
std::string evaluate(dialect &d) const override
|
||||
std::string evaluate(dialect &d, query_context &query) const override
|
||||
{
|
||||
return "'" + std::to_string(value) + "' " + operand + " " + d.prepare_identifier(field_.name());
|
||||
}
|
||||
|
|
@ -159,13 +155,12 @@ public:
|
|||
* @param d The d used to evaluate
|
||||
* @return A condition IN part of the query
|
||||
*/
|
||||
std::string evaluate(dialect &d) const override {
|
||||
std::string evaluate(dialect &d, query_context &query) const override {
|
||||
auto count = size();
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
d.add_host_var(field_.name());
|
||||
query.bind_vars.emplace_back(field_.name());
|
||||
}
|
||||
|
||||
|
||||
std::string result = d.prepare_identifier(field_.name()) + " IN (";
|
||||
if (args_.size() < 2) {
|
||||
for (const auto &val : args_) {
|
||||
|
|
@ -205,7 +200,7 @@ private:
|
|||
* @endcode
|
||||
*/
|
||||
template <>
|
||||
class condition<column, query> : public basic_column_condition
|
||||
class condition<column, query_context> : public basic_column_condition
|
||||
{
|
||||
public:
|
||||
/**
|
||||
|
|
@ -219,7 +214,7 @@ public:
|
|||
* @param op Operand of the condition
|
||||
* @param q The query to be evaluated to the IN arguments
|
||||
*/
|
||||
condition(column col, basic_condition::operand_t op, query &q);
|
||||
condition(column col, basic_condition::operand_t op, query_context &q);
|
||||
|
||||
/**
|
||||
* @brief Evaluates the condition
|
||||
|
|
@ -230,10 +225,10 @@ public:
|
|||
* @param d The d used to evaluate
|
||||
* @return A condition IN part of the query
|
||||
*/
|
||||
std::string evaluate(dialect &d) const override;
|
||||
std::string evaluate(dialect &d, query_context &query) const override;
|
||||
|
||||
private:
|
||||
query &query_;
|
||||
query_context &query_;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -265,9 +260,9 @@ public:
|
|||
* @param d The d used to evaluate
|
||||
* @return A condition BETWEEN part of the query
|
||||
*/
|
||||
std::string evaluate(dialect &d) const override {
|
||||
d.add_host_var(field_.name());
|
||||
d.add_host_var(field_.name());
|
||||
std::string evaluate(dialect &d, query_context &query) const override {
|
||||
query.bind_vars.emplace_back(field_.name());
|
||||
query.bind_vars.emplace_back(field_.name());
|
||||
return d.prepare_identifier(field_.name()) + " BETWEEN " + std::to_string(range_.first) + " AND " + std::to_string(range_.second);
|
||||
}
|
||||
|
||||
|
|
@ -307,11 +302,11 @@ public:
|
|||
* @param d The d used to evaluate
|
||||
* @return The evaluated string based on the compile type
|
||||
*/
|
||||
std::string evaluate(dialect &d) const override
|
||||
std::string evaluate(dialect &d, query_context &query) const override
|
||||
{
|
||||
// ensure the numbering order for host vars
|
||||
auto cl = left.evaluate(d);
|
||||
auto cr = right.evaluate(d);
|
||||
auto cl = left.evaluate(d, query);
|
||||
auto cr = right.evaluate(d, query);
|
||||
if (operand == basic_condition::operand_t::AND) {
|
||||
return "(" + cl + " " + basic_condition::operands[operand] + " " + cr + ")";
|
||||
} else {
|
||||
|
|
@ -350,7 +345,7 @@ public:
|
|||
* @param d The d used to evaluate
|
||||
* @return The evaluated string based on the compile type
|
||||
*/
|
||||
std::string evaluate(dialect &d) const override
|
||||
std::string evaluate(dialect &d, query_context &query) const override
|
||||
{
|
||||
return operand + " (" + cond.evaluate(d) + ")";
|
||||
}
|
||||
|
|
@ -397,7 +392,7 @@ condition<column, std::initializer_list<V>> in(const column &col, std::initializ
|
|||
* @param q The query to be executes as sub select
|
||||
* @return The condition object
|
||||
*/
|
||||
condition<column, query> in(const column &col, query &&q);
|
||||
condition<column, query_context> in(const column &col, query_context &&q);
|
||||
|
||||
/**
|
||||
* @brief Creates a between condition.
|
||||
|
|
@ -456,7 +451,7 @@ condition<column, T> operator==(const column &col, T val)
|
|||
* @param q The query to compare with
|
||||
* @return The condition object representing the equality operation
|
||||
*/
|
||||
condition<column, query> equals(const column &col, query &q);
|
||||
condition<column, query_context> equals(const column &col, query_context &q);
|
||||
|
||||
/**
|
||||
* @brief Condition inequality operator for a column and a value
|
||||
|
|
|
|||
|
|
@ -4,8 +4,10 @@
|
|||
#include "matador/sql/connection_info.hpp"
|
||||
#include "matador/sql/connection_impl.hpp"
|
||||
#include "matador/sql/dialect.hpp"
|
||||
#include "matador/sql/query_context.hpp"
|
||||
#include "matador/sql/query_result.hpp"
|
||||
#include "matador/sql/record.hpp"
|
||||
#include "matador/sql/statement.hpp"
|
||||
|
||||
#include "matador/utils/logger.hpp"
|
||||
|
||||
|
|
@ -13,8 +15,6 @@
|
|||
|
||||
namespace matador::sql {
|
||||
|
||||
class connection_impl;
|
||||
|
||||
class connection
|
||||
{
|
||||
public:
|
||||
|
|
@ -37,6 +37,8 @@ public:
|
|||
[[nodiscard]] std::unique_ptr<query_result_impl> fetch(const std::string &sql) const;
|
||||
[[nodiscard]] std::pair<size_t, std::string> execute(const std::string &sql) const;
|
||||
|
||||
statement prepare(query_context &&query) const;
|
||||
|
||||
private:
|
||||
connection_info connection_info_;
|
||||
std::unique_ptr<connection_impl> connection_;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@
|
|||
|
||||
#include "matador/sql/connection_info.hpp"
|
||||
#include "matador/sql/query_result_impl.hpp"
|
||||
#include "matador/sql/query_context.hpp"
|
||||
#include "matador/sql/record.hpp"
|
||||
#include "matador/sql/statement_impl.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
|
@ -22,7 +24,7 @@ public:
|
|||
|
||||
virtual size_t execute(const std::string &stmt) = 0;
|
||||
virtual std::unique_ptr<query_result_impl> fetch(const std::string &stmt) = 0;
|
||||
virtual void prepare(const std::string &stmt) = 0;
|
||||
virtual std::unique_ptr<statement_impl> prepare(query_context context) = 0;
|
||||
|
||||
virtual record describe(const std::string &table) = 0;
|
||||
virtual bool exists(const std::string &table_name) = 0;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "matador/sql/types.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
|
@ -12,7 +13,7 @@ namespace matador::sql {
|
|||
|
||||
class column;
|
||||
|
||||
class dialect
|
||||
class dialect final
|
||||
{
|
||||
public:
|
||||
enum class token_t : uint8_t
|
||||
|
|
@ -65,14 +66,11 @@ public:
|
|||
using data_type_to_string_map = std::unordered_map<data_type_t, std::string>;
|
||||
using sql_func_to_string_map = std::unordered_map<sql_function_t, std::string>;
|
||||
|
||||
public:
|
||||
dialect() = default;
|
||||
dialect(const token_to_string_map &token_replace_map, const data_type_to_string_map &data_type_replace_map);
|
||||
explicit dialect(const data_type_to_string_map &data_type_replace_map);
|
||||
explicit dialect(const token_to_string_map &token_replace_map);
|
||||
using next_placeholder_func = std::function<std::string(size_t)>;
|
||||
|
||||
const std::string& token_at(token_t token) const;
|
||||
const std::string& data_type_at(data_type_t type) const;
|
||||
public:
|
||||
[[nodiscard]] const std::string& token_at(token_t token) const;
|
||||
[[nodiscard]] const std::string& data_type_at(data_type_t type) const;
|
||||
|
||||
/**
|
||||
* Prepare sql dialect identifier for execution
|
||||
|
|
@ -82,14 +80,14 @@ public:
|
|||
* @param str The identifier string to be prepared
|
||||
* @return The prepared string
|
||||
*/
|
||||
std::string prepare_identifier(const column &col) const;
|
||||
[[nodiscard]] std::string prepare_identifier(const column &col) const;
|
||||
|
||||
/**
|
||||
* Prepare string literal
|
||||
*
|
||||
* @param str String literal to be prepared
|
||||
*/
|
||||
std::string prepare_literal(const std::string &str) const;
|
||||
[[nodiscard]] std::string prepare_literal(const std::string &str) const;
|
||||
|
||||
/**
|
||||
* Wrap identifier quotes around a sql identifier keyword
|
||||
|
|
@ -118,7 +116,7 @@ public:
|
|||
*
|
||||
* @return How the identifier quotes should be escaped
|
||||
*/
|
||||
virtual escape_identifier_t identifier_escape_type() const;
|
||||
[[nodiscard]] virtual escape_identifier_t identifier_escape_type() const;
|
||||
|
||||
/**
|
||||
* Generates a next placeholder string. default is
|
||||
|
|
@ -126,17 +124,24 @@ public:
|
|||
*
|
||||
* @return Placeholder string
|
||||
*/
|
||||
virtual std::string next_placeholder() const;
|
||||
[[nodiscard]] std::string next_placeholder(const std::vector<std::string> &bind_vars) const;
|
||||
|
||||
void add_host_var(const std::string &host_var);
|
||||
void add_column(const std::string &column);
|
||||
|
||||
const std::vector<std::string>& host_vars() const;
|
||||
const std::vector<std::string>& columns() const;
|
||||
/**
|
||||
* Returns the default schema name.
|
||||
*
|
||||
* @return The default schema name.
|
||||
*/
|
||||
[[nodiscard]] std::string default_schema_name() const;
|
||||
|
||||
private:
|
||||
std::vector<std::string> host_vars_;
|
||||
std::vector<std::string> columns_;
|
||||
dialect() = default;
|
||||
|
||||
private:
|
||||
friend class dialect_builder;
|
||||
|
||||
next_placeholder_func placeholder_func_ = [](size_t) { return "?"; };
|
||||
|
||||
std::string default_schema_name_;
|
||||
|
||||
token_to_string_map tokens_ {
|
||||
{token_t::CREATE, "CREATE"},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
#ifndef QUERY_DIALECT_BUILDER_HPP
|
||||
#define QUERY_DIALECT_BUILDER_HPP
|
||||
|
||||
#include "matador/sql/dialect.hpp"
|
||||
|
||||
namespace matador::sql {
|
||||
|
||||
class dialect_builder
|
||||
{
|
||||
public:
|
||||
static dialect_builder& builder();
|
||||
|
||||
dialect_builder& create();
|
||||
|
||||
dialect_builder& with_token_replace_map(const dialect::token_to_string_map &token_replace_map);
|
||||
dialect_builder& with_data_type_replace_map(const dialect::data_type_to_string_map &data_type_replace_map);
|
||||
dialect_builder& with_placeholder_func(const dialect::next_placeholder_func &func);
|
||||
dialect_builder& with_default_schema_name(const std::string &schema_name);
|
||||
|
||||
dialect build();
|
||||
|
||||
private:
|
||||
dialect_builder() = default;
|
||||
|
||||
private:
|
||||
dialect dialect_;
|
||||
};
|
||||
|
||||
}
|
||||
#endif //QUERY_DIALECT_BUILDER_HPP
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
#ifndef QUERY_OBJECT_BINDER_HPP
|
||||
#define QUERY_OBJECT_BINDER_HPP
|
||||
|
||||
#include "matador/utils/cascade_type.hpp"
|
||||
#include "matador/utils/field_attributes.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace matador::sql {
|
||||
|
||||
class parameter_binder;
|
||||
|
||||
class object_binder
|
||||
{
|
||||
public:
|
||||
explicit object_binder(parameter_binder &binder);
|
||||
|
||||
void reset();
|
||||
|
||||
template < class V >
|
||||
void on_primary_key(const char *id, V &val, typename std::enable_if<std::is_integral<V>::value && !std::is_same<bool, V>::value>::type* = 0)
|
||||
{
|
||||
binder_.bind(index_++, val);
|
||||
}
|
||||
void on_primary_key(const char *id, std::string &, size_t);
|
||||
void on_revision(const char *id, unsigned long long &/*rev*/);
|
||||
|
||||
template<typename Type>
|
||||
void on_attribute(const char *id, Type &val, const utils::field_attributes &/*attr*/ = utils::null_attributes)
|
||||
{
|
||||
binder_.bind(index_++, val);
|
||||
}
|
||||
|
||||
template<class Type, template < class ... > class Pointer>
|
||||
void on_belongs_to(const char *id, Pointer<Type> &x, utils::cascade_type)
|
||||
{
|
||||
// binder_.bind(index_++, val);
|
||||
}
|
||||
template<class Type, template < class ... > class Pointer>
|
||||
void on_has_one(const char *id, Pointer<Type> &x, utils::cascade_type)
|
||||
{
|
||||
// binder_.bind(index_++, val);
|
||||
}
|
||||
template<class ContainerType>
|
||||
void on_has_many(const char *, ContainerType &, const char *, const char *, utils::cascade_type) {}
|
||||
template<class ContainerType>
|
||||
void on_has_many(const char *, ContainerType &, utils::cascade_type) {}
|
||||
|
||||
private:
|
||||
parameter_binder &binder_;
|
||||
size_t index_{0};
|
||||
};
|
||||
|
||||
}
|
||||
#endif //QUERY_OBJECT_BINDER_HPP
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
#ifndef QUERY_PARAMETER_BINDER_HPP
|
||||
#define QUERY_PARAMETER_BINDER_HPP
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
|
||||
namespace matador::sql {
|
||||
|
||||
class parameter_binder
|
||||
{
|
||||
public:
|
||||
virtual ~parameter_binder() = default;
|
||||
|
||||
virtual void bind(size_t pos, char) = 0;
|
||||
virtual void bind(size_t pos, short) = 0;
|
||||
virtual void bind(size_t pos, int) = 0;
|
||||
virtual void bind(size_t pos, long) = 0;
|
||||
virtual void bind(size_t pos, long long) = 0;
|
||||
virtual void bind(size_t pos, unsigned char) = 0;
|
||||
virtual void bind(size_t pos, unsigned short) = 0;
|
||||
virtual void bind(size_t pos, unsigned int) = 0;
|
||||
virtual void bind(size_t pos, unsigned long) = 0;
|
||||
virtual void bind(size_t pos, unsigned long long) = 0;
|
||||
virtual void bind(size_t pos, bool) = 0;
|
||||
virtual void bind(size_t pos, float) = 0;
|
||||
virtual void bind(size_t pos, double) = 0;
|
||||
virtual void bind(size_t pos, const char *) = 0;
|
||||
virtual void bind(size_t pos, const char *, size_t size) = 0;
|
||||
virtual void bind(size_t pos, const std::string&) = 0;
|
||||
virtual void bind(size_t pos, const std::string &x, size_t size) = 0;
|
||||
// virtual void bind(size_t pos, const matador::time&) = 0;
|
||||
// virtual void bind(size_t pos, const matador::date&) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //QUERY_PARAMETER_BINDER_HPP
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef QUERY_PLACEHOLDER_HPP
|
||||
#define QUERY_PLACEHOLDER_HPP
|
||||
|
||||
namespace matador::sql {
|
||||
|
||||
struct placeholder {};
|
||||
|
||||
inline constexpr bool operator==(const placeholder&, const placeholder&) { return true; }
|
||||
|
||||
const placeholder _;
|
||||
|
||||
}
|
||||
|
||||
#endif //QUERY_PLACEHOLDER_HPP
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
#ifndef QUERY_PLACEHOLDER_GENERATOR_HPP
|
||||
#define QUERY_PLACEHOLDER_GENERATOR_HPP
|
||||
|
||||
#include "matador/sql/any_type.hpp"
|
||||
|
||||
#include "matador/utils/cascade_type.hpp"
|
||||
#include "matador/utils/field_attributes.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace matador::sql {
|
||||
|
||||
class placeholder_generator
|
||||
{
|
||||
public:
|
||||
template < class V >
|
||||
void on_primary_key(const char *id, V &val, typename std::enable_if<std::is_integral<V>::value && !std::is_same<bool, V>::value>::type* = 0)
|
||||
{
|
||||
placeholder_values.emplace_back(_);
|
||||
}
|
||||
void on_primary_key(const char *id, std::string &, size_t);
|
||||
void on_revision(const char *id, unsigned long long &/*rev*/);
|
||||
|
||||
template<typename Type>
|
||||
void on_attribute(const char *id, Type &val, const utils::field_attributes &/*attr*/ = utils::null_attributes)
|
||||
{
|
||||
placeholder_values.emplace_back(_);
|
||||
}
|
||||
|
||||
template<class Type, template < class ... > class Pointer>
|
||||
void on_belongs_to(const char *id, Pointer<Type> &x, utils::cascade_type)
|
||||
{
|
||||
placeholder_values.emplace_back(_);
|
||||
}
|
||||
template<class Type, template < class ... > class Pointer>
|
||||
void on_has_one(const char *id, Pointer<Type> &x, utils::cascade_type)
|
||||
{
|
||||
placeholder_values.emplace_back(_);
|
||||
}
|
||||
template<class ContainerType>
|
||||
void on_has_many(const char *, ContainerType &, const char *, const char *, utils::cascade_type) {}
|
||||
template<class ContainerType>
|
||||
void on_has_many(const char *, ContainerType &, utils::cascade_type) {}
|
||||
|
||||
std::vector<any_type> placeholder_values;
|
||||
};
|
||||
|
||||
}
|
||||
#endif //QUERY_PLACEHOLDER_GENERATOR_HPP
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
#include "matador/sql/basic_condition.hpp"
|
||||
#include "matador/sql/column.hpp"
|
||||
#include "matador/sql/key_value_pair.hpp"
|
||||
#include "matador/sql/query_context.hpp"
|
||||
#include "matador/sql/record.hpp"
|
||||
|
||||
#include <string>
|
||||
|
|
@ -18,7 +19,7 @@ class dialect;
|
|||
namespace detail {
|
||||
struct any_type_to_string_visitor
|
||||
{
|
||||
explicit any_type_to_string_visitor(const dialect &d) : d(d) {}
|
||||
explicit any_type_to_string_visitor(const dialect &d, query_context &query);
|
||||
|
||||
void operator()(char &x) { to_string(x); }
|
||||
void operator()(short &x) { to_string(x); }
|
||||
|
|
@ -35,6 +36,7 @@ struct any_type_to_string_visitor
|
|||
void operator()(double &x) { to_string(x); }
|
||||
void operator()(const char *x) { to_string(x); }
|
||||
void operator()(std::string &x) { to_string(x); }
|
||||
void operator()(placeholder &x) { to_string(x); }
|
||||
|
||||
template<typename Type>
|
||||
void to_string(Type &val)
|
||||
|
|
@ -43,8 +45,10 @@ struct any_type_to_string_visitor
|
|||
}
|
||||
void to_string(const char *val);
|
||||
void to_string(std::string &val);
|
||||
void to_string(placeholder &val);
|
||||
|
||||
const dialect &d;
|
||||
query_context &query;
|
||||
std::string result;
|
||||
};
|
||||
|
||||
|
|
@ -59,14 +63,6 @@ enum class join_type_t {
|
|||
INNER, OUTER, LEFT, RIGHT
|
||||
};
|
||||
|
||||
struct query
|
||||
{
|
||||
std::string sql;
|
||||
std::string table_name;
|
||||
record prototype;
|
||||
std::vector<any_type> host_vars;
|
||||
};
|
||||
|
||||
class query_builder
|
||||
{
|
||||
private:
|
||||
|
|
@ -134,7 +130,7 @@ public:
|
|||
query_builder& offset(size_t count);
|
||||
query_builder& limit(size_t count);
|
||||
|
||||
query compile();
|
||||
query_context compile();
|
||||
|
||||
private:
|
||||
void transition_to(state_t next);
|
||||
|
|
@ -150,7 +146,7 @@ private:
|
|||
|
||||
detail::any_type_to_string_visitor value_to_string_;
|
||||
|
||||
query query_;
|
||||
query_context query_;
|
||||
|
||||
using query_state_set = std::unordered_set<state_t>;
|
||||
using query_state_transition_map = std::unordered_map<state_t, query_state_set>;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef QUERY_QUERY_CONTEXT_HPP
|
||||
#define QUERY_QUERY_CONTEXT_HPP
|
||||
|
||||
#include "matador/sql/record.hpp"
|
||||
|
||||
namespace matador::sql {
|
||||
|
||||
struct query_context
|
||||
{
|
||||
std::string sql;
|
||||
std::string command_name;
|
||||
std::string table_name;
|
||||
record prototype;
|
||||
std::vector<any_type> host_vars;
|
||||
std::vector<std::string> bind_vars;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //QUERY_QUERY_CONTEXT_HPP
|
||||
|
|
@ -6,9 +6,11 @@
|
|||
#include "matador/sql/column_name_generator.hpp"
|
||||
#include "matador/sql/key_value_generator.hpp"
|
||||
#include "matador/sql/key_value_pair.hpp"
|
||||
#include "matador/sql/placeholder_generator.hpp"
|
||||
#include "matador/sql/query_builder.hpp"
|
||||
#include "matador/sql/query_result.hpp"
|
||||
#include "matador/sql/record.hpp"
|
||||
#include "matador/sql/statement.hpp"
|
||||
#include "matador/sql/table_repository.hpp"
|
||||
#include "matador/sql/value_extractor.hpp"
|
||||
|
||||
|
|
@ -35,6 +37,7 @@ public:
|
|||
using query_intermediate::query_intermediate;
|
||||
|
||||
std::pair<size_t, std::string> execute();
|
||||
statement prepare();
|
||||
};
|
||||
|
||||
class query_select_finish : public query_intermediate
|
||||
|
|
@ -57,6 +60,8 @@ public:
|
|||
return fetch_one().at(0).as<Type>();
|
||||
}
|
||||
|
||||
statement prepare();
|
||||
|
||||
private:
|
||||
std::unique_ptr<query_result_impl> fetch();
|
||||
};
|
||||
|
|
@ -140,6 +145,15 @@ public:
|
|||
query_from_intermediate from(const std::string &table, const std::string &as = "");
|
||||
};
|
||||
|
||||
template < class Type >
|
||||
std::vector<any_type> as_placeholder(const Type &obj)
|
||||
{
|
||||
placeholder_generator generator;
|
||||
matador::utils::access::process(generator, obj);
|
||||
|
||||
return generator.placeholder_values;
|
||||
}
|
||||
|
||||
class query_into_intermediate : public query_intermediate
|
||||
{
|
||||
public:
|
||||
|
|
@ -147,6 +161,12 @@ public:
|
|||
|
||||
query_execute_finish values(std::initializer_list<any_type> values);
|
||||
template<class Type>
|
||||
query_execute_finish values()
|
||||
{
|
||||
Type obj;
|
||||
return {session_, builder_.values(as_placeholder(obj))};
|
||||
}
|
||||
template<class Type>
|
||||
query_execute_finish values(const Type &obj)
|
||||
{
|
||||
return {session_, builder_.values(value_extractor::extract(obj))};
|
||||
|
|
@ -216,7 +236,7 @@ public:
|
|||
class query_update_intermediate : public query_start_intermediate
|
||||
{
|
||||
public:
|
||||
query_update_intermediate(session &s, std::string table_name);
|
||||
query_update_intermediate(session &s, const std::string& table_name);
|
||||
|
||||
query_set_intermediate set(std::initializer_list<key_value_pair> columns);
|
||||
template<class Type>
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
#ifndef QUERY_RESULT_HPP
|
||||
#define QUERY_RESULT_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace matador::sql {
|
||||
|
||||
struct result
|
||||
{
|
||||
std::string sql;
|
||||
};
|
||||
|
||||
}
|
||||
#endif //QUERY_RESULT_HPP
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
#include "matador/sql/connection_pool.hpp"
|
||||
#include "matador/sql/query_builder.hpp"
|
||||
#include "matador/sql/query_intermediates.hpp"
|
||||
#include "matador/sql/statement.hpp"
|
||||
#include "matador/sql/table_repository.hpp"
|
||||
|
||||
#include <unordered_map>
|
||||
|
|
@ -22,18 +23,16 @@ public:
|
|||
query_create_intermediate create();
|
||||
query_drop_intermediate drop();
|
||||
template < class Type >
|
||||
query_select_intermediate select()
|
||||
{
|
||||
return query_select_intermediate{*this, column_generator::generate<Type>(table_repository_)};
|
||||
}
|
||||
query_select_intermediate select();
|
||||
query_select_intermediate select(std::initializer_list<column> columns);
|
||||
query_insert_intermediate insert();
|
||||
query_update_intermediate update(const std::string &table);
|
||||
query_delete_intermediate remove();
|
||||
|
||||
[[nodiscard]] query_result<record> fetch(const query &q) const;
|
||||
[[nodiscard]] query_result<record> fetch(const query_context &q) const;
|
||||
// [[nodiscard]] query_result<record> fetch(const std::string &sql) const;
|
||||
[[nodiscard]] std::pair<size_t, std::string> execute(const std::string &sql) const;
|
||||
statement prepare(query_context q) const;
|
||||
|
||||
record describe_table(const std::string &table_name) const;
|
||||
bool table_exists(const std::string &table_name) const;
|
||||
|
|
@ -61,5 +60,11 @@ private:
|
|||
mutable std::unordered_map<std::string, record> prototypes_;
|
||||
};
|
||||
|
||||
template<class Type>
|
||||
query_select_intermediate session::select()
|
||||
{
|
||||
return query_select_intermediate{*this, column_generator::generate<Type>(table_repository_)};
|
||||
}
|
||||
|
||||
}
|
||||
#endif //QUERY_SESSION_HPP
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
#ifndef QUERY_STATEMENT_HPP
|
||||
#define QUERY_STATEMENT_HPP
|
||||
|
||||
#include "matador/sql/object_binder.hpp"
|
||||
#include "matador/sql/query_result.hpp"
|
||||
#include "matador/sql/statement_impl.hpp"
|
||||
|
||||
#include "matador/utils/logger.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace matador::sql {
|
||||
|
||||
class statement
|
||||
{
|
||||
public:
|
||||
explicit statement(std::unique_ptr<statement_impl> impl, const utils::logger &logger);
|
||||
|
||||
template < typename Type >
|
||||
statement& bind(size_t pos, const Type &value)
|
||||
{
|
||||
statement_->bind(pos, value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
statement& bind(size_t pos, std::string &val, size_t size);
|
||||
|
||||
template < class Type >
|
||||
statement& bind(const Type &obj)
|
||||
{
|
||||
object_binder_.reset();
|
||||
matador::utils::access::process(object_binder_, obj);
|
||||
return *this;
|
||||
}
|
||||
|
||||
size_t execute();
|
||||
|
||||
template<class Type>
|
||||
query_result<Type> fetch()
|
||||
{
|
||||
logger_.info(statement_->query_.sql);
|
||||
return query_result<Type>(statement_->fetch());
|
||||
}
|
||||
query_result<record> fetch();
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
std::unique_ptr<statement_impl> statement_;
|
||||
const utils::logger &logger_;
|
||||
object_binder object_binder_;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //QUERY_STATEMENT_HPP
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
#ifndef QUERY_STATEMENT_CACHE_HPP
|
||||
#define QUERY_STATEMENT_CACHE_HPP
|
||||
|
||||
#include "matador/sql/statement.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace matador::sql {
|
||||
|
||||
class connection;
|
||||
|
||||
struct cache_info
|
||||
{
|
||||
std::unique_ptr<statement> statement_;
|
||||
size_t connection_id_;
|
||||
};
|
||||
|
||||
class statement_cache
|
||||
{
|
||||
public:
|
||||
statement& acquire(const std::string &stmt, const connection &conn);
|
||||
void release(const statement &stmt);
|
||||
|
||||
private:
|
||||
mutable std::mutex mutex_;
|
||||
size_t max_cache_size_{256};
|
||||
std::hash<std::string> hash_;
|
||||
using statement_map = std::unordered_map<size_t, cache_info>;
|
||||
statement_map statement_map_;
|
||||
};
|
||||
|
||||
}
|
||||
#endif //QUERY_STATEMENT_CACHE_HPP
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
#ifndef QUERY_STATEMENT_IMPL_HPP
|
||||
#define QUERY_STATEMENT_IMPL_HPP
|
||||
|
||||
#include "matador/sql/query_context.hpp"
|
||||
#include "matador/sql/query_result_impl.hpp"
|
||||
#include "matador/sql/parameter_binder.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace matador::sql {
|
||||
|
||||
class statement_impl
|
||||
{
|
||||
protected:
|
||||
explicit statement_impl(query_context query);
|
||||
|
||||
public:
|
||||
virtual ~statement_impl() = default;
|
||||
|
||||
virtual size_t execute() = 0;
|
||||
virtual std::unique_ptr<query_result_impl> fetch() = 0;
|
||||
|
||||
template < class V >
|
||||
void bind(size_t pos, V &val)
|
||||
{
|
||||
binder().bind(pos, val);
|
||||
}
|
||||
|
||||
void bind(size_t pos, std::string &val, size_t size)
|
||||
{
|
||||
binder().bind(pos, val, size);
|
||||
}
|
||||
|
||||
virtual void reset() = 0;
|
||||
|
||||
protected:
|
||||
virtual parameter_binder& binder() = 0;
|
||||
|
||||
protected:
|
||||
friend class statement;
|
||||
|
||||
query_context query_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //QUERY_STATEMENT_IMPL_HPP
|
||||
|
|
@ -19,7 +19,13 @@ set(SQL_SOURCES
|
|||
sql/table_repository.cpp
|
||||
sql/any_type_to_visitor.cpp
|
||||
sql/query_result.cpp
|
||||
sql/query_result_reader.cpp)
|
||||
sql/query_result_reader.cpp
|
||||
sql/statement_cache.cpp
|
||||
sql/statement_impl.cpp
|
||||
sql/dialect_builder.cpp
|
||||
sql/object_binder.cpp
|
||||
sql/placeholder_generator.cpp
|
||||
)
|
||||
|
||||
set(SQL_HEADER
|
||||
../include/matador/sql/dialect.hpp
|
||||
|
|
@ -31,7 +37,6 @@ set(SQL_HEADER
|
|||
../include/matador/sql/condition.hpp
|
||||
../include/matador/sql/connection.hpp
|
||||
../include/matador/sql/query_intermediates.hpp
|
||||
../include/matador/sql/result.hpp
|
||||
../include/matador/sql/record.hpp
|
||||
../include/matador/sql/query_result.hpp
|
||||
../include/matador/sql/connection_impl.hpp
|
||||
|
|
@ -50,7 +55,17 @@ set(SQL_HEADER
|
|||
../include/matador/sql/table_repository.hpp
|
||||
../include/matador/sql/any_type_to_visitor.hpp
|
||||
../include/matador/sql/query_result_reader.hpp
|
||||
../include/matador/sql/to_value.hpp)
|
||||
../include/matador/sql/to_value.hpp
|
||||
../include/matador/sql/statement_cache.hpp
|
||||
../include/matador/sql/statement.hpp
|
||||
../include/matador/sql/statement_impl.hpp
|
||||
../include/matador/sql/query_context.hpp
|
||||
sql/statement.cpp
|
||||
../include/matador/sql/parameter_binder.hpp
|
||||
../include/matador/sql/dialect_builder.hpp
|
||||
../include/matador/sql/object_binder.hpp
|
||||
../include/matador/sql/placeholder_generator.hpp
|
||||
)
|
||||
|
||||
set(UTILS_HEADER
|
||||
../include/matador/utils/field_attributes.hpp
|
||||
|
|
|
|||
|
|
@ -3,21 +3,21 @@
|
|||
namespace matador::sql {
|
||||
|
||||
condition<column, placeholder, std::enable_if<true>::type>::condition(const column &fld, basic_condition::operand_t op, const placeholder &val)
|
||||
: basic_column_condition(fld, op)
|
||||
, value(val)
|
||||
{ }
|
||||
|
||||
std::string condition<column, placeholder, std::enable_if<true>::type>::evaluate(dialect &d) const {
|
||||
d.add_host_var(field_.name());
|
||||
return d.prepare_identifier(field_.name()) + " " + operand + " " + d.next_placeholder();
|
||||
}
|
||||
|
||||
condition<column, query>::condition(column col, basic_condition::operand_t op, query &q)
|
||||
: basic_column_condition(std::move(col), op)
|
||||
, query_(q)
|
||||
: basic_column_condition(fld, op), value(val)
|
||||
{}
|
||||
|
||||
std::string condition<column, query>::evaluate(dialect &d) const {
|
||||
std::string condition<column, placeholder, std::enable_if<true>::type>::evaluate(dialect &d, query_context &query) const
|
||||
{
|
||||
query.bind_vars.emplace_back(field_.name());
|
||||
return d.prepare_identifier(field_.name()) + " " + operand + " " + d.next_placeholder(query.bind_vars);
|
||||
}
|
||||
|
||||
condition<column, query_context>::condition(column col, basic_condition::operand_t op, query_context &q)
|
||||
: basic_column_condition(std::move(col), op), query_(q)
|
||||
{}
|
||||
|
||||
std::string condition<column, query_context>::evaluate(dialect &d, query_context &query) const
|
||||
{
|
||||
std::string result(d.prepare_identifier(field_.name()) + " " + operand + " (");
|
||||
result += (")");
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -88,4 +88,9 @@ std::unique_ptr<query_result_impl> connection::fetch(const std::string &sql) con
|
|||
return connection_->fetch(sql);
|
||||
}
|
||||
|
||||
statement connection::prepare(query_context &&query) const
|
||||
{
|
||||
return statement(connection_->prepare(std::move(query)), logger_);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,25 +5,6 @@
|
|||
|
||||
namespace matador::sql {
|
||||
|
||||
dialect::dialect(const dialect::token_to_string_map &token_replace_map, const dialect::data_type_to_string_map &data_type_replace_map)
|
||||
{
|
||||
for (const auto &token : token_replace_map) {
|
||||
tokens_[token.first] = token.second;
|
||||
}
|
||||
|
||||
for (const auto &data_type : data_type_replace_map) {
|
||||
data_types_[data_type.first] = data_type.second;
|
||||
}
|
||||
}
|
||||
|
||||
dialect::dialect(const dialect::data_type_to_string_map &data_type_replace_map)
|
||||
: dialect({}, data_type_replace_map)
|
||||
{}
|
||||
|
||||
dialect::dialect(const dialect::token_to_string_map &token_replace_map)
|
||||
: dialect(token_replace_map, {})
|
||||
{}
|
||||
|
||||
const std::string& dialect::token_at(dialect::token_t token) const
|
||||
{
|
||||
return tokens_.at(token);
|
||||
|
|
@ -85,29 +66,14 @@ dialect::escape_identifier_t dialect::identifier_escape_type() const
|
|||
return dialect::escape_identifier_t::ESCAPE_BOTH_SAME;
|
||||
}
|
||||
|
||||
std::string dialect::next_placeholder() const
|
||||
std::string dialect::next_placeholder(const std::vector<std::string> &bind_vars) const
|
||||
{
|
||||
return "?";
|
||||
return placeholder_func_(bind_vars.size());
|
||||
}
|
||||
|
||||
void dialect::add_host_var(const std::string &host_var)
|
||||
std::string dialect::default_schema_name() const
|
||||
{
|
||||
host_vars_.emplace_back(host_var);
|
||||
}
|
||||
|
||||
void dialect::add_column(const std::string &column)
|
||||
{
|
||||
columns_.emplace_back(column);
|
||||
}
|
||||
|
||||
const std::vector<std::string> &dialect::host_vars() const
|
||||
{
|
||||
return host_vars_;
|
||||
}
|
||||
|
||||
const std::vector<std::string> &dialect::columns() const
|
||||
{
|
||||
return columns_;
|
||||
return default_schema_name_;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
#include "matador/sql/dialect_builder.hpp"
|
||||
|
||||
namespace matador::sql {
|
||||
dialect_builder &dialect_builder::builder()
|
||||
{
|
||||
static dialect_builder instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
dialect_builder &dialect_builder::create()
|
||||
{
|
||||
dialect_ = {};
|
||||
return *this;
|
||||
}
|
||||
|
||||
dialect_builder& dialect_builder::with_token_replace_map(const dialect::token_to_string_map &token_replace_map)
|
||||
{
|
||||
for (const auto &token : token_replace_map) {
|
||||
dialect_.tokens_[token.first] = token.second;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
dialect_builder& dialect_builder::with_data_type_replace_map(const dialect::data_type_to_string_map &data_type_replace_map)
|
||||
{
|
||||
for (const auto &data_type : data_type_replace_map) {
|
||||
dialect_.data_types_[data_type.first] = data_type.second;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
dialect_builder& dialect_builder::with_placeholder_func(const dialect::next_placeholder_func &func)
|
||||
{
|
||||
dialect_.placeholder_func_ = func;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
dialect_builder &dialect_builder::with_default_schema_name(const std::string &schema_name)
|
||||
{
|
||||
dialect_.default_schema_name_ = schema_name;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
dialect dialect_builder::build()
|
||||
{
|
||||
return dialect_;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
#include "matador/sql/object_binder.hpp"
|
||||
#include "matador/sql/parameter_binder.hpp"
|
||||
|
||||
namespace matador::sql {
|
||||
|
||||
object_binder::object_binder(parameter_binder &binder)
|
||||
: binder_(binder) {}
|
||||
|
||||
void object_binder::reset()
|
||||
{
|
||||
index_ = 0;
|
||||
}
|
||||
|
||||
void object_binder::on_primary_key(const char *id, std::string &val, size_t)
|
||||
{
|
||||
binder_.bind(index_++, val);
|
||||
}
|
||||
|
||||
void object_binder::on_revision(const char *id, unsigned long long int &rev)
|
||||
{
|
||||
binder_.bind(index_++, rev);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
#include "matador/sql/placeholder_generator.hpp"
|
||||
|
||||
namespace matador::sql {
|
||||
|
||||
void placeholder_generator::on_primary_key(const char *id, std::string &, size_t)
|
||||
{
|
||||
placeholder_values.emplace_back(_);
|
||||
}
|
||||
|
||||
void placeholder_generator::on_revision(const char *id, unsigned long long int &)
|
||||
{
|
||||
placeholder_values.emplace_back(_);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -9,6 +9,9 @@ namespace matador::sql {
|
|||
|
||||
namespace detail {
|
||||
|
||||
any_type_to_string_visitor::any_type_to_string_visitor(const dialect &d, query_context &query)
|
||||
: d(d), query(query) {}
|
||||
|
||||
void any_type_to_string_visitor::to_string(const char *val)
|
||||
{
|
||||
result = "'" + d.prepare_literal(val) + "'";
|
||||
|
|
@ -19,69 +22,76 @@ void any_type_to_string_visitor::to_string(std::string &val)
|
|||
result = "'" + d.prepare_literal(val) + "'";
|
||||
}
|
||||
|
||||
void any_type_to_string_visitor::to_string(placeholder &/*val*/)
|
||||
{
|
||||
query.bind_vars.emplace_back("unknown");
|
||||
result = d.next_placeholder(query.bind_vars);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// poor mens state machine
|
||||
// but does the job for query
|
||||
query_builder::query_state_transition_map query_builder::transitions_{
|
||||
{state_t::QUERY_INIT, {state_t::QUERY_CREATE, state_t::QUERY_DROP, state_t::QUERY_SELECT, state_t::QUERY_INSERT, state_t::QUERY_UPDATE, state_t::QUERY_DELETE}},
|
||||
{state_t::QUERY_CREATE, {state_t::QUERY_TABLE_CREATE}},
|
||||
{state_t::QUERY_DROP, {state_t::QUERY_TABLE_DROP}},
|
||||
{state_t::QUERY_SELECT, {state_t::QUERY_FROM}},
|
||||
{state_t::QUERY_INSERT, {state_t::QUERY_INTO}},
|
||||
{state_t::QUERY_UPDATE, {state_t::QUERY_SET}},
|
||||
{state_t::QUERY_DELETE, {state_t::QUERY_FROM}},
|
||||
{state_t::QUERY_TABLE_CREATE, {state_t::QUERY_FINISH}},
|
||||
{state_t::QUERY_TABLE_DROP, {state_t::QUERY_FINISH}},
|
||||
{state_t::QUERY_FROM, {state_t::QUERY_OFFSET, state_t::QUERY_LIMIT, state_t::QUERY_WHERE, state_t::QUERY_ORDER_BY, state_t::QUERY_GROUP_BY, state_t::QUERY_FINISH}},
|
||||
{state_t::QUERY_SET, {state_t::QUERY_WHERE, state_t::QUERY_FINISH}},
|
||||
{state_t::QUERY_INTO, {state_t::QUERY_VALUES}},
|
||||
{state_t::QUERY_WHERE, {state_t::QUERY_ORDER_BY, state_t::QUERY_GROUP_BY, state_t::QUERY_OFFSET, state_t::QUERY_LIMIT, state_t::QUERY_FINISH}},
|
||||
{state_t::QUERY_ORDER_BY, {state_t::QUERY_ORDER_DIRECTION}},
|
||||
{state_t::QUERY_ORDER_DIRECTION, {state_t::QUERY_OFFSET, state_t::QUERY_LIMIT, state_t::QUERY_FINISH}},
|
||||
{state_t::QUERY_GROUP_BY, {state_t::QUERY_ORDER_BY, state_t::QUERY_FINISH}},
|
||||
{state_t::QUERY_OFFSET, {state_t::QUERY_LIMIT}},
|
||||
{state_t::QUERY_LIMIT, {state_t::QUERY_FINISH}},
|
||||
{state_t::QUERY_VALUES, {state_t::QUERY_FINISH}},
|
||||
{state_t::QUERY_FINISH, {}},
|
||||
{state_t::QUERY_INIT, {state_t::QUERY_CREATE, state_t::QUERY_DROP, state_t::QUERY_SELECT, state_t::QUERY_INSERT, state_t::QUERY_UPDATE, state_t::QUERY_DELETE}},
|
||||
{state_t::QUERY_CREATE, {state_t::QUERY_TABLE_CREATE}},
|
||||
{state_t::QUERY_DROP, {state_t::QUERY_TABLE_DROP}},
|
||||
{state_t::QUERY_SELECT, {state_t::QUERY_FROM}},
|
||||
{state_t::QUERY_INSERT, {state_t::QUERY_INTO}},
|
||||
{state_t::QUERY_UPDATE, {state_t::QUERY_SET}},
|
||||
{state_t::QUERY_DELETE, {state_t::QUERY_FROM}},
|
||||
{state_t::QUERY_TABLE_CREATE, {state_t::QUERY_FINISH}},
|
||||
{state_t::QUERY_TABLE_DROP, {state_t::QUERY_FINISH}},
|
||||
{state_t::QUERY_FROM, {state_t::QUERY_OFFSET, state_t::QUERY_LIMIT, state_t::QUERY_WHERE, state_t::QUERY_ORDER_BY, state_t::QUERY_GROUP_BY, state_t::QUERY_FINISH}},
|
||||
{state_t::QUERY_SET, {state_t::QUERY_WHERE, state_t::QUERY_FINISH}},
|
||||
{state_t::QUERY_INTO, {state_t::QUERY_VALUES}},
|
||||
{state_t::QUERY_WHERE, {state_t::QUERY_ORDER_BY, state_t::QUERY_GROUP_BY, state_t::QUERY_OFFSET, state_t::QUERY_LIMIT, state_t::QUERY_FINISH}},
|
||||
{state_t::QUERY_ORDER_BY, {state_t::QUERY_ORDER_DIRECTION}},
|
||||
{state_t::QUERY_ORDER_DIRECTION, {state_t::QUERY_OFFSET, state_t::QUERY_LIMIT, state_t::QUERY_FINISH}},
|
||||
{state_t::QUERY_GROUP_BY, {state_t::QUERY_ORDER_BY, state_t::QUERY_FINISH}},
|
||||
{state_t::QUERY_OFFSET, {state_t::QUERY_LIMIT}},
|
||||
{state_t::QUERY_LIMIT, {state_t::QUERY_FINISH}},
|
||||
{state_t::QUERY_VALUES, {state_t::QUERY_FINISH}},
|
||||
{state_t::QUERY_FINISH, {}},
|
||||
};
|
||||
|
||||
query_builder::query_state_to_string_map query_builder::state_strings_ {
|
||||
{ state_t::QUERY_INIT, "init" },
|
||||
{ state_t::QUERY_CREATE, "create" },
|
||||
{ state_t::QUERY_DROP, "drop" },
|
||||
{ state_t::QUERY_SELECT, "select" },
|
||||
{ state_t::QUERY_INSERT, "insert" },
|
||||
{ state_t::QUERY_UPDATE, "update" },
|
||||
{ state_t::QUERY_DELETE, "delete" },
|
||||
{ state_t::QUERY_TABLE_CREATE, "table" },
|
||||
{ state_t::QUERY_TABLE_DROP, "table" },
|
||||
{ state_t::QUERY_FROM, "from" },
|
||||
{ state_t::QUERY_SET, "set" },
|
||||
{ state_t::QUERY_INTO, "into" },
|
||||
{ state_t::QUERY_WHERE, "where" },
|
||||
{ state_t::QUERY_ORDER_BY, "order_by" },
|
||||
{ state_t::QUERY_GROUP_BY, "group_by" },
|
||||
{ state_t::QUERY_OFFSET, "offset" },
|
||||
{ state_t::QUERY_LIMIT, "limit" },
|
||||
{ state_t::QUERY_FINISH, "finish" },
|
||||
query_builder::query_state_to_string_map query_builder::state_strings_{
|
||||
{state_t::QUERY_INIT, "init"},
|
||||
{state_t::QUERY_CREATE, "create"},
|
||||
{state_t::QUERY_DROP, "drop"},
|
||||
{state_t::QUERY_SELECT, "select"},
|
||||
{state_t::QUERY_INSERT, "insert"},
|
||||
{state_t::QUERY_UPDATE, "update"},
|
||||
{state_t::QUERY_DELETE, "delete"},
|
||||
{state_t::QUERY_TABLE_CREATE, "table"},
|
||||
{state_t::QUERY_TABLE_DROP, "table"},
|
||||
{state_t::QUERY_FROM, "from"},
|
||||
{state_t::QUERY_SET, "set"},
|
||||
{state_t::QUERY_INTO, "into"},
|
||||
{state_t::QUERY_WHERE, "where"},
|
||||
{state_t::QUERY_ORDER_BY, "order_by"},
|
||||
{state_t::QUERY_GROUP_BY, "group_by"},
|
||||
{state_t::QUERY_OFFSET, "offset"},
|
||||
{state_t::QUERY_LIMIT, "limit"},
|
||||
{state_t::QUERY_FINISH, "finish"},
|
||||
};
|
||||
|
||||
query_builder::query_command_to_string_map query_builder::command_strings_ {
|
||||
{ command_t::UNKNOWN, "unknown" },
|
||||
{ command_t::CREATE, "create" },
|
||||
{ command_t::DROP, "drop" },
|
||||
{ command_t::SELECT, "select" },
|
||||
{ command_t::INSERT, "insert" },
|
||||
{ command_t::UPDATE, "update" },
|
||||
{ command_t::REMOVE, "remove" },
|
||||
query_builder::query_command_to_string_map query_builder::command_strings_{
|
||||
{command_t::UNKNOWN, "unknown"},
|
||||
{command_t::CREATE, "create"},
|
||||
{command_t::DROP, "drop"},
|
||||
{command_t::SELECT, "select"},
|
||||
{command_t::INSERT, "insert"},
|
||||
{command_t::UPDATE, "update"},
|
||||
{command_t::REMOVE, "remove"},
|
||||
};
|
||||
|
||||
query_builder::query_builder(const dialect &d)
|
||||
: dialect_(d)
|
||||
, value_to_string_(d) {}
|
||||
: dialect_(d), value_to_string_(d, query_)
|
||||
{}
|
||||
|
||||
query_builder& query_builder::create() {
|
||||
query_builder &query_builder::create()
|
||||
{
|
||||
initialize(command_t::CREATE, state_t::QUERY_CREATE);
|
||||
|
||||
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::CREATE));
|
||||
|
|
@ -89,7 +99,8 @@ query_builder& query_builder::create() {
|
|||
return *this;
|
||||
}
|
||||
|
||||
query_builder& query_builder::drop() {
|
||||
query_builder &query_builder::drop()
|
||||
{
|
||||
initialize(command_t::DROP, state_t::QUERY_DROP);
|
||||
|
||||
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::DROP));
|
||||
|
|
@ -97,7 +108,7 @@ query_builder& query_builder::drop() {
|
|||
return *this;
|
||||
}
|
||||
|
||||
query_builder& query_builder::select(std::initializer_list<column> columns)
|
||||
query_builder &query_builder::select(std::initializer_list<column> columns)
|
||||
{
|
||||
return select(std::vector<column>{columns});
|
||||
}
|
||||
|
|
@ -112,7 +123,7 @@ query_builder &query_builder::select(const std::vector<column> &columns)
|
|||
|
||||
std::string result;
|
||||
if (columns.size() < 2) {
|
||||
for (const auto &col : columns) {
|
||||
for (const auto &col: columns) {
|
||||
result.append(dialect_.prepare_identifier(col));
|
||||
query_.prototype.append(col);
|
||||
}
|
||||
|
|
@ -131,7 +142,8 @@ query_builder &query_builder::select(const std::vector<column> &columns)
|
|||
return *this;
|
||||
}
|
||||
|
||||
query_builder& query_builder::insert() {
|
||||
query_builder &query_builder::insert()
|
||||
{
|
||||
initialize(command_t::INSERT, state_t::QUERY_INSERT);
|
||||
|
||||
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::INSERT));
|
||||
|
|
@ -139,7 +151,8 @@ query_builder& query_builder::insert() {
|
|||
return *this;
|
||||
}
|
||||
|
||||
query_builder& query_builder::update(const std::string &table) {
|
||||
query_builder &query_builder::update(const std::string &table)
|
||||
{
|
||||
initialize(command_t::UPDATE, state_t::QUERY_UPDATE);
|
||||
|
||||
query_.table_name = table;
|
||||
|
|
@ -148,7 +161,8 @@ query_builder& query_builder::update(const std::string &table) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
query_builder& query_builder::remove() {
|
||||
query_builder &query_builder::remove()
|
||||
{
|
||||
initialize(command_t::REMOVE, state_t::QUERY_DELETE);
|
||||
|
||||
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::REMOVE));
|
||||
|
|
@ -156,7 +170,7 @@ query_builder& query_builder::remove() {
|
|||
return *this;
|
||||
}
|
||||
|
||||
query_builder& query_builder::table(const std::string &table, std::initializer_list<column> columns)
|
||||
query_builder &query_builder::table(const std::string &table, std::initializer_list<column> columns)
|
||||
{
|
||||
return this->table(table, std::vector<column>{columns});
|
||||
}
|
||||
|
|
@ -176,7 +190,8 @@ struct column_context
|
|||
|
||||
std::string build_create_column(const column &col, const dialect &d, column_context &context);
|
||||
|
||||
query_builder &query_builder::table(const std::string &table, const std::vector<column> &columns) {
|
||||
query_builder &query_builder::table(const std::string &table, const std::vector<column> &columns)
|
||||
{
|
||||
transition_to(state_t::QUERY_TABLE_CREATE);
|
||||
|
||||
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(table) + " ");
|
||||
|
|
@ -187,7 +202,7 @@ query_builder &query_builder::table(const std::string &table, const std::vector<
|
|||
column_context context;
|
||||
|
||||
if (columns.size() < 2) {
|
||||
for (const auto &col : columns) {
|
||||
for (const auto &col: columns) {
|
||||
result.append(build_create_column(col, dialect_, context));
|
||||
}
|
||||
} else {
|
||||
|
|
@ -202,7 +217,7 @@ query_builder &query_builder::table(const std::string &table, const std::vector<
|
|||
if (!context.primary_keys.empty()) {
|
||||
result.append(", CONSTRAINT PK_" + table + " PRIMARY KEY (" + utils::join(context.primary_keys, ", ") + ")");
|
||||
}
|
||||
for (const auto &fk : context.foreign_contexts) {
|
||||
for (const auto &fk: context.foreign_contexts) {
|
||||
result += ", CONSTRAINT FK_" + table;
|
||||
result += "_" + fk.column;
|
||||
result += " FOREIGN KEY (" + fk.column + ")";
|
||||
|
|
@ -214,7 +229,8 @@ query_builder &query_builder::table(const std::string &table, const std::vector<
|
|||
return *this;
|
||||
}
|
||||
|
||||
query_builder& query_builder::table(const std::string &table) {
|
||||
query_builder &query_builder::table(const std::string &table)
|
||||
{
|
||||
transition_to(state_t::QUERY_TABLE_DROP);
|
||||
|
||||
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(table));
|
||||
|
|
@ -223,7 +239,7 @@ query_builder& query_builder::table(const std::string &table) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
query_builder& query_builder::into(const std::string &table, std::initializer_list<std::string> column_names)
|
||||
query_builder &query_builder::into(const std::string &table, std::initializer_list<std::string> column_names)
|
||||
{
|
||||
return into(table, std::vector<std::string>{column_names});
|
||||
}
|
||||
|
|
@ -237,7 +253,7 @@ query_builder &query_builder::into(const std::string &table, const std::vector<s
|
|||
|
||||
std::string result{"("};
|
||||
if (column_names.size() < 2) {
|
||||
for (const auto &col : column_names) {
|
||||
for (const auto &col: column_names) {
|
||||
result.append(dialect_.prepare_identifier(col));
|
||||
}
|
||||
} else {
|
||||
|
|
@ -254,19 +270,20 @@ query_builder &query_builder::into(const std::string &table, const std::vector<s
|
|||
return *this;
|
||||
}
|
||||
|
||||
query_builder& query_builder::values(std::initializer_list<any_type> values)
|
||||
query_builder &query_builder::values(std::initializer_list<any_type> values)
|
||||
{
|
||||
return this->values(std::vector<any_type>{values});
|
||||
}
|
||||
|
||||
query_builder& query_builder::values(const std::vector<any_type> &values) {
|
||||
query_builder &query_builder::values(const std::vector<any_type> &values)
|
||||
{
|
||||
transition_to(state_t::QUERY_VALUES);
|
||||
|
||||
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::VALUES) + " ");
|
||||
|
||||
std::string result{"("};
|
||||
if (values.size() < 2) {
|
||||
for (auto val : values) {
|
||||
for (auto val: values) {
|
||||
query_.host_vars.push_back(val);
|
||||
std::visit(value_to_string_, val);
|
||||
result.append(value_to_string_.result);
|
||||
|
|
@ -291,10 +308,20 @@ query_builder& query_builder::values(const std::vector<any_type> &values) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
query_builder& query_builder::from(const std::string &table, const std::string &as) {
|
||||
query_builder &query_builder::from(const std::string &table, const std::string &as)
|
||||
{
|
||||
transition_to(state_t::QUERY_FROM);
|
||||
|
||||
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::FROM) + " " + dialect_.prepare_identifier(table) + (as.empty() ? "" : " " + as));
|
||||
if (dialect_.default_schema_name().empty()) {
|
||||
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::FROM) +
|
||||
" " + dialect_.prepare_identifier(table) +
|
||||
(as.empty() ? "" : " " + as));
|
||||
} else {
|
||||
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::FROM) +
|
||||
" " + dialect_.prepare_identifier(dialect_.default_schema_name()) +
|
||||
"." + dialect_.prepare_identifier(table) +
|
||||
(as.empty() ? "" : " " + as));
|
||||
}
|
||||
query_.table_name = table;
|
||||
|
||||
return *this;
|
||||
|
|
@ -315,14 +342,15 @@ query_builder &query_builder::set(std::initializer_list<key_value_pair> key_valu
|
|||
return set(std::vector<key_value_pair>{key_values});
|
||||
}
|
||||
|
||||
query_builder& query_builder::set(const std::vector<key_value_pair> &key_values) {
|
||||
query_builder &query_builder::set(const std::vector<key_value_pair> &key_values)
|
||||
{
|
||||
transition_to(state_t::QUERY_SET);
|
||||
|
||||
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::SET) + " ");
|
||||
|
||||
std::string result;
|
||||
if (key_values.size() < 2) {
|
||||
for (const auto &col : key_values) {
|
||||
for (const auto &col: key_values) {
|
||||
result.append(dialect_.prepare_identifier(col.name()) + "=");
|
||||
auto var = col.value();
|
||||
std::visit(value_to_string_, var);
|
||||
|
|
@ -347,67 +375,76 @@ query_builder& query_builder::set(const std::vector<key_value_pair> &key_values)
|
|||
return *this;
|
||||
}
|
||||
|
||||
query_builder& query_builder::where(const basic_condition &cond) {
|
||||
query_builder &query_builder::where(const basic_condition &cond)
|
||||
{
|
||||
transition_to(state_t::QUERY_WHERE);
|
||||
|
||||
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::WHERE) + " ");
|
||||
query_parts_.emplace_back(cond.evaluate(const_cast<dialect &>(dialect_)));
|
||||
query_parts_.emplace_back(cond.evaluate(const_cast<dialect &>(dialect_), query_));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
query_builder& query_builder::order_by(const std::string& column) {
|
||||
transition_to(state_t::QUERY_ORDER_BY);
|
||||
query_builder &query_builder::order_by(const std::string &column)
|
||||
{
|
||||
transition_to(state_t::QUERY_ORDER_BY);
|
||||
|
||||
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::ORDER_BY) + " " + dialect_.prepare_identifier(column));
|
||||
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::ORDER_BY) + " " + dialect_.prepare_identifier(column));
|
||||
|
||||
return *this;
|
||||
return *this;
|
||||
}
|
||||
|
||||
query_builder& query_builder::group_by(const std::string& column) {
|
||||
transition_to(state_t::QUERY_GROUP_BY);
|
||||
query_builder &query_builder::group_by(const std::string &column)
|
||||
{
|
||||
transition_to(state_t::QUERY_GROUP_BY);
|
||||
|
||||
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::GROUP_BY) + " " + dialect_.prepare_identifier(column));
|
||||
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::GROUP_BY) + " " + dialect_.prepare_identifier(column));
|
||||
|
||||
return *this;
|
||||
return *this;
|
||||
}
|
||||
|
||||
query_builder& query_builder::asc() {
|
||||
transition_to(state_t::QUERY_ORDER_DIRECTION);
|
||||
query_builder &query_builder::asc()
|
||||
{
|
||||
transition_to(state_t::QUERY_ORDER_DIRECTION);
|
||||
|
||||
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::ASC));
|
||||
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::ASC));
|
||||
|
||||
return *this;
|
||||
return *this;
|
||||
}
|
||||
|
||||
query_builder& query_builder::desc() {
|
||||
transition_to(state_t::QUERY_ORDER_DIRECTION);
|
||||
query_builder &query_builder::desc()
|
||||
{
|
||||
transition_to(state_t::QUERY_ORDER_DIRECTION);
|
||||
|
||||
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::DESC));
|
||||
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::DESC));
|
||||
|
||||
return *this;
|
||||
return *this;
|
||||
}
|
||||
|
||||
query_builder& query_builder::offset( size_t count ) {
|
||||
transition_to(state_t::QUERY_OFFSET);
|
||||
query_builder &query_builder::offset(size_t count)
|
||||
{
|
||||
transition_to(state_t::QUERY_OFFSET);
|
||||
|
||||
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::OFFSET) + " " + std::to_string(count));
|
||||
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::OFFSET) + " " + std::to_string(count));
|
||||
|
||||
return *this;
|
||||
return *this;
|
||||
}
|
||||
|
||||
query_builder& query_builder::limit( size_t count ) {
|
||||
transition_to(state_t::QUERY_LIMIT);
|
||||
query_builder &query_builder::limit(size_t count)
|
||||
{
|
||||
transition_to(state_t::QUERY_LIMIT);
|
||||
|
||||
query_parts_.emplace_back( " " + dialect_.token_at(dialect::token_t::LIMIT) + " " + std::to_string( count));
|
||||
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::LIMIT) + " " + std::to_string(count));
|
||||
|
||||
return *this;
|
||||
return *this;
|
||||
}
|
||||
|
||||
query query_builder::compile() {
|
||||
for (const auto &part : query_parts_) {
|
||||
query_context query_builder::compile()
|
||||
{
|
||||
for (const auto &part: query_parts_) {
|
||||
query_.sql.append(part);
|
||||
}
|
||||
query_.command_name = command_strings_[command_];
|
||||
return query_;
|
||||
}
|
||||
|
||||
|
|
@ -427,22 +464,23 @@ void query_builder::initialize(query_builder::command_t cmd, query_builder::stat
|
|||
query_parts_.clear();
|
||||
}
|
||||
|
||||
std::string build_create_column(const column &col, const dialect &d, column_context &context) {
|
||||
std::string result = d.prepare_identifier(col.name()) + " " + d.data_type_at(col.type());
|
||||
if (col.attributes().size() > 0) {
|
||||
result.append("(" + std::to_string(col.attributes().size()) +")");
|
||||
}
|
||||
if (is_constraint_set(col.attributes().options(), utils::constraints::NOT_NULL)) {
|
||||
result.append(" NOT NULL");
|
||||
}
|
||||
if (is_constraint_set(col.attributes().options(), utils::constraints::PRIMARY_KEY)) {
|
||||
context.primary_keys.emplace_back(col.name());
|
||||
}
|
||||
if (is_constraint_set(col.attributes().options(), utils::constraints::FOREIGN_KEY)) {
|
||||
context.foreign_contexts.push_back({col.name(), col.ref_table(), col.ref_column()});
|
||||
}
|
||||
std::string build_create_column(const column &col, const dialect &d, column_context &context)
|
||||
{
|
||||
std::string result = d.prepare_identifier(col.name()) + " " + d.data_type_at(col.type());
|
||||
if (col.attributes().size() > 0) {
|
||||
result.append("(" + std::to_string(col.attributes().size()) + ")");
|
||||
}
|
||||
if (is_constraint_set(col.attributes().options(), utils::constraints::NOT_NULL)) {
|
||||
result.append(" NOT NULL");
|
||||
}
|
||||
if (is_constraint_set(col.attributes().options(), utils::constraints::PRIMARY_KEY)) {
|
||||
context.primary_keys.emplace_back(col.name());
|
||||
}
|
||||
if (is_constraint_set(col.attributes().options(), utils::constraints::FOREIGN_KEY)) {
|
||||
context.foreign_contexts.push_back({col.name(), col.ref_table(), col.ref_column()});
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
column alias(const std::string &column, const std::string &as)
|
||||
|
|
@ -450,7 +488,8 @@ column alias(const std::string &column, const std::string &as)
|
|||
return {column, as};
|
||||
}
|
||||
|
||||
column alias(column &&col, const std::string &as) {
|
||||
column alias(column &&col, const std::string &as)
|
||||
{
|
||||
col.alias(as);
|
||||
return std::move(col);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,11 @@ std::unique_ptr<query_result_impl> query_select_finish::fetch()
|
|||
return session_.fetch(builder_.compile().sql);
|
||||
}
|
||||
|
||||
statement query_select_finish::prepare()
|
||||
{
|
||||
return session_.prepare(builder_.compile());
|
||||
}
|
||||
|
||||
query_intermediate::query_intermediate(session &db, query_builder &query)
|
||||
: session_(db), builder_(query) {}
|
||||
|
||||
|
|
@ -103,6 +108,11 @@ std::pair<size_t, std::string> query_execute_finish::execute()
|
|||
return session_.execute(builder_.compile().sql);
|
||||
}
|
||||
|
||||
statement query_execute_finish::prepare()
|
||||
{
|
||||
return session_.prepare(builder_.compile());
|
||||
}
|
||||
|
||||
query_execute_finish query_into_intermediate::values(std::initializer_list<any_type> values)
|
||||
{
|
||||
return {session_, builder_.values(values)};
|
||||
|
|
@ -140,10 +150,10 @@ query_execute_where_intermediate query_set_intermediate::where(const basic_condi
|
|||
return {session_, builder_.where(cond)};
|
||||
}
|
||||
|
||||
query_update_intermediate::query_update_intermediate(session &s, std::string table_name)
|
||||
query_update_intermediate::query_update_intermediate(session &s, const std::string& table_name)
|
||||
: query_start_intermediate(s)
|
||||
{
|
||||
builder_.update(std::move(table_name));
|
||||
builder_.update(table_name);
|
||||
}
|
||||
|
||||
query_set_intermediate query_update_intermediate::set(std::initializer_list<key_value_pair> columns)
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ query_delete_intermediate session::remove()
|
|||
return query_delete_intermediate{*this};
|
||||
}
|
||||
|
||||
query_result<record> session::fetch(const query &q) const
|
||||
query_result<record> session::fetch(const query_context &q) const
|
||||
{
|
||||
auto c = pool_.acquire();
|
||||
if (!c.valid()) {
|
||||
|
|
@ -73,6 +73,15 @@ std::pair<size_t, std::string> session::execute(const std::string &sql) const {
|
|||
return c->execute(sql);
|
||||
}
|
||||
|
||||
statement session::prepare(query_context q) const
|
||||
{
|
||||
auto c = pool_.acquire();
|
||||
if (!c.valid()) {
|
||||
throw std::logic_error("no database connection available");
|
||||
}
|
||||
return c->prepare(std::move(q));
|
||||
}
|
||||
|
||||
record session::describe_table(const std::string &table_name) const
|
||||
{
|
||||
auto c = pool_.acquire();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
#include "matador/sql/statement.hpp"
|
||||
|
||||
namespace matador::sql {
|
||||
|
||||
statement::statement(std::unique_ptr<statement_impl> impl, const utils::logger &logger)
|
||||
: statement_(std::move(impl))
|
||||
, logger_(logger)
|
||||
, object_binder_(statement_->binder())
|
||||
{}
|
||||
|
||||
statement &statement::bind(size_t pos, std::string &val, size_t size)
|
||||
{
|
||||
statement_->bind(pos, val, size);
|
||||
return *this;
|
||||
}
|
||||
|
||||
size_t statement::execute()
|
||||
{
|
||||
logger_.info(statement_->query_.sql);
|
||||
return statement_->execute();
|
||||
}
|
||||
|
||||
query_result<record> statement::fetch()
|
||||
{
|
||||
logger_.info(statement_->query_.sql);
|
||||
return query_result<record>(statement_->fetch());
|
||||
}
|
||||
|
||||
void statement::reset()
|
||||
{
|
||||
statement_->reset();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
#include "matador/sql/statement_cache.hpp"
|
||||
|
||||
namespace matador::sql {
|
||||
statement &statement_cache::acquire(const std::string &stmt, const connection &conn) {
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
auto key = hash_(stmt);
|
||||
auto it = statement_map_.find(key);
|
||||
if (it == statement_map_.end()) {
|
||||
|
||||
}
|
||||
return *it->second.statement_;
|
||||
}
|
||||
|
||||
void statement_cache::release(const statement &stmt) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
#include "matador/sql/statement_impl.hpp"
|
||||
|
||||
namespace matador::sql {
|
||||
|
||||
statement_impl::statement_impl(query_context query)
|
||||
: query_(std::move(query))
|
||||
{}
|
||||
|
||||
}
|
||||
|
|
@ -25,7 +25,9 @@ add_executable(tests QueryBuilderTest.cpp
|
|||
models/person.hpp
|
||||
AnyTypeToVisitorTest.cpp
|
||||
ColumnTest.cpp
|
||||
SessionRecordTest.cpp)
|
||||
SessionRecordTest.cpp
|
||||
StatementCacheTest.cpp
|
||||
StatementTest.cpp)
|
||||
target_link_libraries(tests PRIVATE
|
||||
Catch2::Catch2WithMain
|
||||
matador
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
|
||||
#include <matador/sql/column.hpp>
|
||||
#include <matador/sql/condition.hpp>
|
||||
#include <matador/sql/dialect.hpp>
|
||||
#include <matador/sql/dialect_builder.hpp>
|
||||
#include <matador/sql/query_builder.hpp>
|
||||
|
||||
using namespace matador::sql;
|
||||
|
||||
TEST_CASE("Create table sql statement string", "[query]") {
|
||||
dialect d;
|
||||
dialect d = dialect_builder::builder().create().build();
|
||||
query_builder query(d);
|
||||
auto q = query.create().table("person", {
|
||||
make_pk_column<unsigned long>("id"),
|
||||
|
|
@ -31,7 +31,7 @@ TEST_CASE("Create table sql statement string", "[query]") {
|
|||
}
|
||||
|
||||
TEST_CASE("Drop table sql statement string", "[query]") {
|
||||
dialect d;
|
||||
dialect d = dialect_builder::builder().create().build();
|
||||
query_builder query(d);
|
||||
const auto q = query.drop().table("person").compile();
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ TEST_CASE("Drop table sql statement string", "[query]") {
|
|||
}
|
||||
|
||||
TEST_CASE("Select sql statement string", "[query]") {
|
||||
dialect d;
|
||||
dialect d = dialect_builder::builder().create().build();
|
||||
query_builder query(d);
|
||||
const auto q = query.select({"id", "name", "age"}).from("person").compile();
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ TEST_CASE("Select sql statement string", "[query]") {
|
|||
}
|
||||
|
||||
TEST_CASE("Insert sql statement string", "[query]") {
|
||||
dialect d;
|
||||
dialect d = dialect_builder::builder().create().build();
|
||||
query_builder query(d);
|
||||
const auto q = query.insert().into("person", {
|
||||
"id", "name", "age"
|
||||
|
|
@ -60,7 +60,7 @@ TEST_CASE("Insert sql statement string", "[query]") {
|
|||
}
|
||||
|
||||
TEST_CASE("Update sql statement string", "[query]") {
|
||||
dialect d;
|
||||
dialect d = dialect_builder::builder().create().build();
|
||||
query_builder query(d);
|
||||
const auto q = query.update("person").set({
|
||||
{"id", 7UL},
|
||||
|
|
@ -73,7 +73,7 @@ TEST_CASE("Update sql statement string", "[query]") {
|
|||
}
|
||||
|
||||
TEST_CASE("Delete sql statement string", "[query]") {
|
||||
dialect d;
|
||||
dialect d = dialect_builder::builder().create().build();
|
||||
query_builder query(d);
|
||||
const auto q = query.remove().from("person").compile();
|
||||
|
||||
|
|
@ -82,7 +82,7 @@ TEST_CASE("Delete sql statement string", "[query]") {
|
|||
}
|
||||
|
||||
TEST_CASE("Select sql statement string with where clause", "[query]") {
|
||||
dialect d;
|
||||
dialect d = dialect_builder::builder().create().build();
|
||||
query_builder query(d);
|
||||
auto q = query.select({"id", "name", "age"})
|
||||
.from("person")
|
||||
|
|
@ -101,8 +101,20 @@ TEST_CASE("Select sql statement string with where clause", "[query]") {
|
|||
REQUIRE(q.table_name == "person");
|
||||
}
|
||||
|
||||
TEST_CASE("Insert sql statement with placeholder", "[query]") {
|
||||
dialect d = dialect_builder::builder().create().build();
|
||||
query_builder query(d);
|
||||
const auto q = query.insert().into("person", {
|
||||
"id", "name", "age"
|
||||
}).values({_, _, _}).compile();
|
||||
|
||||
REQUIRE(q.sql == R"(INSERT INTO "person" ("id", "name", "age") VALUES (?, ?, ?))");
|
||||
REQUIRE(q.table_name == "person");
|
||||
REQUIRE(q.bind_vars.size() == 3);
|
||||
}
|
||||
|
||||
TEST_CASE("Select sql statement string with order by", "[query]") {
|
||||
dialect d;
|
||||
dialect d = dialect_builder::builder().create().build();
|
||||
query_builder query(d);
|
||||
const auto q = query.select({"id", "name", "age"})
|
||||
.from("person")
|
||||
|
|
@ -114,7 +126,7 @@ TEST_CASE("Select sql statement string with order by", "[query]") {
|
|||
}
|
||||
|
||||
TEST_CASE("Select sql statement string with group by", "[query]") {
|
||||
dialect d;
|
||||
dialect d = dialect_builder::builder().create().build();
|
||||
query_builder query(d);
|
||||
const auto q = query.select({"id", "name", "age"})
|
||||
.from("person")
|
||||
|
|
@ -126,7 +138,7 @@ TEST_CASE("Select sql statement string with group by", "[query]") {
|
|||
}
|
||||
|
||||
TEST_CASE("Select sql statement string with offset and limit", "[query]") {
|
||||
dialect d;
|
||||
dialect d = dialect_builder::builder().create().build();
|
||||
query_builder query(d);
|
||||
const auto q = query.select({"id", "name", "age"})
|
||||
.from("person")
|
||||
|
|
|
|||
|
|
@ -365,7 +365,7 @@ TEST_CASE("Execute delete statement", "[session record]") {
|
|||
.where("id"_col == 1)
|
||||
.execute();
|
||||
|
||||
REQUIRE(res.second == R"(DELETE FROM "person" WHERE "id" = 1)");
|
||||
REQUIRE(res.second == R"(DELETE FROM "main"."person" WHERE "id" = 1)");
|
||||
REQUIRE(res.first == 1);
|
||||
|
||||
count = s.select({count_all()}).from("person").fetch_value<int>();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include "matador/sql/connection_info.hpp"
|
||||
#include "matador/sql/connection_pool.hpp"
|
||||
#include "matador/sql/session.hpp"
|
||||
#include "matador/sql/statement_cache.hpp"
|
||||
|
||||
using namespace matador;
|
||||
|
||||
class TestConnection
|
||||
{
|
||||
public:
|
||||
explicit TestConnection(sql::connection_info info)
|
||||
: info_(std::move(info)) {}
|
||||
void open() {}
|
||||
|
||||
private:
|
||||
sql::connection_info info_;
|
||||
};
|
||||
|
||||
TEST_CASE("Acquire prepared statement", "[statement cache]") {
|
||||
sql::statement_cache cache;
|
||||
sql::connection_pool<TestConnection> pool("sqlite://sqlite.db", 4);
|
||||
|
||||
// sql::session s(pool);
|
||||
// auto conn = pool.acquire();
|
||||
|
||||
std::string sql = R"(SELECT * FROM person WHERE name = 'george')";
|
||||
// auto stmt = cache.acquire(sql, conn);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/catch_template_test_macros.hpp>
|
||||
|
||||
#include "matador/sql/column.hpp"
|
||||
#include "matador/sql/condition.hpp"
|
||||
#include "matador/sql/connection_info.hpp"
|
||||
#include "matador/sql/connection_pool.hpp"
|
||||
#include "matador/sql/session.hpp"
|
||||
|
||||
#include "models/airplane.hpp"
|
||||
|
||||
using namespace matador::sql;
|
||||
using namespace matador::test;
|
||||
|
||||
struct Postgres
|
||||
{
|
||||
// constexpr static const char *dns{"postgres://test:test123@127.0.0.1:15432/test"};
|
||||
constexpr static const char *dns{"postgres://test:test123@127.0.0.1:5432/matador_test"};
|
||||
};
|
||||
|
||||
struct Sqlite
|
||||
{
|
||||
constexpr static const char *dns{"sqlite://sqlite.db"};
|
||||
};
|
||||
|
||||
template<class Type>
|
||||
class StatementTestFixture
|
||||
{
|
||||
public:
|
||||
StatementTestFixture()
|
||||
: pool_(Type::dns, 4), session_(pool_)
|
||||
{
|
||||
auto res = session_.create()
|
||||
.table<airplane>("airplane")
|
||||
.execute();
|
||||
REQUIRE(res.first == 0);
|
||||
REQUIRE(res.second == R"(CREATE TABLE "airplane" ("id" BIGINT, "brand" VARCHAR(255), "model" VARCHAR(255), CONSTRAINT PK_airplane PRIMARY KEY (id)))");
|
||||
}
|
||||
|
||||
~StatementTestFixture()
|
||||
{
|
||||
session_.drop().table("airplane").execute();
|
||||
}
|
||||
|
||||
matador::sql::session &session()
|
||||
{ return session_; }
|
||||
|
||||
std::vector<entity<airplane>> &planes()
|
||||
{ return planes_; }
|
||||
|
||||
private:
|
||||
matador::sql::connection_pool<matador::sql::connection> pool_;
|
||||
matador::sql::session session_;
|
||||
|
||||
std::vector<entity<airplane>> planes_{
|
||||
make_entity<airplane>(1, "Airbus", "A380"),
|
||||
make_entity<airplane>(2, "Boeing", "707"),
|
||||
make_entity<airplane>(3, "Boeing", "747")
|
||||
};
|
||||
};
|
||||
|
||||
TEMPLATE_TEST_CASE_METHOD(StatementTestFixture, "Create prepared statement", "[statement]", Sqlite, Postgres)
|
||||
{
|
||||
auto &s = StatementTestFixture<TestType>::session();
|
||||
auto &planes = StatementTestFixture<TestType>::planes();
|
||||
|
||||
SECTION("Insert with prepared statement and placeholder") {
|
||||
auto stmt = s.insert()
|
||||
.template into<airplane>("airplane")
|
||||
.template values<airplane>().prepare();
|
||||
|
||||
for (const auto &plane: planes) {
|
||||
auto res = stmt.bind(*plane).execute();
|
||||
REQUIRE(res == 1);
|
||||
stmt.reset();
|
||||
}
|
||||
|
||||
auto result = s.template select<airplane>().from("airplane").template fetch_all<airplane>();
|
||||
|
||||
size_t index{0};
|
||||
for (const auto &i: result) {
|
||||
REQUIRE(i.id == planes[index]->id);
|
||||
REQUIRE(i.brand == planes[index]->brand);
|
||||
REQUIRE(i.model == planes[index++]->model);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Select with prepared statement") {
|
||||
for (const auto &plane: planes) {
|
||||
auto res = s.insert().template into<airplane>("airplane").values(*plane).execute();
|
||||
REQUIRE(res.first == 1);
|
||||
}
|
||||
|
||||
auto stmt = s.template select<airplane>().from("airplane").where("brand"_col == _).prepare();
|
||||
|
||||
stmt.bind(0, "Airbus");
|
||||
|
||||
auto result = stmt.template fetch<airplane>();
|
||||
|
||||
for (const auto &i: result) {
|
||||
REQUIRE(i.id == planes[0]->id);
|
||||
REQUIRE(i.brand == planes[0]->brand);
|
||||
REQUIRE(i.model == planes[0]->model);
|
||||
}
|
||||
|
||||
stmt.reset();
|
||||
|
||||
stmt.bind(0, "Boeing");
|
||||
|
||||
result = stmt.template fetch<airplane>();
|
||||
|
||||
size_t index{1};
|
||||
for (const auto &i: result) {
|
||||
REQUIRE(i.id == planes[index]->id);
|
||||
REQUIRE(i.brand == planes[index]->brand);
|
||||
REQUIRE(i.model == planes[index++]->model);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue