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
|
Object definition
|
||||||
```cpp
|
```cpp
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ set(HEADER
|
||||||
include/postgres_error.hpp
|
include/postgres_error.hpp
|
||||||
include/postgres_result_reader.hpp
|
include/postgres_result_reader.hpp
|
||||||
include/postgres_dialect.hpp
|
include/postgres_dialect.hpp
|
||||||
|
include/postgres_statement.hpp
|
||||||
|
include/postgres_parameter_binder.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
|
|
@ -10,17 +12,22 @@ set(SOURCES
|
||||||
src/postgres_error.cpp
|
src/postgres_error.cpp
|
||||||
src/postgres_result_reader.cpp
|
src/postgres_result_reader.cpp
|
||||||
src/postgres_dialect.cpp
|
src/postgres_dialect.cpp
|
||||||
|
src/postgres_statement.cpp
|
||||||
|
src/postgres_parameter_binder.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(matador-postgres SHARED ${SOURCES} ${HEADER})
|
set(LIBRARY_TARGET matador-postgres)
|
||||||
target_include_directories(matador-postgres PRIVATE
|
|
||||||
|
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}/include
|
||||||
${PROJECT_SOURCE_DIR}/backends/postgres/include
|
${PROJECT_SOURCE_DIR}/backends/postgres/include
|
||||||
${PostgreSQL_INCLUDE_DIRS})
|
${PostgreSQL_INCLUDE_DIRS})
|
||||||
|
|
||||||
target_link_libraries(matador-postgres matador ${PostgreSQL_LIBRARIES})
|
target_link_libraries(${LIBRARY_TARGET} matador ${PostgreSQL_LIBRARIES})
|
||||||
|
|
||||||
set_target_properties(matador-postgres
|
|
||||||
PROPERTIES
|
|
||||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/backends"
|
|
||||||
)
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@
|
||||||
|
|
||||||
#include "matador/sql/connection_impl.hpp"
|
#include "matador/sql/connection_impl.hpp"
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
#include <libpq-fe.h>
|
#include <libpq-fe.h>
|
||||||
|
|
||||||
namespace matador::backends::postgres {
|
namespace matador::backends::postgres {
|
||||||
|
|
@ -27,7 +29,7 @@ public:
|
||||||
bool is_open() override;
|
bool is_open() override;
|
||||||
|
|
||||||
std::unique_ptr<sql::query_result_impl> fetch(const std::string &stmt) 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;
|
size_t execute(const std::string &stmt) override;
|
||||||
|
|
||||||
|
|
@ -35,8 +37,15 @@ public:
|
||||||
|
|
||||||
bool exists(const std::string &table_name) override;
|
bool exists(const std::string &table_name) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
[[nodiscard]] static std::string generate_statement_name(const sql::query_context &query) ;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PGconn *conn_{nullptr};
|
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_connection.hpp"
|
||||||
#include "postgres_error.hpp"
|
#include "postgres_error.hpp"
|
||||||
#include "postgres_result_reader.hpp"
|
#include "postgres_result_reader.hpp"
|
||||||
|
#include "postgres_statement.hpp"
|
||||||
|
|
||||||
#include "matador/sql/record.hpp"
|
#include "matador/sql/record.hpp"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
namespace matador::backends::postgres {
|
namespace matador::backends::postgres {
|
||||||
|
|
||||||
|
postgres_connection::string_to_int_map postgres_connection::statement_name_map_{};
|
||||||
|
|
||||||
postgres_connection::postgres_connection(const sql::connection_info &info)
|
postgres_connection::postgres_connection(const sql::connection_info &info)
|
||||||
: connection_impl(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);
|
throw_postgres_error(res, conn_, "postgres", stmt);
|
||||||
|
|
||||||
sql::record prototype;
|
sql::record prototype;
|
||||||
auto ncol = PQnfields(res);
|
auto num_col = PQnfields(res);
|
||||||
for (int i = 0; i < ncol; ++i) {
|
for (int i = 0; i < num_col; ++i) {
|
||||||
const char *col_name = PQfname(res, i);
|
const char *col_name = PQfname(res, i);
|
||||||
auto type = PQftype(res, i);
|
auto type = PQftype(res, i);
|
||||||
auto size = PQfmod(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});
|
prototype.append({col_name});
|
||||||
}
|
}
|
||||||
return std::move(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res), std::move(prototype)));
|
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)
|
size_t postgres_connection::execute(const std::string &stmt)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,15 @@
|
||||||
#include "postgres_dialect.hpp"
|
#include "postgres_dialect.hpp"
|
||||||
|
|
||||||
|
#include "matador/sql/dialect_builder.hpp"
|
||||||
|
|
||||||
[[maybe_unused]] const matador::sql::dialect* get_dialect() {
|
[[maybe_unused]] const matador::sql::dialect* get_dialect() {
|
||||||
using namespace matador::sql;
|
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;
|
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_error.hpp
|
||||||
include/sqlite_dialect.hpp
|
include/sqlite_dialect.hpp
|
||||||
include/sqlite_result_reader.hpp
|
include/sqlite_result_reader.hpp
|
||||||
|
include/sqlite_statement.hpp
|
||||||
|
include/sqlite_parameter_binder.h
|
||||||
|
include/sqlite_prepared_result_reader.hpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
|
|
@ -10,16 +13,23 @@ set(SOURCES
|
||||||
src/sqlite_error.cpp
|
src/sqlite_error.cpp
|
||||||
src/sqlite_dialect.cpp
|
src/sqlite_dialect.cpp
|
||||||
src/sqlite_result_reader.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})
|
set(LIBRARY_TARGET matador-sqlite)
|
||||||
target_include_directories(matador-sqlite PRIVATE
|
|
||||||
|
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}/include
|
||||||
${PROJECT_SOURCE_DIR}/backends/sqlite/include
|
${PROJECT_SOURCE_DIR}/backends/sqlite/include
|
||||||
${SQLite3_INCLUDE_DIRS})
|
${SQLite3_INCLUDE_DIRS})
|
||||||
|
|
||||||
target_link_libraries(matador-sqlite matador ${SQLite3_LIBRARIES})
|
target_link_libraries(${LIBRARY_TARGET} matador ${SQLite3_LIBRARIES})
|
||||||
set_target_properties(matador-sqlite
|
|
||||||
PROPERTIES
|
|
||||||
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/backends"
|
|
||||||
)
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ public:
|
||||||
bool is_open() override;
|
bool is_open() override;
|
||||||
|
|
||||||
std::unique_ptr<sql::query_result_impl> fetch(const std::string &stmt) 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;
|
size_t execute(const std::string &stmt) override;
|
||||||
|
|
||||||
|
|
@ -50,7 +50,7 @@ private:
|
||||||
fetch_context fetch_internal(const std::string &stmt);
|
fetch_context fetch_internal(const std::string &stmt);
|
||||||
|
|
||||||
private:
|
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 {
|
namespace matador::backends::sqlite {
|
||||||
|
|
||||||
class sqlite_result_reader : public sql::query_result_reader
|
class sqlite_result_reader final : public sql::query_result_reader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using columns = std::vector<char*>;
|
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_connection.hpp"
|
||||||
#include "sqlite_error.hpp"
|
#include "sqlite_error.hpp"
|
||||||
#include "sqlite_result_reader.hpp"
|
#include "sqlite_result_reader.hpp"
|
||||||
|
#include "sqlite_statement.hpp"
|
||||||
|
|
||||||
#include "matador/sql/record.hpp"
|
#include "matador/sql/record.hpp"
|
||||||
|
|
||||||
|
|
@ -20,25 +21,25 @@ void sqlite_connection::open()
|
||||||
return;
|
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) {
|
if (ret != SQLITE_OK) {
|
||||||
throw_sqlite_error(ret, sqlite_db_, "open");
|
throw_sqlite_error(ret, db_, "open");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void sqlite_connection::close()
|
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()
|
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)
|
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;
|
fetch_context context;
|
||||||
char *errmsg = nullptr;
|
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;
|
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)
|
size_t sqlite_connection::execute(const std::string &stmt)
|
||||||
{
|
{
|
||||||
char *errmsg = nullptr;
|
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)
|
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)));
|
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)
|
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 + ")");
|
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;
|
sql::record prototype;
|
||||||
while (reader.fetch()) {
|
while (reader.fetch()) {
|
||||||
char *end = nullptr;
|
char *end = nullptr;
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,19 @@
|
||||||
#include "sqlite_dialect.hpp"
|
#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;
|
using namespace matador::sql;
|
||||||
const static dialect d{{
|
const static dialect d = dialect_builder::builder()
|
||||||
|
.create()
|
||||||
|
.with_token_replace_map({
|
||||||
{dialect::token_t::BEGIN, "BEGIN TRANSACTION"},
|
{dialect::token_t::BEGIN, "BEGIN TRANSACTION"},
|
||||||
{dialect::token_t::COMMIT, "COMMIT TRANSACTION"},
|
{dialect::token_t::COMMIT, "COMMIT TRANSACTION"},
|
||||||
{dialect::token_t::ROLLBACK, "ROLLBACK TRANSACTION"}
|
{dialect::token_t::ROLLBACK, "ROLLBACK TRANSACTION"}
|
||||||
}};
|
})
|
||||||
|
.with_default_schema_name("main")
|
||||||
|
.build();
|
||||||
|
|
||||||
return &d;
|
return &d;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ namespace matador::backends::sqlite {
|
||||||
|
|
||||||
void throw_sqlite_error(int ec, sqlite3 *db, const std::string &source)
|
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;
|
std::stringstream msg;
|
||||||
msg << "sqlite error (" << source << "): " << sqlite3_errmsg(db);
|
msg << "sqlite error (" << source << "): " << sqlite3_errmsg(db);
|
||||||
throw std::logic_error(msg.str());
|
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)
|
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;
|
std::stringstream msg;
|
||||||
msg << "sqlite error (" << source << ", sql: " << sql << "): " << sqlite3_errmsg(db);
|
msg << "sqlite error (" << source << ", sql: " << sql << "): " << sqlite3_errmsg(db);
|
||||||
throw std::logic_error(msg.str());
|
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
|
#ifndef QUERY_ANY_TYPE_HPP
|
||||||
#define QUERY_ANY_TYPE_HPP
|
#define QUERY_ANY_TYPE_HPP
|
||||||
|
|
||||||
|
#include "matador/sql/placeholder.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
|
|
@ -13,6 +15,7 @@ using any_type = std::variant<
|
||||||
bool,
|
bool,
|
||||||
const char*,
|
const char*,
|
||||||
std::string,
|
std::string,
|
||||||
|
placeholder,
|
||||||
nullptr_t>;
|
nullptr_t>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@
|
||||||
|
|
||||||
namespace matador::sql {
|
namespace matador::sql {
|
||||||
|
|
||||||
|
struct placeholder;
|
||||||
|
|
||||||
template < typename DestType, typename SourceType >
|
template < typename DestType, typename SourceType >
|
||||||
void convert(DestType &dest, SourceType source, typename std::enable_if<std::is_same<DestType, SourceType>::value>::type* = nullptr)
|
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()(double &x) { convert(result, x); }
|
||||||
void operator()(const char *x) { convert(result, x); }
|
void operator()(const char *x) { convert(result, x); }
|
||||||
void operator()(std::string &x) { convert(result, x); }
|
void operator()(std::string &x) { convert(result, x); }
|
||||||
|
void operator()(placeholder &/*x*/) {}
|
||||||
|
|
||||||
Type result{};
|
Type result{};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
namespace matador::sql {
|
namespace matador::sql {
|
||||||
|
|
||||||
class dialect;
|
class dialect;
|
||||||
|
class query_context;
|
||||||
|
|
||||||
class basic_condition
|
class basic_condition
|
||||||
{
|
{
|
||||||
|
|
@ -32,7 +33,7 @@ public:
|
||||||
LIKE
|
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;
|
static std::unordered_map<operand_t, std::string> operands;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,13 @@
|
||||||
|
|
||||||
#include "matador/sql/basic_condition.hpp"
|
#include "matador/sql/basic_condition.hpp"
|
||||||
#include "matador/sql/dialect.hpp"
|
#include "matador/sql/dialect.hpp"
|
||||||
|
#include "matador/sql/placeholder.hpp"
|
||||||
|
#include "matador/sql/query_context.hpp"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace matador::sql {
|
namespace matador::sql {
|
||||||
|
|
||||||
struct placeholder {};
|
|
||||||
|
|
||||||
const placeholder _;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class condition
|
* @class condition
|
||||||
* @brief Represents a sql query condition
|
* @brief Represents a sql query condition
|
||||||
|
|
@ -27,8 +25,6 @@ const placeholder _;
|
||||||
|
|
||||||
/// @cond MATADOR_DEV
|
/// @cond MATADOR_DEV
|
||||||
|
|
||||||
class query;
|
|
||||||
|
|
||||||
template<class L, class R, class Enabled = void>
|
template<class L, class R, class Enabled = void>
|
||||||
class condition;
|
class condition;
|
||||||
|
|
||||||
|
|
@ -40,7 +36,7 @@ public:
|
||||||
|
|
||||||
placeholder value;
|
placeholder value;
|
||||||
|
|
||||||
std::string evaluate(dialect &d) const override;
|
std::string evaluate(dialect &d, query_context &query) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
|
|
@ -57,9 +53,9 @@ public:
|
||||||
|
|
||||||
T value;
|
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);
|
return d.prepare_identifier(field_.name()) + " " + operand + " " + std::to_string(value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -77,9 +73,9 @@ public:
|
||||||
|
|
||||||
T value;
|
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 + "'";
|
return d.prepare_identifier(field_.name()) + " " + operand + " '" + value + "'";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -98,7 +94,7 @@ public:
|
||||||
|
|
||||||
T value;
|
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());
|
return std::to_string(value) + " " + operand + " " + d.prepare_identifier(field_.name());
|
||||||
}
|
}
|
||||||
|
|
@ -117,7 +113,7 @@ public:
|
||||||
|
|
||||||
T value;
|
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());
|
return "'" + std::to_string(value) + "' " + operand + " " + d.prepare_identifier(field_.name());
|
||||||
}
|
}
|
||||||
|
|
@ -159,13 +155,12 @@ public:
|
||||||
* @param d The d used to evaluate
|
* @param d The d used to evaluate
|
||||||
* @return A condition IN part of the query
|
* @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();
|
auto count = size();
|
||||||
for (size_t i = 0; i < count; ++i) {
|
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 (";
|
std::string result = d.prepare_identifier(field_.name()) + " IN (";
|
||||||
if (args_.size() < 2) {
|
if (args_.size() < 2) {
|
||||||
for (const auto &val : args_) {
|
for (const auto &val : args_) {
|
||||||
|
|
@ -205,7 +200,7 @@ private:
|
||||||
* @endcode
|
* @endcode
|
||||||
*/
|
*/
|
||||||
template <>
|
template <>
|
||||||
class condition<column, query> : public basic_column_condition
|
class condition<column, query_context> : public basic_column_condition
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
|
|
@ -219,7 +214,7 @@ public:
|
||||||
* @param op Operand of the condition
|
* @param op Operand of the condition
|
||||||
* @param q The query to be evaluated to the IN arguments
|
* @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
|
* @brief Evaluates the condition
|
||||||
|
|
@ -230,10 +225,10 @@ public:
|
||||||
* @param d The d used to evaluate
|
* @param d The d used to evaluate
|
||||||
* @return A condition IN part of the query
|
* @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:
|
private:
|
||||||
query &query_;
|
query_context &query_;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -265,9 +260,9 @@ public:
|
||||||
* @param d The d used to evaluate
|
* @param d The d used to evaluate
|
||||||
* @return A condition BETWEEN part of the query
|
* @return A condition BETWEEN part of the query
|
||||||
*/
|
*/
|
||||||
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());
|
||||||
d.add_host_var(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);
|
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
|
* @param d The d used to evaluate
|
||||||
* @return The evaluated string based on the compile type
|
* @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
|
// ensure the numbering order for host vars
|
||||||
auto cl = left.evaluate(d);
|
auto cl = left.evaluate(d, query);
|
||||||
auto cr = right.evaluate(d);
|
auto cr = right.evaluate(d, query);
|
||||||
if (operand == basic_condition::operand_t::AND) {
|
if (operand == basic_condition::operand_t::AND) {
|
||||||
return "(" + cl + " " + basic_condition::operands[operand] + " " + cr + ")";
|
return "(" + cl + " " + basic_condition::operands[operand] + " " + cr + ")";
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -350,7 +345,7 @@ public:
|
||||||
* @param d The d used to evaluate
|
* @param d The d used to evaluate
|
||||||
* @return The evaluated string based on the compile type
|
* @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) + ")";
|
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
|
* @param q The query to be executes as sub select
|
||||||
* @return The condition object
|
* @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.
|
* @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
|
* @param q The query to compare with
|
||||||
* @return The condition object representing the equality operation
|
* @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
|
* @brief Condition inequality operator for a column and a value
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,10 @@
|
||||||
#include "matador/sql/connection_info.hpp"
|
#include "matador/sql/connection_info.hpp"
|
||||||
#include "matador/sql/connection_impl.hpp"
|
#include "matador/sql/connection_impl.hpp"
|
||||||
#include "matador/sql/dialect.hpp"
|
#include "matador/sql/dialect.hpp"
|
||||||
|
#include "matador/sql/query_context.hpp"
|
||||||
#include "matador/sql/query_result.hpp"
|
#include "matador/sql/query_result.hpp"
|
||||||
#include "matador/sql/record.hpp"
|
#include "matador/sql/record.hpp"
|
||||||
|
#include "matador/sql/statement.hpp"
|
||||||
|
|
||||||
#include "matador/utils/logger.hpp"
|
#include "matador/utils/logger.hpp"
|
||||||
|
|
||||||
|
|
@ -13,8 +15,6 @@
|
||||||
|
|
||||||
namespace matador::sql {
|
namespace matador::sql {
|
||||||
|
|
||||||
class connection_impl;
|
|
||||||
|
|
||||||
class connection
|
class connection
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -37,6 +37,8 @@ public:
|
||||||
[[nodiscard]] std::unique_ptr<query_result_impl> fetch(const std::string &sql) const;
|
[[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;
|
[[nodiscard]] std::pair<size_t, std::string> execute(const std::string &sql) const;
|
||||||
|
|
||||||
|
statement prepare(query_context &&query) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
connection_info connection_info_;
|
connection_info connection_info_;
|
||||||
std::unique_ptr<connection_impl> connection_;
|
std::unique_ptr<connection_impl> connection_;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,9 @@
|
||||||
|
|
||||||
#include "matador/sql/connection_info.hpp"
|
#include "matador/sql/connection_info.hpp"
|
||||||
#include "matador/sql/query_result_impl.hpp"
|
#include "matador/sql/query_result_impl.hpp"
|
||||||
|
#include "matador/sql/query_context.hpp"
|
||||||
#include "matador/sql/record.hpp"
|
#include "matador/sql/record.hpp"
|
||||||
|
#include "matador/sql/statement_impl.hpp"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
|
@ -22,7 +24,7 @@ public:
|
||||||
|
|
||||||
virtual size_t execute(const std::string &stmt) = 0;
|
virtual size_t execute(const std::string &stmt) = 0;
|
||||||
virtual std::unique_ptr<query_result_impl> fetch(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 record describe(const std::string &table) = 0;
|
||||||
virtual bool exists(const std::string &table_name) = 0;
|
virtual bool exists(const std::string &table_name) = 0;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
#include "matador/sql/types.hpp"
|
#include "matador/sql/types.hpp"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
@ -12,7 +13,7 @@ namespace matador::sql {
|
||||||
|
|
||||||
class column;
|
class column;
|
||||||
|
|
||||||
class dialect
|
class dialect final
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum class token_t : uint8_t
|
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 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>;
|
using sql_func_to_string_map = std::unordered_map<sql_function_t, std::string>;
|
||||||
|
|
||||||
public:
|
using next_placeholder_func = std::function<std::string(size_t)>;
|
||||||
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);
|
|
||||||
|
|
||||||
const std::string& token_at(token_t token) const;
|
public:
|
||||||
const std::string& data_type_at(data_type_t type) const;
|
[[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
|
* Prepare sql dialect identifier for execution
|
||||||
|
|
@ -82,14 +80,14 @@ public:
|
||||||
* @param str The identifier string to be prepared
|
* @param str The identifier string to be prepared
|
||||||
* @return The prepared string
|
* @return The prepared string
|
||||||
*/
|
*/
|
||||||
std::string prepare_identifier(const column &col) const;
|
[[nodiscard]] std::string prepare_identifier(const column &col) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare string literal
|
* Prepare string literal
|
||||||
*
|
*
|
||||||
* @param str String literal to be prepared
|
* @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
|
* Wrap identifier quotes around a sql identifier keyword
|
||||||
|
|
@ -118,7 +116,7 @@ public:
|
||||||
*
|
*
|
||||||
* @return How the identifier quotes should be escaped
|
* @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
|
* Generates a next placeholder string. default is
|
||||||
|
|
@ -126,17 +124,24 @@ public:
|
||||||
*
|
*
|
||||||
* @return Placeholder string
|
* @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);
|
* Returns the default schema name.
|
||||||
|
*
|
||||||
const std::vector<std::string>& host_vars() const;
|
* @return The default schema name.
|
||||||
const std::vector<std::string>& columns() const;
|
*/
|
||||||
|
[[nodiscard]] std::string default_schema_name() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<std::string> host_vars_;
|
dialect() = default;
|
||||||
std::vector<std::string> columns_;
|
|
||||||
|
private:
|
||||||
|
friend class dialect_builder;
|
||||||
|
|
||||||
|
next_placeholder_func placeholder_func_ = [](size_t) { return "?"; };
|
||||||
|
|
||||||
|
std::string default_schema_name_;
|
||||||
|
|
||||||
token_to_string_map tokens_ {
|
token_to_string_map tokens_ {
|
||||||
{token_t::CREATE, "CREATE"},
|
{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/basic_condition.hpp"
|
||||||
#include "matador/sql/column.hpp"
|
#include "matador/sql/column.hpp"
|
||||||
#include "matador/sql/key_value_pair.hpp"
|
#include "matador/sql/key_value_pair.hpp"
|
||||||
|
#include "matador/sql/query_context.hpp"
|
||||||
#include "matador/sql/record.hpp"
|
#include "matador/sql/record.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
@ -18,7 +19,7 @@ class dialect;
|
||||||
namespace detail {
|
namespace detail {
|
||||||
struct any_type_to_string_visitor
|
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()(char &x) { to_string(x); }
|
||||||
void operator()(short &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()(double &x) { to_string(x); }
|
||||||
void operator()(const char *x) { to_string(x); }
|
void operator()(const char *x) { to_string(x); }
|
||||||
void operator()(std::string &x) { to_string(x); }
|
void operator()(std::string &x) { to_string(x); }
|
||||||
|
void operator()(placeholder &x) { to_string(x); }
|
||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
void to_string(Type &val)
|
void to_string(Type &val)
|
||||||
|
|
@ -43,8 +45,10 @@ struct any_type_to_string_visitor
|
||||||
}
|
}
|
||||||
void to_string(const char *val);
|
void to_string(const char *val);
|
||||||
void to_string(std::string &val);
|
void to_string(std::string &val);
|
||||||
|
void to_string(placeholder &val);
|
||||||
|
|
||||||
const dialect &d;
|
const dialect &d;
|
||||||
|
query_context &query;
|
||||||
std::string result;
|
std::string result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -59,14 +63,6 @@ enum class join_type_t {
|
||||||
INNER, OUTER, LEFT, RIGHT
|
INNER, OUTER, LEFT, RIGHT
|
||||||
};
|
};
|
||||||
|
|
||||||
struct query
|
|
||||||
{
|
|
||||||
std::string sql;
|
|
||||||
std::string table_name;
|
|
||||||
record prototype;
|
|
||||||
std::vector<any_type> host_vars;
|
|
||||||
};
|
|
||||||
|
|
||||||
class query_builder
|
class query_builder
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
@ -134,7 +130,7 @@ public:
|
||||||
query_builder& offset(size_t count);
|
query_builder& offset(size_t count);
|
||||||
query_builder& limit(size_t count);
|
query_builder& limit(size_t count);
|
||||||
|
|
||||||
query compile();
|
query_context compile();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void transition_to(state_t next);
|
void transition_to(state_t next);
|
||||||
|
|
@ -150,7 +146,7 @@ private:
|
||||||
|
|
||||||
detail::any_type_to_string_visitor value_to_string_;
|
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_set = std::unordered_set<state_t>;
|
||||||
using query_state_transition_map = std::unordered_map<state_t, query_state_set>;
|
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/column_name_generator.hpp"
|
||||||
#include "matador/sql/key_value_generator.hpp"
|
#include "matador/sql/key_value_generator.hpp"
|
||||||
#include "matador/sql/key_value_pair.hpp"
|
#include "matador/sql/key_value_pair.hpp"
|
||||||
|
#include "matador/sql/placeholder_generator.hpp"
|
||||||
#include "matador/sql/query_builder.hpp"
|
#include "matador/sql/query_builder.hpp"
|
||||||
#include "matador/sql/query_result.hpp"
|
#include "matador/sql/query_result.hpp"
|
||||||
#include "matador/sql/record.hpp"
|
#include "matador/sql/record.hpp"
|
||||||
|
#include "matador/sql/statement.hpp"
|
||||||
#include "matador/sql/table_repository.hpp"
|
#include "matador/sql/table_repository.hpp"
|
||||||
#include "matador/sql/value_extractor.hpp"
|
#include "matador/sql/value_extractor.hpp"
|
||||||
|
|
||||||
|
|
@ -35,6 +37,7 @@ public:
|
||||||
using query_intermediate::query_intermediate;
|
using query_intermediate::query_intermediate;
|
||||||
|
|
||||||
std::pair<size_t, std::string> execute();
|
std::pair<size_t, std::string> execute();
|
||||||
|
statement prepare();
|
||||||
};
|
};
|
||||||
|
|
||||||
class query_select_finish : public query_intermediate
|
class query_select_finish : public query_intermediate
|
||||||
|
|
@ -57,6 +60,8 @@ public:
|
||||||
return fetch_one().at(0).as<Type>();
|
return fetch_one().at(0).as<Type>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
statement prepare();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<query_result_impl> fetch();
|
std::unique_ptr<query_result_impl> fetch();
|
||||||
};
|
};
|
||||||
|
|
@ -140,6 +145,15 @@ public:
|
||||||
query_from_intermediate from(const std::string &table, const std::string &as = "");
|
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
|
class query_into_intermediate : public query_intermediate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -147,6 +161,12 @@ public:
|
||||||
|
|
||||||
query_execute_finish values(std::initializer_list<any_type> values);
|
query_execute_finish values(std::initializer_list<any_type> values);
|
||||||
template<class Type>
|
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)
|
query_execute_finish values(const Type &obj)
|
||||||
{
|
{
|
||||||
return {session_, builder_.values(value_extractor::extract(obj))};
|
return {session_, builder_.values(value_extractor::extract(obj))};
|
||||||
|
|
@ -216,7 +236,7 @@ public:
|
||||||
class query_update_intermediate : public query_start_intermediate
|
class query_update_intermediate : public query_start_intermediate
|
||||||
{
|
{
|
||||||
public:
|
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);
|
query_set_intermediate set(std::initializer_list<key_value_pair> columns);
|
||||||
template<class Type>
|
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/connection_pool.hpp"
|
||||||
#include "matador/sql/query_builder.hpp"
|
#include "matador/sql/query_builder.hpp"
|
||||||
#include "matador/sql/query_intermediates.hpp"
|
#include "matador/sql/query_intermediates.hpp"
|
||||||
|
#include "matador/sql/statement.hpp"
|
||||||
#include "matador/sql/table_repository.hpp"
|
#include "matador/sql/table_repository.hpp"
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
@ -22,18 +23,16 @@ public:
|
||||||
query_create_intermediate create();
|
query_create_intermediate create();
|
||||||
query_drop_intermediate drop();
|
query_drop_intermediate drop();
|
||||||
template < class Type >
|
template < class Type >
|
||||||
query_select_intermediate select()
|
query_select_intermediate select();
|
||||||
{
|
|
||||||
return query_select_intermediate{*this, column_generator::generate<Type>(table_repository_)};
|
|
||||||
}
|
|
||||||
query_select_intermediate select(std::initializer_list<column> columns);
|
query_select_intermediate select(std::initializer_list<column> columns);
|
||||||
query_insert_intermediate insert();
|
query_insert_intermediate insert();
|
||||||
query_update_intermediate update(const std::string &table);
|
query_update_intermediate update(const std::string &table);
|
||||||
query_delete_intermediate remove();
|
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]] query_result<record> fetch(const std::string &sql) const;
|
||||||
[[nodiscard]] std::pair<size_t, std::string> execute(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;
|
record describe_table(const std::string &table_name) const;
|
||||||
bool table_exists(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_;
|
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
|
#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/table_repository.cpp
|
||||||
sql/any_type_to_visitor.cpp
|
sql/any_type_to_visitor.cpp
|
||||||
sql/query_result.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
|
set(SQL_HEADER
|
||||||
../include/matador/sql/dialect.hpp
|
../include/matador/sql/dialect.hpp
|
||||||
|
|
@ -31,7 +37,6 @@ set(SQL_HEADER
|
||||||
../include/matador/sql/condition.hpp
|
../include/matador/sql/condition.hpp
|
||||||
../include/matador/sql/connection.hpp
|
../include/matador/sql/connection.hpp
|
||||||
../include/matador/sql/query_intermediates.hpp
|
../include/matador/sql/query_intermediates.hpp
|
||||||
../include/matador/sql/result.hpp
|
|
||||||
../include/matador/sql/record.hpp
|
../include/matador/sql/record.hpp
|
||||||
../include/matador/sql/query_result.hpp
|
../include/matador/sql/query_result.hpp
|
||||||
../include/matador/sql/connection_impl.hpp
|
../include/matador/sql/connection_impl.hpp
|
||||||
|
|
@ -50,7 +55,17 @@ set(SQL_HEADER
|
||||||
../include/matador/sql/table_repository.hpp
|
../include/matador/sql/table_repository.hpp
|
||||||
../include/matador/sql/any_type_to_visitor.hpp
|
../include/matador/sql/any_type_to_visitor.hpp
|
||||||
../include/matador/sql/query_result_reader.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
|
set(UTILS_HEADER
|
||||||
../include/matador/utils/field_attributes.hpp
|
../include/matador/utils/field_attributes.hpp
|
||||||
|
|
|
||||||
|
|
@ -3,21 +3,21 @@
|
||||||
namespace matador::sql {
|
namespace matador::sql {
|
||||||
|
|
||||||
condition<column, placeholder, std::enable_if<true>::type>::condition(const column &fld, basic_condition::operand_t op, const placeholder &val)
|
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)
|
: basic_column_condition(fld, op), value(val)
|
||||||
, value(val)
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
std::string condition<column, placeholder, std::enable_if<true>::type>::evaluate(dialect &d) const {
|
std::string condition<column, placeholder, std::enable_if<true>::type>::evaluate(dialect &d, query_context &query) const
|
||||||
d.add_host_var(field_.name());
|
{
|
||||||
return d.prepare_identifier(field_.name()) + " " + operand + " " + d.next_placeholder();
|
query.bind_vars.emplace_back(field_.name());
|
||||||
|
return d.prepare_identifier(field_.name()) + " " + operand + " " + d.next_placeholder(query.bind_vars);
|
||||||
}
|
}
|
||||||
|
|
||||||
condition<column, query>::condition(column col, basic_condition::operand_t op, query &q)
|
condition<column, query_context>::condition(column col, basic_condition::operand_t op, query_context &q)
|
||||||
: basic_column_condition(std::move(col), op)
|
: basic_column_condition(std::move(col), op), query_(q)
|
||||||
, query_(q)
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
std::string condition<column, query>::evaluate(dialect &d) const {
|
std::string condition<column, query_context>::evaluate(dialect &d, query_context &query) const
|
||||||
|
{
|
||||||
std::string result(d.prepare_identifier(field_.name()) + " " + operand + " (");
|
std::string result(d.prepare_identifier(field_.name()) + " " + operand + " (");
|
||||||
result += (")");
|
result += (")");
|
||||||
return result;
|
return result;
|
||||||
|
|
|
||||||
|
|
@ -88,4 +88,9 @@ std::unique_ptr<query_result_impl> connection::fetch(const std::string &sql) con
|
||||||
return connection_->fetch(sql);
|
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 {
|
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
|
const std::string& dialect::token_at(dialect::token_t token) const
|
||||||
{
|
{
|
||||||
return tokens_.at(token);
|
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;
|
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);
|
return default_schema_name_;
|
||||||
}
|
|
||||||
|
|
||||||
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_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -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 {
|
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)
|
void any_type_to_string_visitor::to_string(const char *val)
|
||||||
{
|
{
|
||||||
result = "'" + d.prepare_literal(val) + "'";
|
result = "'" + d.prepare_literal(val) + "'";
|
||||||
|
|
@ -19,6 +22,12 @@ void any_type_to_string_visitor::to_string(std::string &val)
|
||||||
result = "'" + d.prepare_literal(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
|
// poor mens state machine
|
||||||
|
|
@ -78,10 +87,11 @@ query_builder::query_command_to_string_map query_builder::command_strings_ {
|
||||||
};
|
};
|
||||||
|
|
||||||
query_builder::query_builder(const dialect &d)
|
query_builder::query_builder(const dialect &d)
|
||||||
: dialect_(d)
|
: dialect_(d), value_to_string_(d, query_)
|
||||||
, value_to_string_(d) {}
|
{}
|
||||||
|
|
||||||
query_builder& query_builder::create() {
|
query_builder &query_builder::create()
|
||||||
|
{
|
||||||
initialize(command_t::CREATE, state_t::QUERY_CREATE);
|
initialize(command_t::CREATE, state_t::QUERY_CREATE);
|
||||||
|
|
||||||
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::CREATE));
|
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::CREATE));
|
||||||
|
|
@ -89,7 +99,8 @@ query_builder& query_builder::create() {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
query_builder& query_builder::drop() {
|
query_builder &query_builder::drop()
|
||||||
|
{
|
||||||
initialize(command_t::DROP, state_t::QUERY_DROP);
|
initialize(command_t::DROP, state_t::QUERY_DROP);
|
||||||
|
|
||||||
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::DROP));
|
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::DROP));
|
||||||
|
|
@ -131,7 +142,8 @@ query_builder &query_builder::select(const std::vector<column> &columns)
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
query_builder& query_builder::insert() {
|
query_builder &query_builder::insert()
|
||||||
|
{
|
||||||
initialize(command_t::INSERT, state_t::QUERY_INSERT);
|
initialize(command_t::INSERT, state_t::QUERY_INSERT);
|
||||||
|
|
||||||
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::INSERT));
|
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::INSERT));
|
||||||
|
|
@ -139,7 +151,8 @@ query_builder& query_builder::insert() {
|
||||||
return *this;
|
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);
|
initialize(command_t::UPDATE, state_t::QUERY_UPDATE);
|
||||||
|
|
||||||
query_.table_name = table;
|
query_.table_name = table;
|
||||||
|
|
@ -148,7 +161,8 @@ query_builder& query_builder::update(const std::string &table) {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
query_builder& query_builder::remove() {
|
query_builder &query_builder::remove()
|
||||||
|
{
|
||||||
initialize(command_t::REMOVE, state_t::QUERY_DELETE);
|
initialize(command_t::REMOVE, state_t::QUERY_DELETE);
|
||||||
|
|
||||||
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::REMOVE));
|
query_parts_.emplace_back(dialect_.token_at(dialect::token_t::REMOVE));
|
||||||
|
|
@ -176,7 +190,8 @@ struct column_context
|
||||||
|
|
||||||
std::string build_create_column(const column &col, const dialect &d, column_context &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);
|
transition_to(state_t::QUERY_TABLE_CREATE);
|
||||||
|
|
||||||
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(table) + " ");
|
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(table) + " ");
|
||||||
|
|
@ -214,7 +229,8 @@ query_builder &query_builder::table(const std::string &table, const std::vector<
|
||||||
return *this;
|
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);
|
transition_to(state_t::QUERY_TABLE_DROP);
|
||||||
|
|
||||||
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(table));
|
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::TABLE) + " " + dialect_.prepare_identifier(table));
|
||||||
|
|
@ -259,7 +275,8 @@ query_builder& query_builder::values(std::initializer_list<any_type> values)
|
||||||
return this->values(std::vector<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);
|
transition_to(state_t::QUERY_VALUES);
|
||||||
|
|
||||||
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::VALUES) + " ");
|
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::VALUES) + " ");
|
||||||
|
|
@ -291,10 +308,20 @@ query_builder& query_builder::values(const std::vector<any_type> &values) {
|
||||||
return *this;
|
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);
|
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;
|
query_.table_name = table;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
|
|
@ -315,7 +342,8 @@ query_builder &query_builder::set(std::initializer_list<key_value_pair> key_valu
|
||||||
return set(std::vector<key_value_pair>{key_values});
|
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);
|
transition_to(state_t::QUERY_SET);
|
||||||
|
|
||||||
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::SET) + " ");
|
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::SET) + " ");
|
||||||
|
|
@ -347,16 +375,18 @@ query_builder& query_builder::set(const std::vector<key_value_pair> &key_values)
|
||||||
return *this;
|
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);
|
transition_to(state_t::QUERY_WHERE);
|
||||||
|
|
||||||
query_parts_.emplace_back(" " + dialect_.token_at(dialect::token_t::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;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
query_builder& query_builder::order_by(const std::string& column) {
|
query_builder &query_builder::order_by(const std::string &column)
|
||||||
|
{
|
||||||
transition_to(state_t::QUERY_ORDER_BY);
|
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));
|
||||||
|
|
@ -364,7 +394,8 @@ query_builder& query_builder::order_by(const std::string& column) {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
query_builder& query_builder::group_by(const std::string& column) {
|
query_builder &query_builder::group_by(const std::string &column)
|
||||||
|
{
|
||||||
transition_to(state_t::QUERY_GROUP_BY);
|
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));
|
||||||
|
|
@ -372,7 +403,8 @@ query_builder& query_builder::group_by(const std::string& column) {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
query_builder& query_builder::asc() {
|
query_builder &query_builder::asc()
|
||||||
|
{
|
||||||
transition_to(state_t::QUERY_ORDER_DIRECTION);
|
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));
|
||||||
|
|
@ -380,7 +412,8 @@ query_builder& query_builder::asc() {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
query_builder& query_builder::desc() {
|
query_builder &query_builder::desc()
|
||||||
|
{
|
||||||
transition_to(state_t::QUERY_ORDER_DIRECTION);
|
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));
|
||||||
|
|
@ -388,7 +421,8 @@ query_builder& query_builder::desc() {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
query_builder& query_builder::offset( size_t count ) {
|
query_builder &query_builder::offset(size_t count)
|
||||||
|
{
|
||||||
transition_to(state_t::QUERY_OFFSET);
|
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));
|
||||||
|
|
@ -396,7 +430,8 @@ query_builder& query_builder::offset( size_t count ) {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
query_builder& query_builder::limit( size_t count ) {
|
query_builder &query_builder::limit(size_t count)
|
||||||
|
{
|
||||||
transition_to(state_t::QUERY_LIMIT);
|
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));
|
||||||
|
|
@ -404,10 +439,12 @@ query_builder& query_builder::limit( size_t count ) {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
query query_builder::compile() {
|
query_context query_builder::compile()
|
||||||
|
{
|
||||||
for (const auto &part: query_parts_) {
|
for (const auto &part: query_parts_) {
|
||||||
query_.sql.append(part);
|
query_.sql.append(part);
|
||||||
}
|
}
|
||||||
|
query_.command_name = command_strings_[command_];
|
||||||
return query_;
|
return query_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -427,7 +464,8 @@ void query_builder::initialize(query_builder::command_t cmd, query_builder::stat
|
||||||
query_parts_.clear();
|
query_parts_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string build_create_column(const column &col, const dialect &d, column_context &context) {
|
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());
|
std::string result = d.prepare_identifier(col.name()) + " " + d.data_type_at(col.type());
|
||||||
if (col.attributes().size() > 0) {
|
if (col.attributes().size() > 0) {
|
||||||
result.append("(" + std::to_string(col.attributes().size()) + ")");
|
result.append("(" + std::to_string(col.attributes().size()) + ")");
|
||||||
|
|
@ -450,7 +488,8 @@ column alias(const std::string &column, const std::string &as)
|
||||||
return {column, as};
|
return {column, as};
|
||||||
}
|
}
|
||||||
|
|
||||||
column alias(column &&col, const std::string &as) {
|
column alias(column &&col, const std::string &as)
|
||||||
|
{
|
||||||
col.alias(as);
|
col.alias(as);
|
||||||
return std::move(col);
|
return std::move(col);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,11 @@ std::unique_ptr<query_result_impl> query_select_finish::fetch()
|
||||||
return session_.fetch(builder_.compile().sql);
|
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)
|
query_intermediate::query_intermediate(session &db, query_builder &query)
|
||||||
: session_(db), 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);
|
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)
|
query_execute_finish query_into_intermediate::values(std::initializer_list<any_type> values)
|
||||||
{
|
{
|
||||||
return {session_, builder_.values(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)};
|
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)
|
: 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)
|
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};
|
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();
|
auto c = pool_.acquire();
|
||||||
if (!c.valid()) {
|
if (!c.valid()) {
|
||||||
|
|
@ -73,6 +73,15 @@ std::pair<size_t, std::string> session::execute(const std::string &sql) const {
|
||||||
return c->execute(sql);
|
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
|
record session::describe_table(const std::string &table_name) const
|
||||||
{
|
{
|
||||||
auto c = pool_.acquire();
|
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
|
models/person.hpp
|
||||||
AnyTypeToVisitorTest.cpp
|
AnyTypeToVisitorTest.cpp
|
||||||
ColumnTest.cpp
|
ColumnTest.cpp
|
||||||
SessionRecordTest.cpp)
|
SessionRecordTest.cpp
|
||||||
|
StatementCacheTest.cpp
|
||||||
|
StatementTest.cpp)
|
||||||
target_link_libraries(tests PRIVATE
|
target_link_libraries(tests PRIVATE
|
||||||
Catch2::Catch2WithMain
|
Catch2::Catch2WithMain
|
||||||
matador
|
matador
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
#include <matador/sql/column.hpp>
|
#include <matador/sql/column.hpp>
|
||||||
#include <matador/sql/condition.hpp>
|
#include <matador/sql/condition.hpp>
|
||||||
#include <matador/sql/dialect.hpp>
|
#include <matador/sql/dialect_builder.hpp>
|
||||||
#include <matador/sql/query_builder.hpp>
|
#include <matador/sql/query_builder.hpp>
|
||||||
|
|
||||||
using namespace matador::sql;
|
using namespace matador::sql;
|
||||||
|
|
||||||
TEST_CASE("Create table sql statement string", "[query]") {
|
TEST_CASE("Create table sql statement string", "[query]") {
|
||||||
dialect d;
|
dialect d = dialect_builder::builder().create().build();
|
||||||
query_builder query(d);
|
query_builder query(d);
|
||||||
auto q = query.create().table("person", {
|
auto q = query.create().table("person", {
|
||||||
make_pk_column<unsigned long>("id"),
|
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]") {
|
TEST_CASE("Drop table sql statement string", "[query]") {
|
||||||
dialect d;
|
dialect d = dialect_builder::builder().create().build();
|
||||||
query_builder query(d);
|
query_builder query(d);
|
||||||
const auto q = query.drop().table("person").compile();
|
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]") {
|
TEST_CASE("Select sql statement string", "[query]") {
|
||||||
dialect d;
|
dialect d = dialect_builder::builder().create().build();
|
||||||
query_builder query(d);
|
query_builder query(d);
|
||||||
const auto q = query.select({"id", "name", "age"}).from("person").compile();
|
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]") {
|
TEST_CASE("Insert sql statement string", "[query]") {
|
||||||
dialect d;
|
dialect d = dialect_builder::builder().create().build();
|
||||||
query_builder query(d);
|
query_builder query(d);
|
||||||
const auto q = query.insert().into("person", {
|
const auto q = query.insert().into("person", {
|
||||||
"id", "name", "age"
|
"id", "name", "age"
|
||||||
|
|
@ -60,7 +60,7 @@ TEST_CASE("Insert sql statement string", "[query]") {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Update sql statement string", "[query]") {
|
TEST_CASE("Update sql statement string", "[query]") {
|
||||||
dialect d;
|
dialect d = dialect_builder::builder().create().build();
|
||||||
query_builder query(d);
|
query_builder query(d);
|
||||||
const auto q = query.update("person").set({
|
const auto q = query.update("person").set({
|
||||||
{"id", 7UL},
|
{"id", 7UL},
|
||||||
|
|
@ -73,7 +73,7 @@ TEST_CASE("Update sql statement string", "[query]") {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Delete sql statement string", "[query]") {
|
TEST_CASE("Delete sql statement string", "[query]") {
|
||||||
dialect d;
|
dialect d = dialect_builder::builder().create().build();
|
||||||
query_builder query(d);
|
query_builder query(d);
|
||||||
const auto q = query.remove().from("person").compile();
|
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]") {
|
TEST_CASE("Select sql statement string with where clause", "[query]") {
|
||||||
dialect d;
|
dialect d = dialect_builder::builder().create().build();
|
||||||
query_builder query(d);
|
query_builder query(d);
|
||||||
auto q = query.select({"id", "name", "age"})
|
auto q = query.select({"id", "name", "age"})
|
||||||
.from("person")
|
.from("person")
|
||||||
|
|
@ -101,8 +101,20 @@ TEST_CASE("Select sql statement string with where clause", "[query]") {
|
||||||
REQUIRE(q.table_name == "person");
|
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]") {
|
TEST_CASE("Select sql statement string with order by", "[query]") {
|
||||||
dialect d;
|
dialect d = dialect_builder::builder().create().build();
|
||||||
query_builder query(d);
|
query_builder query(d);
|
||||||
const auto q = query.select({"id", "name", "age"})
|
const auto q = query.select({"id", "name", "age"})
|
||||||
.from("person")
|
.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]") {
|
TEST_CASE("Select sql statement string with group by", "[query]") {
|
||||||
dialect d;
|
dialect d = dialect_builder::builder().create().build();
|
||||||
query_builder query(d);
|
query_builder query(d);
|
||||||
const auto q = query.select({"id", "name", "age"})
|
const auto q = query.select({"id", "name", "age"})
|
||||||
.from("person")
|
.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]") {
|
TEST_CASE("Select sql statement string with offset and limit", "[query]") {
|
||||||
dialect d;
|
dialect d = dialect_builder::builder().create().build();
|
||||||
query_builder query(d);
|
query_builder query(d);
|
||||||
const auto q = query.select({"id", "name", "age"})
|
const auto q = query.select({"id", "name", "age"})
|
||||||
.from("person")
|
.from("person")
|
||||||
|
|
|
||||||
|
|
@ -365,7 +365,7 @@ TEST_CASE("Execute delete statement", "[session record]") {
|
||||||
.where("id"_col == 1)
|
.where("id"_col == 1)
|
||||||
.execute();
|
.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);
|
REQUIRE(res.first == 1);
|
||||||
|
|
||||||
count = s.select({count_all()}).from("person").fetch_value<int>();
|
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