added mysql backend

This commit is contained in:
2023-12-17 22:11:43 +01:00
parent 2893f749b6
commit 320d06bb20
39 changed files with 1272 additions and 46 deletions
+35
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})
@@ -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
+19
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
+20
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
@@ -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
@@ -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
@@ -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
@@ -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
+248
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;
}
}
+20
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;
}
+30
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());
}
}
}
@@ -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_;
}
}
@@ -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;
}
}
@@ -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;
}
}
+53
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_;
}
}