added mysql backend

This commit is contained in:
Sascha Kuehl 2023-12-17 22:11:43 +01:00
parent 2893f749b6
commit 320d06bb20
39 changed files with 1272 additions and 46 deletions

View File

@ -1,2 +1,3 @@
add_subdirectory(sqlite)
add_subdirectory(postgres)
add_subdirectory(postgres)
add_subdirectory(mysql)

View File

@ -0,0 +1,35 @@
set(HEADER
include/mysql_connection.hpp
include/mysql_error.hpp
include/mysql_result_reader.hpp
include/mysql_dialect.hpp
include/mysql_statement.hpp
include/mysql_parameter_binder.hpp
include/mysql_prepared_result_reader.hpp
)
set(SOURCES
src/mysql_connection.cpp
src/mysql_error.cpp
src/mysql_result_reader.cpp
src/mysql_dialect.cpp
src/mysql_statement.cpp
src/mysql_parameter_binder.cpp
src/mysql_prepared_result_reader.cpp
)
set(LIBRARY_TARGET matador-mysql)
add_library(${LIBRARY_TARGET} MODULE ${SOURCES} ${HEADER})
set_target_properties(${LIBRARY_TARGET}
PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/backends"
)
target_include_directories(${LIBRARY_TARGET} PRIVATE
${PROJECT_SOURCE_DIR}/include
${PROJECT_SOURCE_DIR}/backends/mysql/include
${MYSQL_INCLUDE_DIR})
target_link_libraries(${LIBRARY_TARGET} matador ${MYSQL_LIBRARY})

View File

@ -0,0 +1,61 @@
#ifndef QUERY_POSTGRES_CONNECTION_HPP
#define QUERY_POSTGRES_CONNECTION_HPP
#ifdef _MSC_VER
#ifdef matador_mysql_EXPORTS
#define MATADOR_MYSQL_API __declspec(dllexport)
#else
#define MATADOR_MYSQL_API __declspec(dllimport)
#endif
#pragma warning(disable: 4355)
#else
#define MATADOR_MYSQL_API
#endif
#include "matador/sql/connection_impl.hpp"
#include <unordered_map>
#ifdef _MSC_VER
#include <mysql.h>
#else
#include <mysql/mysql.h>
#endif
namespace matador::backends::mysql {
class mysql_connection : public matador::sql::connection_impl
{
public:
explicit mysql_connection(const sql::connection_info &info);
void open() override;
void close() override;
bool is_open() override;
std::unique_ptr<sql::query_result_impl> fetch(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;
sql::record describe(const std::string& table) override;
bool exists(const std::string &schema_name, const std::string &table_name) override;
private:
mutable std::unique_ptr<MYSQL> mysql_;
using string_to_int_map = std::unordered_map<std::string, unsigned long>;
static string_to_int_map statement_name_map_;
};
}
extern "C"
{
MATADOR_MYSQL_API matador::sql::connection_impl* create_database(const matador::sql::connection_info &info);
MATADOR_MYSQL_API void destroy_database(matador::sql::connection_impl *db);
}
#endif //QUERY_POSTGRES_CONNECTION_HPP

View File

@ -0,0 +1,19 @@
#ifndef QUERY_POSTGRES_DIALECT_HPP
#define QUERY_POSTGRES_DIALECT_HPP
#ifdef _MSC_VER
#ifdef matador_mysql_EXPORTS
#define MATADOR_MYSQL_API __declspec(dllexport)
#else
#define MATADOR_MYSQL_API __declspec(dllimport)
#endif
#pragma warning(disable: 4355)
#else
#define MATADOR_MYSQL_API
#endif
#include "matador/sql/dialect.hpp"
extern "C" [[maybe_unused]] MATADOR_MYSQL_API const matador::sql::dialect* get_dialect();
#endif //QUERY_POSTGRES_DIALECT_HPP

View File

@ -0,0 +1,20 @@
#ifndef QUERY_POSTGRES_ERROR_HPP
#define QUERY_POSTGRES_ERROR_HPP
#ifdef _MSC_VER
#include <mysql.h>
#else
#include <mysql/mysql.h>
#endif
#include <string>
namespace matador::backends::mysql {
void throw_mysql_error(const char *what, const std::string &source);
void throw_mysql_error(MYSQL *db, const std::string &source);
void throw_mysql_error(MYSQL_STMT *stmt, const std::string &source, const std::string &sql);
}
#endif //QUERY_POSTGRES_ERROR_HPP

View File

@ -0,0 +1,72 @@
#ifndef QUERY_POSTGRES_PARAMETER_BINDER_H
#define QUERY_POSTGRES_PARAMETER_BINDER_H
#include "matador/sql/parameter_binder.hpp"
#ifdef _MSC_VER
#include <mysql.h>
#else
#include <mysql/mysql.h>
#endif
#include <vector>
namespace matador::backends::mysql {
struct mysql_result_info
{
unsigned long length = 0;
my_bool is_null = false;
my_bool error = false;
// std::unique_ptr<char[]> buffer;
char *buffer = nullptr;
unsigned long buffer_length = 0;
bool is_allocated = false;
~mysql_result_info()
{
if (is_allocated) {
delete [] buffer;
}
}
};
class mysql_parameter_binder final : public sql::parameter_binder
{
public:
explicit mysql_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;
[[nodiscard]] std::vector<MYSQL_BIND>& bind_params();
private:
struct is_null_t
{
my_bool is_null = false;
};
std::vector<MYSQL_BIND> bind_params_;
std::vector<is_null_t> is_null_vector;
std::vector<mysql_result_info> info_;
};
}
#endif //QUERY_POSTGRES_PARAMETER_BINDER_H

View File

@ -0,0 +1,36 @@
#ifndef QUERY_MYSQL_PREPARED_RESULT_READER_HPP
#define QUERY_MYSQL_PREPARED_RESULT_READER_HPP
#include "matador/sql/query_result_reader.hpp"
#ifdef _MSC_VER
#include <mysql.h>
#else
#include <mysql/mysql.h>
#endif
namespace matador::backends::mysql {
class mysql_prepared_result_reader : public sql::query_result_reader
{
public:
explicit mysql_prepared_result_reader(MYSQL_STMT *stmt);
~mysql_prepared_result_reader() override;
[[nodiscard]] size_t column_count() const override;
[[nodiscard]] const char *column(size_t index) const override;
bool fetch() override;
private:
MYSQL_STMT *stmt_{};
MYSQL_ROW current_row_{};
size_t row_count_{};
size_t column_count_{};
int row_index_{-1};
};
}
#endif //QUERY_MYSQL_PREPARED_RESULT_READER_HPP

View File

@ -0,0 +1,36 @@
#ifndef QUERY_POSTGRES_RESULT_READER_HPP
#define QUERY_POSTGRES_RESULT_READER_HPP
#include "matador/sql/query_result_reader.hpp"
#ifdef _MSC_VER
#include <mysql.h>
#else
#include <mysql/mysql.h>
#endif
namespace matador::backends::mysql {
class mysql_result_reader : public sql::query_result_reader
{
public:
explicit mysql_result_reader(MYSQL_RES *result, unsigned int column_count);
~mysql_result_reader() override;
[[nodiscard]] size_t column_count() const override;
[[nodiscard]] const char *column(size_t index) const override;
bool fetch() override;
private:
MYSQL_RES *result_{};
MYSQL_ROW current_row_{};
size_t row_count_{};
size_t column_count_{};
int row_index_{-1};
};
}
#endif //QUERY_POSTGRES_RESULT_READER_HPP

View File

@ -0,0 +1,37 @@
#ifndef QUERY_POSTGRES_STATEMENT_HPP
#define QUERY_POSTGRES_STATEMENT_HPP
#include "matador/sql/statement_impl.hpp"
#include "mysql_parameter_binder.hpp"
#ifdef _MSC_VER
#include <mysql.h>
#else
#include <mysql/mysql.h>
#endif
namespace matador::backends::mysql {
class mysql_statement final : public sql::statement_impl
{
public:
mysql_statement(MYSQL_STMT *stmt, 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:
MYSQL_STMT *stmt_{nullptr};
std::string name_;
mysql_parameter_binder binder_;
};
}
#endif //QUERY_POSTGRES_STATEMENT_HPP

View File

@ -0,0 +1,248 @@
#include "mysql_connection.hpp"
#include "mysql_error.hpp"
#include "mysql_result_reader.hpp"
#include "mysql_statement.hpp"
#include "matador/sql/record.hpp"
#include <memory>
#include <regex>
namespace matador::backends::mysql {
mysql_connection::string_to_int_map mysql_connection::statement_name_map_{};
mysql_connection::mysql_connection(const sql::connection_info &info)
: connection_impl(info) {}
void mysql_connection::open()
{
if (is_open()) {
return;
}
mysql_ = std::make_unique<MYSQL>();
if (!mysql_init(mysql_.get())) {
throw_mysql_error(mysql_.get(), "mysql_init");
}
if (!mysql_real_connect(mysql_.get(),
info().hostname.c_str(),
info().user.c_str(),
!info().password.empty() ? info().password.c_str() : nullptr,
info().database.c_str(),
info().port,
nullptr,
0)) {
// disconnect all handles
const std::string error_message = mysql_error(mysql_.get());
mysql_close(mysql_.get());
mysql_.reset();
// throw exception
throw_mysql_error(error_message.c_str(), "mysql_real_connect");
}
}
void mysql_connection::close()
{
if (mysql_) {
mysql_close(mysql_.get());
mysql_.reset();
}
}
bool mysql_connection::is_open()
{
return mysql_ != nullptr;
}
sql::data_type_t to_type(enum_field_types type, unsigned int flags)
{
switch (type) {
case MYSQL_TYPE_TINY:
return flags & UNSIGNED_FLAG ? sql::data_type_t::type_unsigned_char : sql::data_type_t::type_char;
case MYSQL_TYPE_SHORT:
return flags & UNSIGNED_FLAG ? sql::data_type_t::type_unsigned_short : sql::data_type_t::type_short;
case MYSQL_TYPE_LONG:
return flags & UNSIGNED_FLAG ? sql::data_type_t::type_unsigned_int : sql::data_type_t::type_int;
case MYSQL_TYPE_LONGLONG:
return flags & UNSIGNED_FLAG ? sql::data_type_t::type_unsigned_long_long : sql::data_type_t::type_long_long;
case MYSQL_TYPE_FLOAT:
return sql::data_type_t::type_float;
case MYSQL_TYPE_DOUBLE:
return sql::data_type_t::type_double;
case MYSQL_TYPE_VARCHAR:
case MYSQL_TYPE_VAR_STRING:
return sql::data_type_t::type_varchar;
case MYSQL_TYPE_BLOB:
return sql::data_type_t::type_blob;
case MYSQL_TYPE_STRING:
return sql::data_type_t::type_text;
case MYSQL_TYPE_DATE:
return sql::data_type_t::type_date;
case MYSQL_TYPE_DATETIME:
case MYSQL_TYPE_TIMESTAMP:
return sql::data_type_t::type_time;
default:
return sql::data_type_t::type_unknown;
}
}
utils::constraints to_options(unsigned int flags)
{
utils::constraints options{utils::constraints::NONE};
if (flags & NOT_NULL_FLAG) {
options |= utils::constraints::NOT_NULL;
}
if (flags & PRI_KEY_FLAG) {
options |= utils::constraints::PRIMARY_KEY;
}
if (flags & UNIQUE_KEY_FLAG) {
options |= utils::constraints::UNIQUE;
}
return options;
}
sql::data_type_t string2type(const std::string &type_string)
{
// if (strcmp(type_string.c_str(), "int")
return sql::data_type_t::type_unknown;
}
struct type_info
{
sql::data_type_t type{sql::data_type_t::type_unknown};
size_t size{};
};
type_info determine_type_info(const std::string &type_string)
{
static const std::regex TYPE_REGEX(R"(^(\w+)(\((\d+)(,(\d+))?\))?$)");
std::smatch matcher;
type_info result;
if (std::regex_match(type_string, matcher, TYPE_REGEX)) {
result.type = string2type(matcher[1].str());
if (matcher[3].matched) {
result.size = std::stoi(matcher[3].str());
}
}
return result;
}
std::unique_ptr<sql::query_result_impl> mysql_connection::fetch(const std::string &stmt)
{
if (mysql_query(mysql_.get(), stmt.c_str())) {
throw_mysql_error(mysql_.get(), stmt);
}
auto result = mysql_store_result(mysql_.get());
if (result == nullptr) {
throw_mysql_error(mysql_.get(), stmt);
}
auto field_count = mysql_num_fields(result);
auto fields = mysql_fetch_fields(result);
sql::record prototype;
for (unsigned i = 0; i < field_count; ++i) {
auto type = to_type(fields[i].type, fields[i].flags);
auto options = to_options(fields[i].flags);
prototype.append({fields[i].name, type, options});
}
return std::move(std::make_unique<sql::query_result_impl>(std::make_unique<mysql_result_reader>(result, field_count), std::move(prototype)));
}
std::unique_ptr<sql::statement_impl> mysql_connection::prepare(sql::query_context context)
{
MYSQL_STMT *stmt = mysql_stmt_init(mysql_.get());
if (stmt == nullptr) {
throw_mysql_error(mysql_.get(), "mysql_stmt_init");
}
if (mysql_stmt_prepare(stmt, context.sql.c_str(), static_cast<unsigned long>(context.sql.size())) != 0) {
throw_mysql_error(stmt, "mysql_stmt_prepare", context.sql);
}
return std::make_unique<mysql_statement>(stmt, std::move(context));
}
size_t mysql_connection::execute(const std::string &stmt)
{
if (mysql_query(mysql_.get(), stmt.c_str())) {
throw_mysql_error(mysql_.get(), stmt);
}
return mysql_affected_rows(mysql_.get());
}
sql::record mysql_connection::describe(const std::string &table)
{
std::string stmt("SHOW COLUMNS FROM " + table);
if (mysql_query(mysql_.get(), stmt.c_str())) {
throw_mysql_error(mysql_.get(), stmt);
}
auto result = mysql_store_result(mysql_.get());
if (result == nullptr) {
throw_mysql_error(mysql_.get(), stmt);
}
mysql_result_reader reader(result, mysql_num_fields(result));
sql::record prototype;
while (reader.fetch()) {
char *end = nullptr;
// Todo: Handle error
auto index = strtoul(reader.column(0), &end, 10);
std::string name = reader.column(1);
// Todo: extract size
auto typeinfo = determine_type_info(reader.column(2));
end = nullptr;
utils::constraints options{};
if (strtoul(reader.column(4), &end, 10) == 0) {
options = utils::constraints::NOT_NULL;
}
// f.default_value(res->column(4));
prototype.append({name, typeinfo.type, {typeinfo.size, options}});
}
return prototype;
}
bool mysql_connection::exists(const std::string &/*schema_name*/, const std::string &table_name)
{
std::string stmt("SELECT 1 FROM information_schema.tables WHERE table_schema = '" + info().database + "' AND table_name = '" + table_name + "'");
if (mysql_query(mysql_.get(), stmt.c_str())) {
throw_mysql_error(mysql_.get(), stmt);
}
auto result = mysql_store_result(mysql_.get());
if (result == nullptr) {
throw_mysql_error(mysql_.get(), stmt);
}
return result->row_count == 1;
}
}
extern "C"
{
MATADOR_MYSQL_API matador::sql::connection_impl *create_database(const matador::sql::connection_info &info)
{
return new matador::backends::mysql::mysql_connection(info);
}
MATADOR_MYSQL_API void destroy_database(matador::sql::connection_impl *db)
{
delete db;
}
}

View File

@ -0,0 +1,20 @@
#include "mysql_dialect.hpp"
#include "matador/sql/dialect_builder.hpp"
[[maybe_unused]] const matador::sql::dialect *get_dialect()
{
using namespace matador::sql;
const static dialect d = dialect_builder::builder()
.create()
.with_placeholder_func([](size_t index) {
return "$" + std::to_string(index);
})
.with_token_replace_map({
{dialect::token_t::START_QUOTE, "`"},
{dialect::token_t::END_QUOTE, "`"},
})
.with_default_schema_name("public")
.build();
return &d;
}

View File

@ -0,0 +1,30 @@
#include "mysql_error.hpp"
#include <sstream>
namespace matador::backends::mysql {
void throw_mysql_error(const char *what, const std::string &source)
{
std::stringstream msg;
msg << "mysql error (" << source << "): " << what;
throw std::logic_error(msg.str());
}
void throw_mysql_error(MYSQL *db, const std::string &source)
{
if (mysql_errno(db) != 0) {
throw_mysql_error(mysql_error(db), source);
}
}
void throw_mysql_error(MYSQL_STMT *stmt, const std::string &source, const std::string &sql)
{
if (mysql_stmt_errno(stmt) != 0) {
std::stringstream msg;
msg << "mysql error (" << source << ") " << mysql_stmt_error(stmt) << ": " << sql;
throw std::logic_error(msg.str());
}
}
}

View File

@ -0,0 +1,186 @@
#include "mysql_parameter_binder.hpp"
namespace matador::backends::mysql {
namespace detail {
template < class T >
void bind_value(enum_field_types type, T value, MYSQL_BIND &bind, my_bool &is_null)
{
if (bind.buffer == nullptr) {
// allocating memory
bind.buffer = new char[sizeof(T)];
bind.buffer_type = type;
bind.buffer_length = sizeof(T);
bind.is_null = &is_null;
bind.is_unsigned = std::is_unsigned<T>::value;
}
*static_cast<T*>(bind.buffer) = value;
is_null = false;
}
void bind_value(enum_field_types type, const char *value, size_t, MYSQL_BIND &bind, my_bool &is_null)
{
std::size_t len(strlen(value) + 1);
if (bind.buffer_length < len) {
// reallocate memory
delete [] static_cast<char*>(bind.buffer);
bind.buffer = nullptr;
bind.buffer_length = 0;
bind.buffer_type = type;
bind.is_null = &is_null;
}
if (bind.buffer == nullptr) {
// allocating memory
bind.buffer = new char[len];
memset(bind.buffer, 0, len);
}
bind.buffer_length = (unsigned long)(len - 1);
#ifdef _MSC_VER
strncpy_s(static_cast<char*>(bind.buffer), len, value, _TRUNCATE);
#else
strncpy(static_cast<char*>(bind.buffer), value, len);
#endif
is_null = false;
}
//void bind_value(enum_field_types type, const matador::date &x, MYSQL_BIND &bind, my_bool &is_null)
//{
// if (bind.buffer == nullptr) {
// size_t s = sizeof(MYSQL_TIME);
// bind.buffer = new char[s];
// bind.buffer_length = (unsigned long)s;
// bind.is_null = &is_null;
// bind.buffer_type = type;
// bind.length = nullptr;
// }
// memset(bind.buffer, 0, sizeof(MYSQL_TIME));
// is_null = false;
// auto *mt = static_cast<MYSQL_TIME*>(bind.buffer);
// mt->day = (unsigned int)x.day();
// mt->month = (unsigned int)x.month();
// mt->year = (unsigned int)x.year();
// mt->time_type = MYSQL_TIMESTAMP_DATE;
//}
//
//void bind_value(enum_field_types type, const matador::time &x, MYSQL_BIND &bind, my_bool &is_null)
//{
// if (bind.buffer == nullptr) {
// size_t s = sizeof(MYSQL_TIME);
// bind.buffer = new char[s];
// bind.buffer_length = (unsigned long)s;
// bind.buffer_type = type;
// bind.length = nullptr;
// bind.is_null = &is_null;
// }
// memset(bind.buffer, 0, sizeof(MYSQL_TIME));
// is_null = false;
// auto *mt = static_cast<MYSQL_TIME*>(bind.buffer);
// mt->day = (unsigned int)x.day();
// mt->month = (unsigned int)x.month();
// mt->year = (unsigned int)x.year();
// mt->hour = (unsigned int)x.hour();
// mt->minute = (unsigned int)x.minute();
// mt->second = (unsigned int)x.second();
// mt->second_part = (unsigned long)x.milli_second() * 1000;
// mt->time_type = MYSQL_TIMESTAMP_DATETIME;
//}
}
mysql_parameter_binder::mysql_parameter_binder(size_t size)
: bind_params_(size)
, info_(size)
{}
void mysql_parameter_binder::bind(size_t pos, char i)
{
detail::bind_value(MYSQL_TYPE_TINY, i, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, short i)
{
detail::bind_value(MYSQL_TYPE_SHORT, i, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, int i)
{
detail::bind_value(MYSQL_TYPE_LONG, i, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, long i)
{
detail::bind_value(MYSQL_TYPE_LONG, i, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, long long int i)
{
detail::bind_value(MYSQL_TYPE_LONGLONG, i, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, unsigned char i)
{
detail::bind_value(MYSQL_TYPE_TINY, i, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, unsigned short i)
{
detail::bind_value(MYSQL_TYPE_SHORT, i, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, unsigned int i)
{
detail::bind_value(MYSQL_TYPE_LONG, i, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, unsigned long i)
{
detail::bind_value(MYSQL_TYPE_LONG, i, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, unsigned long long int i)
{
detail::bind_value(MYSQL_TYPE_LONGLONG, i, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, bool b)
{
detail::bind_value(MYSQL_TYPE_TINY, b, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, float d)
{
detail::bind_value(MYSQL_TYPE_FLOAT, d, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, double d)
{
detail::bind_value(MYSQL_TYPE_DOUBLE, d, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, const char *str)
{
detail::bind_value(MYSQL_TYPE_STRING, str, strlen(str), bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, const char *str, size_t size)
{
detail::bind_value(MYSQL_TYPE_VAR_STRING, str, size, bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, const std::string &str)
{
detail::bind_value(MYSQL_TYPE_STRING, str.data(), str.size(), bind_params_[pos], is_null_vector[pos].is_null);
}
void mysql_parameter_binder::bind(size_t pos, const std::string &str, size_t size)
{
detail::bind_value(MYSQL_TYPE_VAR_STRING, str.data(), size, bind_params_[pos], is_null_vector[pos].is_null);
}
std::vector<MYSQL_BIND> &mysql_parameter_binder::bind_params()
{
return bind_params_;
}
}

View File

@ -0,0 +1,28 @@
#include "mysql_prepared_result_reader.hpp"
namespace matador::backends::mysql {
mysql_prepared_result_reader::mysql_prepared_result_reader(MYSQL_STMT *stmt)
: stmt_(stmt)
{}
mysql_prepared_result_reader::~mysql_prepared_result_reader()
{
}
size_t mysql_prepared_result_reader::column_count() const
{
return 0;
}
const char *mysql_prepared_result_reader::column(size_t index) const
{
return nullptr;
}
bool mysql_prepared_result_reader::fetch()
{
return false;
}
}

View File

@ -0,0 +1,35 @@
#include "mysql_result_reader.hpp"
namespace matador::backends::mysql {
mysql_result_reader::mysql_result_reader(MYSQL_RES *result, unsigned int column_count)
: result_(result)
, row_count_(mysql_num_rows(result_))
, column_count_(column_count)
{}
mysql_result_reader::~mysql_result_reader()
{
if (result_) {
mysql_free_result(result_);
}
}
size_t mysql_result_reader::column_count() const
{
return column_count_;
}
const char *mysql_result_reader::column(size_t index) const
{
return current_row_[index];
}
bool mysql_result_reader::fetch()
{
current_row_ = mysql_fetch_row(result_);
return current_row_ != nullptr;
}
}

View File

@ -0,0 +1,53 @@
#include "mysql_statement.hpp"
#include "mysql_error.hpp"
#include "mysql_prepared_result_reader.hpp"
namespace matador::backends::mysql {
mysql_statement::mysql_statement(MYSQL_STMT *stmt, const sql::query_context &query)
: statement_impl(query)
, stmt_(stmt)
, binder_(query_.bind_vars.size())
{}
size_t mysql_statement::execute()
{
if (!binder_.bind_params().empty()) {
if (mysql_stmt_bind_param(stmt_, binder_.bind_params().data()) != 0) {
throw_mysql_error(stmt_, "mysql", query_.sql);
}
}
if (mysql_stmt_execute(stmt_) != 0) {
throw_mysql_error(stmt_, "mysql", query_.sql);
}
return mysql_stmt_affected_rows(stmt_);
}
std::unique_ptr<sql::query_result_impl> mysql_statement::fetch()
{
if (!binder_.bind_params().empty()) {
if (mysql_stmt_bind_param(stmt_, binder_.bind_params().data()) != 0) {
throw_mysql_error(stmt_, "mysql", query_.sql);
}
}
if (mysql_stmt_execute(stmt_) != 0) {
throw_mysql_error(stmt_, "mysql", query_.sql);
}
if (mysql_stmt_store_result(stmt_) != 0) {
throw_mysql_error(stmt_, "mysql", query_.sql);
}
return std::move(std::make_unique<sql::query_result_impl>(std::make_unique<mysql_prepared_result_reader>(stmt_), std::move(query_.prototype)));
}
void mysql_statement::reset() {}
sql::parameter_binder& mysql_statement::binder()
{
return binder_;
}
}

View File

@ -35,7 +35,7 @@ public:
sql::record describe(const std::string& table) override;
bool exists(const std::string &table_name) override;
bool exists(const std::string &schema_name, const std::string &table_name) override;
private:
[[nodiscard]] static std::string generate_statement_name(const sql::query_context &query) ;

View File

@ -12,7 +12,7 @@ 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);
postgres_statement(PGconn *db, PGresult *result, std::string name, const sql::query_context &query);
size_t execute() override;
std::unique_ptr<sql::query_result_impl> fetch() override;

View File

@ -161,9 +161,9 @@ sql::record postgres_connection::describe(const std::string &table)
return std::move(prototype);
}
bool postgres_connection::exists(const std::string &table_name)
bool postgres_connection::exists(const std::string &schema_name, const std::string &table_name)
{
std::string stmt("SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = '" + table_name + "'");
std::string stmt("SELECT 1 FROM information_schema.tables WHERE table_schema = '" + schema_name + "' AND table_name = '" + table_name + "'");
PGresult *res = PQexec(conn_, stmt.c_str());

View File

@ -4,11 +4,11 @@
namespace matador::backends::postgres {
postgres_statement::postgres_statement(PGconn *db, PGresult *result, const std::string &name, const sql::query_context &query)
postgres_statement::postgres_statement(PGconn *db, PGresult *result, std::string name, const sql::query_context &query)
: statement_impl(query)
, db_(db)
, result_(result)
, name_(name)
, name_(std::move(name))
, binder_(query_.bind_vars.size())
{}

View File

@ -35,7 +35,7 @@ public:
sql::record describe(const std::string& table) override;
bool exists(const std::string &table_name) override;
bool exists(const std::string &schema_name, const std::string &table_name) override;
private:
struct fetch_context

View File

@ -156,7 +156,6 @@ sql::record sqlite_connection::describe(const std::string& table)
auto index = strtoul(reader.column(0), &end, 10);
std::string name = reader.column(1);
// Todo: extract size
auto type = (string2type(reader.column(2)));
end = nullptr;
utils::constraints options{};
@ -170,7 +169,7 @@ sql::record sqlite_connection::describe(const std::string& table)
return std::move(prototype);
}
bool sqlite_connection::exists(const std::string &table_name)
bool sqlite_connection::exists(const std::string &schema_name, const std::string &table_name)
{
const auto result = fetch_internal("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND tbl_name='" + table_name + "' LIMIT 1");
sqlite_result_reader reader(result.rows, result.prototype.size());

View File

@ -32,7 +32,7 @@ public:
[[nodiscard]] const connection_info& info() const;
[[nodiscard]] record describe(const std::string &table_name) const;
[[nodiscard]] bool exists(const std::string &table_name) const;
[[nodiscard]] bool exists(const std::string &schema_name, const std::string &table_name) const;
[[nodiscard]] std::unique_ptr<query_result_impl> fetch(const std::string &sql) const;
[[nodiscard]] size_t execute(const std::string &sql) const;

View File

@ -27,7 +27,7 @@ public:
virtual std::unique_ptr<statement_impl> prepare(query_context context) = 0;
virtual record describe(const std::string &table) = 0;
virtual bool exists(const std::string &table_name) = 0;
virtual bool exists(const std::string &schema_name, const std::string &table_name) = 0;
protected:
explicit connection_impl(const connection_info &info);

View File

@ -1,5 +1,5 @@
#ifndef QUERY_OBJECT_BINDER_HPP
#define QUERY_OBJECT_BINDER_HPP
#ifndef QUERY_OBJECT_PARAMETER_BINDER_HPP
#define QUERY_OBJECT_PARAMETER_BINDER_HPP
#include "matador/sql/parameter_binder.hpp"
#include "matador/sql/types.hpp"
@ -49,10 +49,10 @@ private:
}
class object_binder
class object_parameter_binder
{
public:
explicit object_binder(parameter_binder &binder);
explicit object_parameter_binder(parameter_binder &binder);
void reset();
@ -101,4 +101,4 @@ void fk_binder::on_primary_key(const char *id, ValueType &value, typename std::e
}
}
#endif //QUERY_OBJECT_BINDER_HPP
#endif //QUERY_OBJECT_PARAMETER_BINDER_HPP

View File

@ -11,7 +11,7 @@ struct query_context
std::string command_name;
std::string table_name;
record prototype;
std::vector<any_type> host_vars;
std::vector<std::string> result_vars;
std::vector<std::string> bind_vars;
};

View File

@ -0,0 +1,133 @@
#ifndef QUERY_RESULT_PARAMETER_BINDER_HPP
#define QUERY_RESULT_PARAMETER_BINDER_HPP
#include "matador/utils/access.hpp"
#include "matador/utils/cascade_type.hpp"
#include "matador/utils/field_attributes.hpp"
#include "matador/sql/any_type.hpp"
#include "matador/sql/types.hpp"
#include <string>
namespace matador::sql {
class result_parameter_binder;
namespace detail {
class fk_result_binder
{
public:
explicit fk_result_binder(result_parameter_binder &result_binder);
template<class Type>
void bind_result(Type &obj, size_t column_index)
{
column_index_ = column_index;
utils::access::process(*this, obj);
}
template<typename ValueType>
void on_primary_key(const char *id, ValueType &value, typename std::enable_if<std::is_integral<ValueType>::value && !std::is_same<bool, ValueType>::value>::type* = 0);
void on_primary_key(const char *id, std::string &value, size_t size);
void on_revision(const char * /*id*/, unsigned long long &/*rev*/) {}
template < class Type >
void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
template < class Pointer >
void on_belongs_to(const char * /*id*/, Pointer &/*x*/, utils::cascade_type) {}
template < class Pointer >
void on_has_one(const char * /*id*/, Pointer &/*x*/, utils::cascade_type) {}
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:
size_t column_index_{};
result_parameter_binder &result_binder_;
};
}
class result_parameter_binder
{
public:
template<typename ValueType>
void on_primary_key(const char * /*id*/, ValueType &value, typename std::enable_if<std::is_integral<ValueType>::value && !std::is_same<bool, ValueType>::value>::type* = 0)
{
data_type_traits<ValueType>::bind_result_value(*this, column_index_++, value);
}
void on_primary_key(const char *id, std::string &value, size_t size);
void on_revision(const char *id, unsigned long long &rev);
template < class Type >
void on_attribute(const char * /*id*/, Type &x, const utils::field_attributes &/*attr*/ = utils::null_attributes)
{
data_type_traits<Type>::bind_result_value(*this, column_index_++, x);
}
void on_attribute(const char *id, char *value, const utils::field_attributes &attr = utils::null_attributes);
void on_attribute(const char *id, std::string &value, const utils::field_attributes &attr = utils::null_attributes);
void on_attribute(const char *id, any_type &value, data_type_t type, const utils::field_attributes &attr = utils::null_attributes);
template < class Pointer >
void on_belongs_to(const char * /*id*/, Pointer &x, utils::cascade_type)
{
if (!x.get()) {
x.reset(new typename Pointer::value_type);
}
fk_result_binder_.bind_result(*x, column_index_++);
}
template < class Pointer >
void on_has_one(const char * /*id*/, Pointer &x, utils::cascade_type)
{
if (!x.get()) {
x.reset(new typename Pointer::value_type);
}
fk_result_binder_.bind_result(*x, column_index_++);
}
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) {}
virtual void bind_result_value(size_t index, char &value) {}
virtual void bind_result_value(size_t index, short &value) {}
virtual void bind_result_value(size_t index, int &value) {}
virtual void bind_result_value(size_t index, long &value) {}
virtual void bind_result_value(size_t index, long long &value) {}
virtual void bind_result_value(size_t index, unsigned char &value) {}
virtual void bind_result_value(size_t index, unsigned short &value) {}
virtual void bind_result_value(size_t index, unsigned int &value) {}
virtual void bind_result_value(size_t index, unsigned long &value) {}
virtual void bind_result_value(size_t index, unsigned long long &value) {}
virtual void bind_result_value(size_t index, bool &value) {}
virtual void bind_result_value(size_t index, float &value) {}
virtual void bind_result_value(size_t index, double &value) {}
// virtual void bind_result_value(size_t index, matador::time &value) {}
// virtual void bind_result_value(size_t index, matador::date &value) {}
virtual void bind_result_value(size_t index, char *value, size_t s) {}
virtual void bind_result_value(size_t index, std::string &value) {}
virtual void bind_result_value(size_t index, std::string &value, size_t s) {}
virtual void bind_result_value(size_t index, any_type &value, data_type_t type, size_t size) {}
private:
size_t column_index_{};
detail::fk_result_binder fk_result_binder_;
};
namespace detail {
template<typename ValueType>
void fk_result_binder::on_primary_key(const char * /*id*/, ValueType &value, typename std::enable_if<std::is_integral<ValueType>::value && !std::is_same<bool, ValueType>::value>::type *)
{
data_type_traits<ValueType>::bind_result_value(result_binder_, column_index_++, value);
}
}
}
#endif //QUERY_RESULT_PARAMETER_BINDER_HPP

View File

@ -1,7 +1,7 @@
#ifndef QUERY_STATEMENT_HPP
#define QUERY_STATEMENT_HPP
#include "matador/sql/object_binder.hpp"
#include "matador/sql/object_parameter_binder.hpp"
#include "matador/sql/query_result.hpp"
#include "matador/sql/statement_impl.hpp"
@ -50,7 +50,7 @@ public:
private:
std::unique_ptr<statement_impl> statement_;
const utils::logger &logger_;
object_binder object_binder_;
object_parameter_binder object_binder_;
};
}

View File

@ -10,6 +10,7 @@ namespace matador::sql {
class query_result_reader;
class parameter_binder;
class result_parameter_binder;
/**
* @brief Enumeration type of all supported builtin data types
@ -65,6 +66,7 @@ template <> struct data_type_traits<char, void>
inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_char; }
static void read_value(query_result_reader &reader, const char *id, size_t index, char &value);
static void bind_value(parameter_binder &binder, size_t index, char &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, char &value);
inline static any_type create_value(char &value) { return value; }
};
@ -73,6 +75,7 @@ template <> struct data_type_traits<short, void>
inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_short; }
static void read_value(query_result_reader &reader, const char *id, size_t index, short &value);
static void bind_value(parameter_binder &binder, size_t index, short &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, short &value);
inline static any_type create_value(short &value) { return value; }
};
@ -81,6 +84,7 @@ template <> struct data_type_traits<int, void>
inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_int; }
static void read_value(query_result_reader &reader, const char *id, size_t index, int &value);
static void bind_value(parameter_binder &binder, size_t index, int &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, int &value);
inline static any_type create_value(int &value) { return value; }
};
@ -89,6 +93,7 @@ template <> struct data_type_traits<long, void>
inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_long; }
static void read_value(query_result_reader &reader, const char *id, size_t index, long &value);
static void bind_value(parameter_binder &binder, size_t index, long &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, long &value);
inline static any_type create_value(long &value) { return value; }
};
@ -97,6 +102,7 @@ template <> struct data_type_traits<long long, void>
inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_long_long; }
static void read_value(query_result_reader &reader, const char *id, size_t index, long long &value);
static void bind_value(parameter_binder &binder, size_t index, long long &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, long long &value);
inline static any_type create_value(long long &value) { return value; }
};
@ -105,6 +111,7 @@ template <> struct data_type_traits<unsigned char, void>
inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_unsigned_char; }
static void read_value(query_result_reader &reader, const char *id, size_t index, unsigned char &value);
static void bind_value(parameter_binder &binder, size_t index, unsigned char &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, unsigned char &value);
inline static any_type create_value(unsigned char &value) { return value; }
};
@ -113,6 +120,7 @@ template <> struct data_type_traits<unsigned short, void>
inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_unsigned_short; }
static void read_value(query_result_reader &reader, const char *id, size_t index, unsigned short &value);
static void bind_value(parameter_binder &binder, size_t index, unsigned short &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, unsigned short &value);
inline static any_type create_value(unsigned short &value) { return value; }
};
@ -121,6 +129,7 @@ template <> struct data_type_traits<unsigned int, void>
inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_unsigned_int; }
static void read_value(query_result_reader &reader, const char *id, size_t index, unsigned int &value);
static void bind_value(parameter_binder &binder, size_t index, unsigned int &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, unsigned int &value);
inline static any_type create_value(unsigned int &value) { return value; }
};
@ -129,6 +138,7 @@ template <> struct data_type_traits<unsigned long, void>
inline static data_type_t builtin_type(std::size_t /*size*/ = 0) { return data_type_t::type_unsigned_long; }
static void read_value(query_result_reader &reader, const char *id, size_t index, unsigned long &value);
static void bind_value(parameter_binder &binder, size_t index, unsigned long &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, unsigned long &value);
inline static any_type create_value(unsigned long &value) { return value; }
};
@ -137,6 +147,7 @@ template <> struct data_type_traits<unsigned long long, void>
inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_unsigned_long_long; }
static void read_value(query_result_reader &reader, const char *id, size_t index, unsigned long long &value);
static void bind_value(parameter_binder &binder, size_t index, unsigned long long &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, unsigned long long &value);
inline static any_type create_value(unsigned long long &value) { return value; }
};
@ -145,6 +156,7 @@ template <> struct data_type_traits<bool, void>
inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_bool; }
static void read_value(query_result_reader &reader, const char *id, size_t index, bool &value);
static void bind_value(parameter_binder &binder, size_t index, bool &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, bool &value);
inline static any_type create_value(bool &value) { return value; }
};
@ -152,6 +164,7 @@ template <> struct data_type_traits<float, void>
{
inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_float; }
static void read_value(query_result_reader &reader, const char *id, size_t index, float &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, float &value);
static void bind_value(parameter_binder &binder, size_t index, float &value);
inline static any_type create_value(float &value) { return value; }
};
@ -160,6 +173,7 @@ template <> struct data_type_traits<double, void>
{
inline static data_type_t builtin_type(std::size_t /*size*/) { return data_type_t::type_double; }
static void read_value(query_result_reader &reader, const char *id, size_t index, double &value);
static void bind_result_value(result_parameter_binder &binder, size_t index, double &value);
static void bind_value(parameter_binder &binder, size_t index, double &value);
inline static any_type create_value(double &value) { return value; }
};
@ -169,6 +183,7 @@ template <> struct data_type_traits<const char*, void>
inline static data_type_t builtin_type(std::size_t size) { return size == 0 ? data_type_t::type_text : data_type_t::type_char_pointer; }
static void read_value(query_result_reader &reader, const char *id, size_t index, const char* value, size_t size);
static void bind_value(parameter_binder &binder, size_t index, const char *value, size_t size = 0);
static void bind_result_value(result_parameter_binder &binder, size_t index, const char *value, size_t size = 0);
inline static any_type create_value(const char *value) { return value; }
};
@ -177,6 +192,7 @@ template <> struct data_type_traits<char*, void>
inline static data_type_t builtin_type(std::size_t size) { return size == 0 ? data_type_t::type_text : data_type_t::type_varchar; }
static void read_value(query_result_reader &reader, const char *id, size_t index, char *value, size_t size);
static void bind_value(parameter_binder &binder, size_t index, char *value, size_t size = 0);
static void bind_result_value(result_parameter_binder &binder, size_t index, char *value, size_t size = 0);
inline static any_type create_value(const char *value) { return value; }
};
@ -185,6 +201,7 @@ template <> struct data_type_traits<std::string, void>
inline static data_type_t builtin_type(std::size_t size) { return size == 0 ? data_type_t::type_text : data_type_t::type_varchar; }
static void read_value(query_result_reader &reader, const char *id, size_t index, std::string &value, size_t size);
static void bind_value(parameter_binder &binder, size_t index, std::string &value, size_t size = 0);
static void bind_result_value(result_parameter_binder &binder, size_t index, std::string &value, size_t size = 0);
inline static any_type create_value(std::string &value) { return value; }
};
@ -216,6 +233,10 @@ struct data_type_traits<EnumType, typename std::enable_if<std::is_enum<EnumType>
{
data_type_traits<int>::bind_value(binder, index, (int&)value);
}
static void bind_result_value(result_parameter_binder &binder, size_t index, EnumType &value)
{
data_type_traits<int>::bind_result_value(binder, index, (int&)value);
}
inline static any_type create_value(EnumType &value) { return (int)value; }
};
/// @endcond

View File

@ -23,9 +23,10 @@ set(SQL_SOURCES
sql/statement_cache.cpp
sql/statement_impl.cpp
sql/dialect_builder.cpp
sql/object_binder.cpp
sql/object_parameter_binder.cpp
sql/placeholder_generator.cpp
sql/types.cpp
sql/result_parameter_binder.cpp
)
set(SQL_HEADER
@ -64,8 +65,9 @@ set(SQL_HEADER
sql/statement.cpp
../include/matador/sql/parameter_binder.hpp
../include/matador/sql/dialect_builder.hpp
../include/matador/sql/object_binder.hpp
../include/matador/sql/object_parameter_binder.hpp
../include/matador/sql/placeholder_generator.hpp
../include/matador/sql/result_parameter_binder.hpp
)
set(UTILS_HEADER

View File

@ -71,9 +71,9 @@ record connection::describe(const std::string &table_name) const
return std::move(connection_->describe(table_name));
}
bool connection::exists(const std::string &table_name) const
bool connection::exists(const std::string &schema_name, const std::string &table_name) const
{
return connection_->exists(table_name);
return connection_->exists(schema_name, table_name);
}
size_t connection::execute(const std::string &sql) const

View File

@ -1,4 +1,4 @@
#include "matador/sql/object_binder.hpp"
#include "matador/sql/object_parameter_binder.hpp"
#include "matador/sql/parameter_binder.hpp"
namespace matador::sql {
@ -15,21 +15,21 @@ void fk_binder::on_primary_key(const char *id, std::string &value, size_t size)
}
object_binder::object_binder(parameter_binder &binder)
object_parameter_binder::object_parameter_binder(parameter_binder &binder)
: binder_(binder)
, fk_binder_(binder) {}
void object_binder::reset()
void object_parameter_binder::reset()
{
index_ = 0;
}
void object_binder::on_primary_key(const char *id, std::string &val, size_t size)
void object_parameter_binder::on_primary_key(const char *id, std::string &val, size_t size)
{
data_type_traits<std::string>::bind_value(binder_, index_++, val, size);
}
void object_binder::on_revision(const char *id, unsigned long long int &rev)
void object_parameter_binder::on_revision(const char *id, unsigned long long int &rev)
{
data_type_traits<unsigned long long>::bind_value(binder_, index_++, rev);
}

View File

@ -125,15 +125,18 @@ query_builder &query_builder::select(const std::vector<column> &columns)
if (columns.size() < 2) {
for (const auto &col: columns) {
result.append(dialect_.prepare_identifier(col));
query_.result_vars.emplace_back(col.name());
query_.prototype.append(col);
}
} else {
auto it = columns.begin();
result.append(dialect_.prepare_identifier(*it));
query_.result_vars.emplace_back(it->name());
query_.prototype.append(column{*it++});
for (; it != columns.end(); ++it) {
result.append(", ");
result.append(dialect_.prepare_identifier(*it));
query_.result_vars.emplace_back(it->name());
query_.prototype.append(column{*it});
}
}
@ -284,20 +287,20 @@ query_builder &query_builder::values(const std::vector<any_type> &values)
std::string result{"("};
if (values.size() < 2) {
for (auto val: values) {
query_.host_vars.push_back(val);
// query_.result_vars.push_back(val);
std::visit(value_to_string_, val);
result.append(value_to_string_.result);
}
} else {
auto it = values.begin();
auto val = *it++;
query_.host_vars.push_back(val);
// query_.result_vars.push_back(val);
std::visit(value_to_string_, val);
result.append(value_to_string_.result);
for (; it != values.end(); ++it) {
result.append(", ");
val = *it;
query_.host_vars.push_back(val);
// query_.result_vars.push_back(val);
std::visit(value_to_string_, val);
result.append(value_to_string_.result);
}

View File

@ -0,0 +1,42 @@
#include "matador/sql/result_parameter_binder.hpp"
namespace matador::sql {
namespace detail {
fk_result_binder::fk_result_binder(result_parameter_binder &result_binder)
: result_binder_(result_binder)
{}
void fk_result_binder::on_primary_key(const char * /*id*/, std::string &value, size_t size)
{
data_type_traits<std::string>::bind_result_value(result_binder_, column_index_++, value, size);
}
}
void result_parameter_binder::on_primary_key(const char * /*id*/, std::string &value, size_t size)
{
data_type_traits<std::string>::bind_result_value(*this, column_index_++, value, size);
}
void result_parameter_binder::on_revision(const char * /*id*/, unsigned long long &rev)
{
data_type_traits<unsigned long long>::bind_result_value(*this, column_index_++, rev);
}
void result_parameter_binder::on_attribute(const char * /*id*/, char *value, const utils::field_attributes &attr)
{
data_type_traits<char*>::bind_result_value(*this, column_index_++, value, attr.size());
}
void result_parameter_binder::on_attribute(const char * /*id*/, std::string &value, const utils::field_attributes &attr)
{
data_type_traits<std::string>::bind_result_value(*this, column_index_++, value, attr.size());
}
void result_parameter_binder::on_attribute(const char * /*id*/, any_type &value, data_type_t type, const utils::field_attributes &attr)
{
}
}

View File

@ -97,7 +97,7 @@ bool session::table_exists(const std::string &table_name) const
if (!c.valid()) {
throw std::logic_error("no database connection available");
}
return c->exists(table_name);
return c->exists(dialect_.default_schema_name(), table_name);
}
const table_repository& session::tables() const

View File

@ -2,6 +2,7 @@
#include "matador/sql/parameter_binder.hpp"
#include "matador/sql/query_result_reader.hpp"
#include "matador/sql/result_parameter_binder.hpp"
namespace matador::sql {
@ -15,6 +16,11 @@ void data_type_traits<char>::bind_value(parameter_binder &binder, size_t index,
binder.bind(index, value);
}
void data_type_traits<char>::bind_result_value(result_parameter_binder &binder, size_t index, char &value)
{
binder.bind_result_value(index, value);
}
void data_type_traits<short>::read_value(query_result_reader &reader, const char *id, size_t index, short &value)
{
reader.read_value(id, index, value);
@ -25,6 +31,11 @@ void data_type_traits<short>::bind_value(parameter_binder &binder, size_t index,
binder.bind(index, value);
}
void data_type_traits<short>::bind_result_value(result_parameter_binder &binder, size_t index, short &value)
{
binder.bind_result_value(index, value);
}
void data_type_traits<int>::read_value(query_result_reader &reader, const char *id, size_t index, int &value)
{
reader.read_value(id, index, value);
@ -35,6 +46,11 @@ void data_type_traits<int>::bind_value(parameter_binder &binder, size_t index, i
binder.bind(index, value);
}
void data_type_traits<int>::bind_result_value(result_parameter_binder &binder, size_t index, int &value)
{
binder.bind_result_value(index, value);
}
void data_type_traits<long>::read_value(query_result_reader &reader, const char *id, size_t index, long &value)
{
reader.read_value(id, index, value);
@ -45,6 +61,11 @@ void data_type_traits<long>::bind_value(parameter_binder &binder, size_t index,
binder.bind(index, value);
}
void data_type_traits<long>::bind_result_value(result_parameter_binder &binder, size_t index, long &value)
{
binder.bind_result_value(index, value);
}
void data_type_traits<long long>::read_value(query_result_reader &reader, const char *id, size_t index, long long &value)
{
reader.read_value(id, index, value);
@ -55,6 +76,11 @@ void data_type_traits<long long int>::bind_value(parameter_binder &binder, size_
binder.bind(index, value);
}
void data_type_traits<long long int>::bind_result_value(result_parameter_binder &binder, size_t index, long long int &value)
{
binder.bind_result_value(index, value);
}
void data_type_traits<unsigned char>::read_value(query_result_reader &reader, const char *id, size_t index, unsigned char &value)
{
reader.read_value(id, index, value);
@ -65,6 +91,11 @@ void data_type_traits<unsigned char>::bind_value(parameter_binder &binder, size_
binder.bind(index, value);
}
void data_type_traits<unsigned char>::bind_result_value(result_parameter_binder &binder, size_t index, unsigned char &value)
{
binder.bind_result_value(index, value);
}
void data_type_traits<unsigned short>::read_value(query_result_reader &reader, const char *id, size_t index, unsigned short &value)
{
reader.read_value(id, index, value);
@ -75,6 +106,11 @@ void data_type_traits<unsigned short>::bind_value(parameter_binder &binder, size
binder.bind(index, value);
}
void data_type_traits<unsigned short>::bind_result_value(result_parameter_binder &binder, size_t index, unsigned short &value)
{
binder.bind_result_value(index, value);
}
void data_type_traits<unsigned int>::read_value(query_result_reader &reader, const char *id, size_t index, unsigned int &value)
{
reader.read_value(id, index, value);
@ -85,6 +121,11 @@ void data_type_traits<unsigned int>::bind_value(parameter_binder &binder, size_t
binder.bind(index, value);
}
void data_type_traits<unsigned int>::bind_result_value(result_parameter_binder &binder, size_t index, unsigned int &value)
{
binder.bind_result_value(index, value);
}
void data_type_traits<unsigned long>::read_value(query_result_reader &reader, const char *id, size_t index, unsigned long &value)
{
reader.read_value(id, index, value);
@ -95,16 +136,26 @@ void data_type_traits<unsigned long>::bind_value(parameter_binder &binder, size_
binder.bind(index, value);
}
void data_type_traits<unsigned long>::bind_result_value(result_parameter_binder &binder, size_t index, unsigned long &value)
{
binder.bind_result_value(index, value);
}
void data_type_traits<unsigned long long>::read_value(query_result_reader &reader, const char *id, size_t index, unsigned long long &value)
{
reader.read_value(id, index, value);
}
void data_type_traits<unsigned long long int>::bind_value(parameter_binder &binder, size_t index, unsigned long long int &value)
void data_type_traits<unsigned long long>::bind_value(parameter_binder &binder, size_t index, unsigned long long &value)
{
binder.bind(index, value);
}
void data_type_traits<unsigned long long>::bind_result_value(result_parameter_binder &binder, size_t index, unsigned long long &value)
{
binder.bind_result_value(index, value);
}
void data_type_traits<bool>::read_value(query_result_reader &reader, const char *id, size_t index, bool &value)
{
reader.read_value(id, index, value);
@ -115,6 +166,11 @@ void data_type_traits<bool>::bind_value(parameter_binder &binder, size_t index,
binder.bind(index, value);
}
void data_type_traits<bool>::bind_result_value(result_parameter_binder &binder, size_t index, bool &value)
{
binder.bind_result_value(index, value);
}
void data_type_traits<float>::read_value(query_result_reader &reader, const char *id, size_t index, float &value)
{
reader.read_value(id, index, value);
@ -125,6 +181,11 @@ void data_type_traits<float>::bind_value(parameter_binder &binder, size_t index,
binder.bind(index, value);
}
void data_type_traits<float>::bind_result_value(result_parameter_binder &binder, size_t index, float &value)
{
binder.bind_result_value(index, value);
}
void data_type_traits<double>::read_value(query_result_reader &reader, const char *id, size_t index, double &value)
{
reader.read_value(id, index, value);
@ -135,6 +196,11 @@ void data_type_traits<double>::bind_value(parameter_binder &binder, size_t index
binder.bind(index, value);
}
void data_type_traits<double>::bind_result_value(result_parameter_binder &binder, size_t index, double &value)
{
binder.bind_result_value(index, value);
}
void data_type_traits<const char*>::read_value(query_result_reader &reader, const char *id, size_t index, const char* value, size_t size)
{
reader.read_value(id, index, const_cast<char*>(value), size);
@ -145,6 +211,11 @@ void data_type_traits<const char *>::bind_value(parameter_binder &binder, size_t
binder.bind(index, value, size);
}
void data_type_traits<const char *>::bind_result_value(result_parameter_binder &binder, size_t index, const char *value, size_t size)
{
binder.bind_result_value(index, const_cast<char*>(value), size);
}
void data_type_traits<char*>::read_value(query_result_reader &reader, const char *id, size_t index, char* value, size_t size)
{
reader.read_value(id, index, value, size);
@ -155,6 +226,11 @@ void data_type_traits<char *>::bind_value(parameter_binder &binder, size_t index
binder.bind(index, value, size);
}
void data_type_traits<char *, void>::bind_result_value(result_parameter_binder &binder, size_t index, char *value, size_t size)
{
binder.bind_result_value(index, value, size);
}
void data_type_traits<std::string>::read_value(query_result_reader &reader, const char *id, size_t index, std::string &value, size_t size)
{
reader.read_value(id, index, value, size);
@ -165,4 +241,8 @@ void data_type_traits<std::string>::bind_value(parameter_binder &binder, size_t
binder.bind(index, value, size);
}
void data_type_traits<std::string, void>::bind_result_value(result_parameter_binder &binder, size_t index, std::string &value, size_t size)
{
binder.bind_result_value(index, value, size);
}
}

View File

@ -1,16 +1,25 @@
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/catch_template_test_macros.hpp>
#include "matador/sql/connection.hpp"
#include "Databases.hpp"
using namespace matador::sql;
TEST_CASE("Create connection", "[connection]") {
auto dns = GENERATE(as<std::string>{},
"sqlite://sqlite.db",
"postgres://test:test123@127.0.0.1:5432/matador_test" );
template<class Type>
class ConnectionTestFixture
{
public:
ConnectionTestFixture() = default;
~ConnectionTestFixture() = default;
connection c(dns);
std::string dns() { return Type::dns; }
};
TEMPLATE_TEST_CASE_METHOD(ConnectionTestFixture, "Create connection", "[connection]", Sqlite, Postgres, MySql) {
connection c(ConnectionTestFixture<TestType>::dns());
REQUIRE(!c.is_open());
c.open();

View File

@ -12,4 +12,9 @@ struct Sqlite
constexpr static const char *dns{"sqlite://sqlite.db"};
};
struct MySql
{
constexpr static const char *dns{"mysql://test:test123!@127.0.0.1:3306/matador_test"};
};
#endif //QUERY_DATABASES_HPP

View File

@ -1,20 +1,35 @@
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <catch2/catch_template_test_macros.hpp>
#include <matador/sql/condition.hpp>
#include <matador/sql/session.hpp>
#include "Databases.hpp"
#include <list>
using namespace matador::sql;
TEST_CASE("Create and drop table statement", "[session record]")
template<class Type>
class SessionRecordTestFixture
{
auto dns = GENERATE(as < std::string > {},
"sqlite://sqlite.db",
"postgres://test:test123@127.0.0.1:5432/matador_test");
connection_pool<connection> pool(dns, 4);
session s(pool);
public:
SessionRecordTestFixture()
: pool_(Type::dns, 4), session_(pool_)
{}
matador::sql::session &session()
{ return session_; }
private:
matador::sql::connection_pool<matador::sql::connection> pool_;
matador::sql::session session_;
};
TEMPLATE_TEST_CASE_METHOD(SessionRecordTestFixture, "Create and drop table statement", "[session record]", Sqlite, Postgres, MySql)
{
auto &s = SessionRecordTestFixture<TestType>::session();
REQUIRE(!s.table_exists("person"));
s.create()