0
0
mirror of https://github.com/rbock/sqlpp11.git synced 2024-11-15 20:31:16 +08:00

Make mysql connector header-only

This commit is contained in:
Roland Bock 2021-08-15 16:17:35 +02:00
parent 7aa4edc0ea
commit 660e3bd1b6
18 changed files with 1063 additions and 1342 deletions

View File

@ -27,8 +27,10 @@
#ifndef SQLPP_MYSQL_BIND_RESULT_H #ifndef SQLPP_MYSQL_BIND_RESULT_H
#define SQLPP_MYSQL_BIND_RESULT_H #define SQLPP_MYSQL_BIND_RESULT_H
#include <iostream>
#include <memory> #include <memory>
#include <sqlpp11/chrono.h> #include <sqlpp11/chrono.h>
#include <sqlpp11/mysql/sqlpp_mysql.h>
namespace sqlpp namespace sqlpp
{ {
@ -36,8 +38,73 @@ namespace sqlpp
{ {
namespace detail namespace detail
{ {
struct prepared_statement_handle_t; struct result_meta_data_t
{
size_t index;
unsigned long bound_len;
my_bool bound_is_null;
my_bool bound_error;
std::vector<char> bound_text_buffer; // also for blobs
const char** text_buffer;
size_t* len;
bool* is_null;
};
struct prepared_statement_handle_t
{
struct wrapped_bool
{
my_bool value;
wrapped_bool() : value(false)
{
} }
wrapped_bool(bool v) : value(v)
{
}
wrapped_bool(const wrapped_bool&) = default;
wrapped_bool(wrapped_bool&&) = default;
wrapped_bool& operator=(const wrapped_bool&) = default;
wrapped_bool& operator=(wrapped_bool&&) = default;
~wrapped_bool() = default;
};
MYSQL_STMT* mysql_stmt;
std::vector<MYSQL_BIND> stmt_params;
std::vector<MYSQL_TIME> stmt_date_time_param_buffer;
std::vector<wrapped_bool> stmt_param_is_null; // my_bool is bool after 8.0, and vector<bool> is bad
std::vector<MYSQL_BIND> result_params;
std::vector<result_meta_data_t> result_param_meta_data;
bool debug;
prepared_statement_handle_t(MYSQL_STMT* stmt, size_t no_of_parameters, size_t no_of_columns, bool debug_)
: mysql_stmt(stmt),
stmt_params(no_of_parameters, MYSQL_BIND{}),
stmt_date_time_param_buffer(no_of_parameters, MYSQL_TIME{}),
stmt_param_is_null(no_of_parameters, false),
result_params(no_of_columns, MYSQL_BIND{}),
result_param_meta_data(no_of_columns, result_meta_data_t{}),
debug(debug_)
{
}
prepared_statement_handle_t(const prepared_statement_handle_t&) = delete;
prepared_statement_handle_t(prepared_statement_handle_t&&) = default;
prepared_statement_handle_t& operator=(const prepared_statement_handle_t&) = delete;
prepared_statement_handle_t& operator=(prepared_statement_handle_t&&) = default;
~prepared_statement_handle_t()
{
if (mysql_stmt)
mysql_stmt_close(mysql_stmt);
}
bool operator!() const
{
return !mysql_stmt;
}
};
} // namespace detail
class bind_result_t class bind_result_t
{ {
@ -46,12 +113,20 @@ namespace sqlpp
public: public:
bind_result_t() = default; bind_result_t() = default;
bind_result_t(const std::shared_ptr<detail::prepared_statement_handle_t>& handle); bind_result_t(const std::shared_ptr<detail::prepared_statement_handle_t>& handle) : _handle(handle)
{
if (_handle and _handle->debug)
std::cerr << "MySQL debug: Constructing bind result, using handle at " << _handle.get() << std::endl;
}
bind_result_t(const bind_result_t&) = delete; bind_result_t(const bind_result_t&) = delete;
bind_result_t(bind_result_t&& rhs) = default; bind_result_t(bind_result_t&& rhs) = default;
bind_result_t& operator=(const bind_result_t&) = delete; bind_result_t& operator=(const bind_result_t&) = delete;
bind_result_t& operator=(bind_result_t&&) = default; bind_result_t& operator=(bind_result_t&&) = default;
~bind_result_t(); ~bind_result_t()
{
if (_handle)
mysql_stmt_free_result(_handle->mysql_stmt);
}
bool operator==(const bind_result_t& rhs) const bool operator==(const bind_result_t& rhs) const
{ {
@ -88,16 +163,187 @@ namespace sqlpp
} }
} }
bool _invalid() const; bool _invalid() const
{
return !_handle or !*_handle;
}
void _bind_boolean_result(size_t index, signed char* value, bool* is_null); void _bind_boolean_result(size_t index, signed char* value, bool* is_null)
void _bind_floating_point_result(size_t index, double* value, bool* is_null); {
void _bind_integral_result(size_t index, int64_t* value, bool* is_null); if (_handle->debug)
void _bind_unsigned_integral_result(size_t index, uint64_t* value, bool* is_null); std::cerr << "MySQL debug: binding boolean result " << static_cast<void*>(value) << " at index: " << index
void _bind_text_result(size_t index, const char** text, size_t* len); << std::endl;
void _bind_blob_result(size_t index, const char** text, size_t* len);
void _bind_date_result(size_t index, ::sqlpp::chrono::day_point* value, bool* is_null); detail::result_meta_data_t& meta_data = _handle->result_param_meta_data[index];
void _bind_date_time_result(size_t index, ::sqlpp::chrono::microsecond_point* value, bool* is_null); meta_data.index = index;
meta_data.len = nullptr;
meta_data.is_null = is_null;
MYSQL_BIND& param = _handle->result_params[index];
param.buffer_type = MYSQL_TYPE_TINY;
param.buffer = value;
param.buffer_length = sizeof(*value);
param.length = &meta_data.bound_len;
param.is_null = &meta_data.bound_is_null;
param.is_unsigned = false;
param.error = &meta_data.bound_error;
}
void _bind_integral_result(size_t index, int64_t* value, bool* is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding integral result " << static_cast<void*>(value) << " at index: " << index
<< std::endl;
detail::result_meta_data_t& meta_data = _handle->result_param_meta_data[index];
meta_data.index = index;
meta_data.len = nullptr;
meta_data.is_null = is_null;
MYSQL_BIND& param = _handle->result_params[index];
param.buffer_type = MYSQL_TYPE_LONGLONG;
param.buffer = value;
param.buffer_length = sizeof(*value);
param.length = &meta_data.bound_len;
param.is_null = &meta_data.bound_is_null;
param.is_unsigned = false;
param.error = &meta_data.bound_error;
}
void _bind_unsigned_integral_result(size_t index, uint64_t* value, bool* is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding unsigned integral result " << static_cast<void*>(value)
<< " at index: " << index << std::endl;
detail::result_meta_data_t& meta_data = _handle->result_param_meta_data[index];
meta_data.index = index;
meta_data.len = nullptr;
meta_data.is_null = is_null;
MYSQL_BIND& param = _handle->result_params[index];
param.buffer_type = MYSQL_TYPE_LONGLONG;
param.buffer = value;
param.buffer_length = sizeof(*value);
param.length = &meta_data.bound_len;
param.is_null = &meta_data.bound_is_null;
param.is_unsigned = true;
param.error = &meta_data.bound_error;
}
void _bind_floating_point_result(size_t index, double* value, bool* is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding floating point result " << static_cast<void*>(value)
<< " at index: " << index << std::endl;
detail::result_meta_data_t& meta_data = _handle->result_param_meta_data[index];
meta_data.index = index;
meta_data.len = nullptr;
meta_data.is_null = is_null;
MYSQL_BIND& param = _handle->result_params[index];
param.buffer_type = MYSQL_TYPE_DOUBLE;
param.buffer = value;
param.buffer_length = sizeof(*value);
param.length = &meta_data.bound_len;
param.is_null = &meta_data.bound_is_null;
param.is_unsigned = false;
param.error = &meta_data.bound_error;
}
void _bind_text_result(size_t index, const char** value, size_t* len)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding text result " << static_cast<const void*>(*value) << " at index: " << index
<< std::endl;
detail::result_meta_data_t& meta_data = _handle->result_param_meta_data[index];
meta_data.index = index;
meta_data.len = len;
meta_data.is_null = nullptr;
meta_data.text_buffer = value;
if (meta_data.bound_text_buffer.empty())
meta_data.bound_text_buffer.resize(8);
MYSQL_BIND& param = _handle->result_params[index];
param.buffer_type = MYSQL_TYPE_STRING;
param.buffer = meta_data.bound_text_buffer.data();
param.buffer_length = meta_data.bound_text_buffer.size();
param.length = &meta_data.bound_len;
param.is_null = &meta_data.bound_is_null;
param.is_unsigned = false;
param.error = &meta_data.bound_error;
}
void _bind_blob_result(size_t index, const char** value, size_t* len)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding text result " << static_cast<const void*>(*value) << " at index: " << index
<< std::endl;
detail::result_meta_data_t& meta_data = _handle->result_param_meta_data[index];
meta_data.index = index;
meta_data.len = len;
meta_data.is_null = nullptr;
meta_data.text_buffer = value;
if (meta_data.bound_text_buffer.empty())
meta_data.bound_text_buffer.resize(8);
MYSQL_BIND& param = _handle->result_params[index];
param.buffer_type = MYSQL_TYPE_BLOB;
param.buffer = meta_data.bound_text_buffer.data();
param.buffer_length = meta_data.bound_text_buffer.size();
param.length = &meta_data.bound_len;
param.is_null = &meta_data.bound_is_null;
param.is_unsigned = false;
param.error = &meta_data.bound_error;
}
void _bind_date_result(size_t index, ::sqlpp::chrono::day_point* value, bool* is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding date result " << static_cast<void*>(value) << " at index: " << index
<< std::endl;
detail::result_meta_data_t& meta_data = _handle->result_param_meta_data[index];
meta_data.index = index;
meta_data.len = nullptr;
meta_data.is_null = is_null;
meta_data.text_buffer = nullptr;
meta_data.bound_text_buffer.resize(sizeof(MYSQL_TIME));
MYSQL_BIND& param = _handle->result_params[index];
param.buffer_type = MYSQL_TYPE_DATE;
param.buffer = meta_data.bound_text_buffer.data();
param.buffer_length = meta_data.bound_text_buffer.size();
param.length = &meta_data.bound_len;
param.is_null = &meta_data.bound_is_null;
param.is_unsigned = false;
param.error = &meta_data.bound_error;
}
void _bind_date_time_result(size_t index, ::sqlpp::chrono::microsecond_point* value, bool* is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding date time result " << static_cast<void*>(value) << " at index: " << index
<< std::endl;
detail::result_meta_data_t& meta_data = _handle->result_param_meta_data[index];
meta_data.index = index;
meta_data.len = nullptr;
meta_data.is_null = is_null;
meta_data.text_buffer = nullptr;
meta_data.bound_text_buffer.resize(sizeof(MYSQL_TIME));
MYSQL_BIND& param = _handle->result_params[index];
param.buffer_type = MYSQL_TYPE_DATETIME;
param.buffer = meta_data.bound_text_buffer.data();
param.buffer_length = meta_data.bound_text_buffer.size();
param.length = &meta_data.bound_len;
param.is_null = &meta_data.bound_is_null;
param.is_unsigned = false;
param.error = &meta_data.bound_error;
}
void _post_bind_boolean_result(size_t /* index */, signed char* /* value */, bool* /* is_null */) void _post_bind_boolean_result(size_t /* index */, signed char* /* value */, bool* /* is_null */)
{ {
@ -114,13 +360,118 @@ namespace sqlpp
void _post_bind_text_result(size_t /* index */, const char** /* text */, size_t* /* len */) void _post_bind_text_result(size_t /* index */, const char** /* text */, size_t* /* len */)
{ {
} }
void _post_bind_date_result(size_t index, ::sqlpp::chrono::day_point* value, bool* is_null); void _post_bind_date_result(size_t index, ::sqlpp::chrono::day_point* value, bool* is_null)
void _post_bind_date_time_result(size_t index, ::sqlpp::chrono::microsecond_point* value, bool* is_null); {
if (_handle->debug)
std::cerr << "MySQL debug: post binding date result " << static_cast<void*>(value) << " at index: " << index
<< std::endl;
if (not *is_null)
{
const auto& dt =
*reinterpret_cast<const MYSQL_TIME*>(_handle->result_param_meta_data[index].bound_text_buffer.data());
*is_null = false;
*value = ::date::year(dt.year) / ::date::month(dt.month) / ::date::day(dt.day);
}
}
void _post_bind_date_time_result(size_t index, ::sqlpp::chrono::microsecond_point* value, bool* is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding date time result " << static_cast<void*>(value) << " at index: " << index
<< std::endl;
if (not *is_null)
{
const auto& dt =
*reinterpret_cast<const MYSQL_TIME*>(_handle->result_param_meta_data[index].bound_text_buffer.data());
*is_null = false;
*value = ::sqlpp::chrono::day_point(::date::year(dt.year) / ::date::month(dt.month) / ::date::day(dt.day)) +
std::chrono::hours(dt.hour) + std::chrono::minutes(dt.minute) + std::chrono::seconds(dt.second) +
std::chrono::microseconds(dt.second_part);
}
}
private: private:
void bind_impl(); void bind_impl()
bool next_impl(); {
}; if (_handle->debug)
std::cerr << "MySQL debug: Binding results for handle at " << _handle.get() << std::endl;
if (mysql_stmt_bind_result(_handle->mysql_stmt, _handle->result_params.data()))
{
throw sqlpp::exception(std::string("MySQL: mysql_stmt_bind_result: ") +
mysql_stmt_error(_handle->mysql_stmt));
} }
} }
bool next_impl()
{
if (_handle->debug)
std::cerr << "MySQL debug: Accessing next row of handle at " << _handle.get() << std::endl;
auto flag = mysql_stmt_fetch(_handle->mysql_stmt);
switch (flag)
{
case 0:
case MYSQL_DATA_TRUNCATED:
{
bool need_to_rebind = false;
for (auto& r : _handle->result_param_meta_data)
{
if (r.len)
{
if (r.bound_is_null)
{
*r.text_buffer = nullptr;
*r.len = 0;
}
else
{
if (r.bound_len > r.bound_text_buffer.size())
{
if (_handle->debug)
std::cerr << "MySQL debug: Need to reallocate buffer " << static_cast<const void*>(*r.text_buffer)
<< " at index " << r.index << " for handle at " << _handle.get() << std::endl;
need_to_rebind = true;
r.bound_text_buffer.resize(r.bound_len);
MYSQL_BIND& param = _handle->result_params[r.index];
param.buffer = r.bound_text_buffer.data();
param.buffer_length = r.bound_text_buffer.size();
auto err = mysql_stmt_fetch_column(_handle->mysql_stmt, &param, r.index, 0);
if (err)
throw sqlpp::exception(std::string("MySQL: Fetch column after reallocate failed: ") +
"error-code: " + std::to_string(err) +
", stmt-error: " + mysql_stmt_error(_handle->mysql_stmt) +
", stmt-errno: " + std::to_string(mysql_stmt_errno(_handle->mysql_stmt)));
}
*r.text_buffer = r.bound_text_buffer.data();
if (_handle->debug)
std::cerr << "MySQL debug: New buffer " << static_cast<const void*>(*r.text_buffer) << " at index "
<< r.index << " for handle at " << _handle.get() << std::endl;
*r.len = r.bound_len;
}
}
if (r.is_null)
*r.is_null = r.bound_is_null;
}
if (need_to_rebind)
bind_impl();
}
return true;
case 1:
throw sqlpp::exception(std::string("MySQL: Could not fetch next result: ") +
mysql_stmt_error(_handle->mysql_stmt));
case MYSQL_NO_DATA:
return false;
default:
throw sqlpp::exception("MySQL: Unexpected return value for mysql_stmt_fetch()");
}
}
};
} // namespace mysql
} // namespace sqlpp
#endif #endif

View File

@ -29,9 +29,11 @@
#include <ciso646> #include <ciso646>
#include <cstdlib> #include <cstdlib>
#include <iostream>
#include <memory> #include <memory>
#include <sqlpp11/chrono.h> #include <sqlpp11/chrono.h>
#include <sqlpp11/exception.h> #include <sqlpp11/exception.h>
#include <sqlpp11/mysql/sqlpp_mysql.h>
#include <sqlpp11/mysql/char_result_row.h> #include <sqlpp11/mysql/char_result_row.h>
namespace sqlpp namespace sqlpp
@ -40,22 +42,80 @@ namespace sqlpp
{ {
namespace detail namespace detail
{ {
struct result_handle; struct result_handle
{
MYSQL_RES* mysql_res;
bool debug;
result_handle(MYSQL_RES* res, bool debug_) : mysql_res(res), debug(debug_)
{
} }
result_handle(const result_handle&) = delete;
result_handle(result_handle&&) = default;
result_handle& operator=(const result_handle&) = delete;
result_handle& operator=(result_handle&&) = default;
~result_handle()
{
if (mysql_res)
mysql_free_result(mysql_res);
}
bool operator!() const
{
return !mysql_res;
}
};
const auto date_digits = std::vector<char>{1, 1, 1, 1, 0, 1, 1, 0, 1, 1}; // 2015-10-28
const auto time_digits = std::vector<char>{0, 1, 1, 0, 1, 1, 0, 1, 1}; // T23:00:12
auto check_digits(const char* text, const std::vector<char>& digitFlags) -> bool
{
for (const auto digitFlag : digitFlags)
{
if (digitFlag)
{
if (not std::isdigit(*text))
{
return false;
}
}
else
{
if (std::isdigit(*text) or *text == '\0')
{
return false;
}
}
++text;
}
return true;
}
} // namespace detail
class char_result_t class char_result_t
{ {
std::unique_ptr<detail::result_handle> _handle; std::unique_ptr<detail::result_handle> _handle;
char_result_row_t _char_result_row; char_result_row_t _char_result_row;
public: public:
char_result_t(); char_result_t() = default;
char_result_t(std::unique_ptr<detail::result_handle>&& handle); char_result_t(std::unique_ptr<detail::result_handle>&& handle) : _handle(std::move(handle))
{
if (_invalid())
throw sqlpp::exception("MySQL: Constructing char_result without valid handle");
if (_handle->debug)
std::cerr << "MySQL debug: Constructing result, using handle at " << _handle.get() << std::endl;
}
char_result_t(const char_result_t&) = delete; char_result_t(const char_result_t&) = delete;
char_result_t(char_result_t&& rhs); char_result_t(char_result_t&& rhs) = default;
char_result_t& operator=(const char_result_t&) = delete; char_result_t& operator=(const char_result_t&) = delete;
char_result_t& operator=(char_result_t&&); char_result_t& operator=(char_result_t&&) = default;
~char_result_t(); ~char_result_t() = default;
bool operator==(const char_result_t& rhs) const bool operator==(const char_result_t& rhs) const
{ {
@ -86,7 +146,10 @@ namespace sqlpp
} }
} }
bool _invalid() const; bool _invalid() const
{
return !_handle or !*_handle;
}
void _bind_boolean_result(size_t index, signed char* value, bool* is_null) void _bind_boolean_result(size_t index, signed char* value, bool* is_null)
{ {
@ -127,12 +190,101 @@ namespace sqlpp
*len = (is_null ? 0 : _char_result_row.len[index]); *len = (is_null ? 0 : _char_result_row.len[index]);
} }
void _bind_date_result(size_t index, ::sqlpp::chrono::day_point* value, bool* is_null); void _bind_date_result(size_t index, ::sqlpp::chrono::day_point* value, bool* is_null)
void _bind_date_time_result(size_t index, ::sqlpp::chrono::microsecond_point* value, bool* is_null); {
if (_handle->debug)
std::cerr << "MySQL debug: parsing date result at index: " << index << std::endl;
*is_null = (_char_result_row.data == nullptr or _char_result_row.data[index] == nullptr);
if (*is_null)
{
*value = {};
return;
}
const auto date_string = _char_result_row.data[index];
if (_handle->debug)
std::cerr << "MySQL debug: date string: " << date_string << std::endl;
if (detail::check_digits(date_string, detail::date_digits))
{
const auto ymd = ::date::year(std::atoi(date_string)) / atoi(date_string + 5) / atoi(date_string + 8);
*value = ::sqlpp::chrono::day_point(ymd);
}
else
{
if (_handle->debug)
std::cerr << "MySQL debug: invalid date result: " << date_string << std::endl;
*value = {};
}
}
void _bind_date_time_result(size_t index, ::sqlpp::chrono::microsecond_point* value, bool* is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: parsing date result at index: " << index << std::endl;
*is_null = (_char_result_row.data == nullptr or _char_result_row.data[index] == nullptr);
if (*is_null)
{
*value = {};
return;
}
const auto date_time_string = _char_result_row.data[index];
if (_handle->debug)
std::cerr << "MySQL debug: date_time string: " << date_time_string << std::endl;
if (detail::check_digits(date_time_string, detail::date_digits))
{
const auto ymd =
::date::year(std::atoi(date_time_string)) / atoi(date_time_string + 5) / atoi(date_time_string + 8);
*value = ::sqlpp::chrono::day_point(ymd);
}
else
{
if (_handle->debug)
std::cerr << "MySQL debug: invalid date_time result: " << date_time_string << std::endl;
*value = {};
return;
}
const auto time_string = date_time_string + 10;
if (detail::check_digits(time_string, detail::time_digits))
{
*value += ::std::chrono::hours(std::atoi(time_string + 1)) +
std::chrono::minutes(std::atoi(time_string + 4)) + std::chrono::seconds(std::atoi(time_string + 7));
}
else
{
return;
}
const auto mu_string = time_string + 9;
if (mu_string[0] == '\0')
{
return;
}
auto factor = 100 * 1000;
for (auto i = 1u; i <= 6u and std::isdigit(mu_string[i]); ++i, factor /= 10)
{
*value += ::std::chrono::microseconds(factor * (mu_string[i] - '0'));
}
}
private: private:
bool next_impl(); bool next_impl()
}; {
if (_handle->debug)
std::cerr << "MySQL debug: Accessing next row of handle at " << _handle.get() << std::endl;
_char_result_row.data = const_cast<const char**>(mysql_fetch_row(_handle->mysql_res));
_char_result_row.len = mysql_fetch_lengths(_handle->mysql_res);
return _char_result_row.data;
} }
} };
} // namespace mysql
} // namespace sqlpp
#endif #endif

View File

@ -35,34 +35,187 @@
#include <sqlpp11/mysql/remove.h> #include <sqlpp11/mysql/remove.h>
#include <sqlpp11/mysql/update.h> #include <sqlpp11/mysql/update.h>
#include <sqlpp11/serialize.h> #include <sqlpp11/serialize.h>
#include <sqlpp11/mysql/sqlpp_mysql.h>
#include <iostream>
#include <sstream> #include <sstream>
#include <string> #include <string>
#if LIBMYSQL_VERSION_ID < 80000
typedef struct st_mysql MYSQL;
#else
struct MYSQL;
#endif
namespace sqlpp namespace sqlpp
{ {
namespace mysql namespace mysql
{ {
namespace detail
{
struct MySqlThreadInitializer
{
MySqlThreadInitializer()
{
if (!mysql_thread_safe())
{
throw sqlpp::exception("MySQL error: Operating on a non-threadsafe client");
}
mysql_thread_init();
}
~MySqlThreadInitializer()
{
mysql_thread_end();
}
};
void thread_init()
{
thread_local MySqlThreadInitializer threadInitializer;
}
void connect(MYSQL* mysql, const connection_config& config)
{
if (!mysql_real_connect(mysql, config.host.empty() ? nullptr : config.host.c_str(),
config.user.empty() ? nullptr : config.user.c_str(),
config.password.empty() ? nullptr : config.password.c_str(), nullptr, config.port,
config.unix_socket.empty() ? nullptr : config.unix_socket.c_str(), config.client_flag))
{
throw sqlpp::exception("MySQL: could not connect to server: " + std::string(mysql_error(mysql)));
}
if (mysql_set_character_set(mysql, config.charset.c_str()))
{
throw sqlpp::exception("MySQL error: can't set character set " + config.charset);
}
if (not config.database.empty() and mysql_select_db(mysql, config.database.c_str()))
{
throw sqlpp::exception("MySQL error: can't select database '" + config.database + "'");
}
}
void handle_cleanup(MYSQL* mysql)
{
mysql_close(mysql);
}
struct connection_handle_t
{
const std::shared_ptr<connection_config> config;
std::unique_ptr<MYSQL, void (*)(MYSQL*)> mysql;
connection_handle_t(const std::shared_ptr<connection_config>& conf)
: config(conf), mysql(mysql_init(nullptr), handle_cleanup)
{
if (not mysql)
{
throw sqlpp::exception("MySQL: could not init mysql data structure");
}
if (config->auto_reconnect)
{
my_bool my_true = true;
if (mysql_options(mysql.get(), MYSQL_OPT_RECONNECT, &my_true))
{
throw sqlpp::exception("MySQL: could not set option MYSQL_OPT_RECONNECT");
}
}
connect(mysql.get(), *config);
}
~connection_handle_t() noexcept = default;
connection_handle_t(const connection_handle_t&) = delete;
connection_handle_t(connection_handle_t&&) = delete;
connection_handle_t& operator=(const connection_handle_t&) = delete;
connection_handle_t& operator=(connection_handle_t&&) = delete;
bool is_valid()
{
return mysql_ping(mysql.get()) == 0;
}
void reconnect()
{
connect(mysql.get(), *config);
}
};
void execute_statement(detail::connection_handle_t& handle, const std::string& statement)
{
thread_init();
if (handle.config->debug)
std::cerr << "MySQL debug: Executing: '" << statement << "'" << std::endl;
if (mysql_query(handle.mysql.get(), statement.c_str()))
{
throw sqlpp::exception(
"MySQL error: Could not execute MySQL-statement: " + std::string(mysql_error(handle.mysql.get())) +
" (statement was >>" + statement + "<<\n");
}
}
void execute_prepared_statement(detail::prepared_statement_handle_t& prepared_statement)
{
thread_init();
if (prepared_statement.debug)
std::cerr << "MySQL debug: Executing prepared_statement" << std::endl;
if (mysql_stmt_bind_param(prepared_statement.mysql_stmt, prepared_statement.stmt_params.data()))
{
throw sqlpp::exception(std::string("MySQL error: Could not bind parameters to statement") +
mysql_stmt_error(prepared_statement.mysql_stmt));
}
if (mysql_stmt_execute(prepared_statement.mysql_stmt))
{
throw sqlpp::exception(std::string("MySQL error: Could not execute prepared statement: ") +
mysql_stmt_error(prepared_statement.mysql_stmt));
}
}
std::shared_ptr<detail::prepared_statement_handle_t> prepare_statement(detail::connection_handle_t& handle,
const std::string& statement,
size_t no_of_parameters,
size_t no_of_columns)
{
thread_init();
if (handle.config->debug)
std::cerr << "MySQL debug: Preparing: '" << statement << "'" << std::endl;
auto prepared_statement = std::make_shared<detail::prepared_statement_handle_t>(
mysql_stmt_init(handle.mysql.get()), no_of_parameters, no_of_columns, handle.config->debug);
if (not prepared_statement)
{
throw sqlpp::exception("MySQL error: Could not allocate prepared statement\n");
}
if (mysql_stmt_prepare(prepared_statement->mysql_stmt, statement.data(), statement.size()))
{
throw sqlpp::exception(
"MySQL error: Could not prepare statement: " + std::string(mysql_error(handle.mysql.get())) +
" (statement was >>" + statement + "<<\n");
}
return prepared_statement;
}
} // namespace detail
struct scoped_library_initializer_t struct scoped_library_initializer_t
{ {
// calls mysql_library_init scoped_library_initializer_t(int argc = 0, char** argv = nullptr, char** groups = nullptr)
scoped_library_initializer_t(int argc = 0, char** argv = nullptr, char** groups = nullptr); {
mysql_library_init(argc, argv, groups);
}
// calls mysql_library_end ~scoped_library_initializer_t()
~scoped_library_initializer_t(); {
mysql_library_end();
}
}; };
// This will also cleanup when the program shuts down // This will also cleanup when the program shuts down
void global_library_init(int argc = 0, char** argv = nullptr, char** groups = nullptr); void global_library_init(int argc = 0, char** argv = nullptr, char** groups = nullptr)
namespace detail
{ {
struct connection_handle_t; static const auto global_init_and_end = scoped_library_initializer_t(argc, argv, groups);
} }
class connection; class connection;
@ -100,17 +253,68 @@ namespace sqlpp
bool _transaction_active = false; bool _transaction_active = false;
// direct execution // direct execution
char_result_t select_impl(const std::string& statement); char_result_t select_impl(const std::string& statement)
size_t insert_impl(const std::string& statement); {
size_t update_impl(const std::string& statement); execute_statement(*_handle, statement);
size_t remove_impl(const std::string& statement); std::unique_ptr<detail::result_handle> result_handle(
new detail::result_handle(mysql_store_result(_handle->mysql.get()), _handle->config->debug));
if (!*result_handle)
{
throw sqlpp::exception("MySQL error: Could not store result set: " +
std::string(mysql_error(_handle->mysql.get())));
}
return {std::move(result_handle)};
}
size_t insert_impl(const std::string& statement)
{
execute_statement(*_handle, statement);
return mysql_insert_id(_handle->mysql.get());
}
size_t update_impl(const std::string& statement)
{
execute_statement(*_handle, statement);
return mysql_affected_rows(_handle->mysql.get());
}
size_t remove_impl(const std::string& statement)
{
execute_statement(*_handle, statement);
return mysql_affected_rows(_handle->mysql.get());
}
// prepared execution // prepared execution
prepared_statement_t prepare_impl(const std::string& statement, size_t no_of_parameters, size_t no_of_columns); prepared_statement_t prepare_impl(const std::string& statement, size_t no_of_parameters, size_t no_of_columns)
bind_result_t run_prepared_select_impl(prepared_statement_t& prepared_statement); {
size_t run_prepared_insert_impl(prepared_statement_t& prepared_statement); return prepare_statement(*_handle, statement, no_of_parameters, no_of_columns);
size_t run_prepared_update_impl(prepared_statement_t& prepared_statement); }
size_t run_prepared_remove_impl(prepared_statement_t& prepared_statement);
bind_result_t run_prepared_select_impl(prepared_statement_t& prepared_statement)
{
execute_prepared_statement(*prepared_statement._handle);
return prepared_statement._handle;
}
size_t run_prepared_insert_impl(prepared_statement_t& prepared_statement)
{
execute_prepared_statement(*prepared_statement._handle);
return mysql_stmt_insert_id(prepared_statement._handle->mysql_stmt);
}
size_t run_prepared_update_impl(prepared_statement_t& prepared_statement)
{
execute_prepared_statement(*prepared_statement._handle);
return mysql_stmt_affected_rows(prepared_statement._handle->mysql_stmt);
}
size_t run_prepared_remove_impl(prepared_statement_t& prepared_statement)
{
execute_prepared_statement(*prepared_statement._handle);
return mysql_stmt_affected_rows(prepared_statement._handle->mysql_stmt);
}
public: public:
using _prepared_statement_t = ::sqlpp::mysql::prepared_statement_t; using _prepared_statement_t = ::sqlpp::mysql::prepared_statement_t;
@ -135,21 +339,41 @@ namespace sqlpp
return serialize(t, context); return serialize(t, context);
} }
connection(const std::shared_ptr<connection_config>& config); connection(const std::shared_ptr<connection_config>& config) : _handle(new detail::connection_handle_t(config))
~connection(); {
}
~connection() = default;
connection(const connection&) = delete; connection(const connection&) = delete;
connection& operator=(const connection&) = delete; connection& operator=(const connection&) = delete;
connection& operator=(connection&&) = default; connection& operator=(connection&&) = default;
connection(connection&& other); connection(connection&& other)
{
this->_transaction_active = other._transaction_active;
this->_handle = std::move(other._handle);
}
bool is_valid(); bool is_valid()
void reconnect(); {
const std::shared_ptr<connection_config> get_config(); return _handle->is_valid();
}
void reconnect()
{
return _handle->reconnect();
}
const std::shared_ptr<connection_config> get_config()
{
return _handle->config;
}
bool is_transaction_active() bool is_transaction_active()
{ {
return _transaction_active; return _transaction_active;
} }
template <typename Select> template <typename Select>
char_result_t select(const Select& s) char_result_t select(const Select& s)
{ {
@ -246,10 +470,18 @@ namespace sqlpp
} }
//! execute arbitrary command (e.g. create a table) //! execute arbitrary command (e.g. create a table)
void execute(const std::string& command); void execute(const std::string& command)
{
execute_statement(*_handle, command);
}
//! escape given string (does not quote, though) //! escape given string (does not quote, though)
std::string escape(const std::string& s) const; std::string escape(const std::string& s) const
{
std::unique_ptr<char[]> dest(new char[s.size() * 2 + 1]);
mysql_real_escape_string(_handle->mysql.get(), dest.get(), s.c_str(), s.size());
return dest.get();
}
//! call run on the argument //! call run on the argument
template <typename T> template <typename T>
@ -291,27 +523,60 @@ namespace sqlpp
} }
//! start transaction //! start transaction
void start_transaction(); void start_transaction()
{
if (_transaction_active)
{
throw sqlpp::exception("MySQL: Cannot have more than one open transaction per connection");
}
execute_statement(*_handle, "START TRANSACTION");
_transaction_active = true;
}
//! commit transaction (or throw if the transaction has been finished already) //! commit transaction (or throw if the transaction has been finished already)
void commit_transaction(); void commit_transaction()
{
if (not _transaction_active)
{
throw sqlpp::exception("MySQL: Cannot commit a finished or failed transaction");
}
_transaction_active = false;
execute_statement(*_handle, "COMMIT");
}
//! rollback transaction with or without reporting the rollback (or throw if the transaction has been finished //! rollback transaction (or throw if the transaction has been finished already)
// already) void rollback_transaction(bool report)
void rollback_transaction(bool report); {
if (not _transaction_active)
{
throw sqlpp::exception("MySQL: Cannot rollback a finished or failed transaction");
}
if (report)
{
std::cerr << "MySQL warning: Rolling back unfinished transaction" << std::endl;
}
_transaction_active = false;
execute_statement(*_handle, "ROLLBACK");
}
//! report a rollback failure (will be called by transactions in case of a rollback failure in the destructor) //! report a rollback failure (will be called by transactions in case of a rollback failure in the destructor)
void report_rollback_failure(const std::string message) noexcept; void report_rollback_failure(const std::string message) noexcept
{
std::cerr << "MySQL message:" << message << std::endl;
}
MYSQL* get_handle(); MYSQL* get_handle()
{
return _handle->mysql.get();
}
}; };
inline std::string serializer_t::escape(std::string arg) inline std::string serializer_t::escape(std::string arg)
{ {
return _db.escape(arg); return _db.escape(arg);
} }
} } // namespace mysql
} } // namespace sqlpp
#include <sqlpp11/mysql/serializer.h> #include <sqlpp11/mysql/serializer.h>

View File

@ -1,320 +0,0 @@
/*
* Copyright (c) 2013 - 2017, Roland Bock
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SQLPP_MYSQL_CONNECTION_H
#define SQLPP_MYSQL_CONNECTION_H
#include <sqlpp11/connection.h>
#include <sqlpp11/mysql/bind_result.h>
#include <sqlpp11/mysql/char_result.h>
#include <sqlpp11/mysql/connection_config.h>
#include <sqlpp11/mysql/prepared_statement.h>
#include <sqlpp11/serialize.h>
#include <sstream>
#include <string>
<<<<<<< Updated upstream
#if LIBMYSQL_VERSION_ID < 80000
typedef struct st_mysql MYSQL;
#else
struct MYSQL;
#endif
=======
struct MYSQL;
>>>>>>> Stashed changes
namespace sqlpp
{
namespace mysql
{
struct scoped_library_initializer_t
{
// calls mysql_library_init
scoped_library_initializer_t(int argc = 0, char** argv = nullptr, char** groups = nullptr);
// calls mysql_library_end
~scoped_library_initializer_t();
};
// This will also cleanup when the program shuts down
void global_library_init(int argc = 0, char** argv = nullptr, char** groups = nullptr);
namespace detail
{
struct connection_handle_t;
}
class connection;
struct serializer_t
{
serializer_t(const connection& db) : _db(db)
{
}
template <typename T>
std::ostream& operator<<(T t)
{
return _os << t;
}
std::string escape(std::string arg);
std::string str() const
{
return _os.str();
}
const connection& _db;
std::stringstream _os;
};
std::integral_constant<char, '`'> get_quote_left(const serializer_t&);
std::integral_constant<char, '`'> get_quote_right(const serializer_t&);
class connection : public sqlpp::connection
{
std::unique_ptr<detail::connection_handle_t> _handle;
bool _transaction_active = false;
// direct execution
char_result_t select_impl(const std::string& statement);
size_t insert_impl(const std::string& statement);
size_t update_impl(const std::string& statement);
size_t remove_impl(const std::string& statement);
// prepared execution
prepared_statement_t prepare_impl(const std::string& statement, size_t no_of_parameters, size_t no_of_columns);
bind_result_t run_prepared_select_impl(prepared_statement_t& prepared_statement);
size_t run_prepared_insert_impl(prepared_statement_t& prepared_statement);
size_t run_prepared_update_impl(prepared_statement_t& prepared_statement);
size_t run_prepared_remove_impl(prepared_statement_t& prepared_statement);
public:
using _prepared_statement_t = ::sqlpp::mysql::prepared_statement_t;
using _context_t = serializer_t;
using _serializer_context_t = _context_t;
using _interpreter_context_t = _context_t;
struct _tags
{
using _null_result_is_trivial_value = std::true_type;
};
template <typename T>
static _context_t& _serialize_interpretable(const T& t, _context_t& context)
{
return ::sqlpp::serialize(t, context);
}
template <typename T>
static _context_t& _interpret_interpretable(const T& t, _context_t& context)
{
return ::sqlpp::serialize(t, context);
}
connection(const std::shared_ptr<connection_config>& config);
~connection();
connection(const connection&) = delete;
connection& operator=(const connection&) = delete;
connection& operator=(connection&&) = default;
connection(connection&& other);
bool is_valid();
void reconnect();
const std::shared_ptr<connection_config> get_config();
bool is_transaction_active()
{
return _transaction_active;
}
template <typename Select>
char_result_t select(const Select& s)
{
_context_t context(*this);
serialize(s, context);
return select_impl(context.str());
}
template <typename Select>
_prepared_statement_t prepare_select(Select& s)
{
_context_t context(*this);
serialize(s, context);
return prepare_impl(context.str(), s._get_no_of_parameters(), s.get_no_of_result_columns());
}
template <typename PreparedSelect>
bind_result_t run_prepared_select(const PreparedSelect& s)
{
s._bind_params();
return run_prepared_select_impl(s._prepared_statement);
}
//! insert returns the last auto_incremented id (or zero, if there is none)
template <typename Insert>
size_t insert(const Insert& i)
{
_context_t context(*this);
serialize(i, context);
return insert_impl(context.str());
}
template <typename Insert>
_prepared_statement_t prepare_insert(Insert& i)
{
_context_t context(*this);
serialize(i, context);
return prepare_impl(context.str(), i._get_no_of_parameters(), 0);
}
template <typename PreparedInsert>
size_t run_prepared_insert(const PreparedInsert& i)
{
i._bind_params();
return run_prepared_insert_impl(i._prepared_statement);
}
//! update returns the number of affected rows
template <typename Update>
size_t update(const Update& u)
{
_context_t context(*this);
serialize(u, context);
return update_impl(context.str());
}
template <typename Update>
_prepared_statement_t prepare_update(Update& u)
{
_context_t context(*this);
serialize(u, context);
return prepare_impl(context.str(), u._get_no_of_parameters(), 0);
}
template <typename PreparedUpdate>
size_t run_prepared_update(const PreparedUpdate& u)
{
u._bind_params();
return run_prepared_update_impl(u._prepared_statement);
}
//! remove returns the number of removed rows
template <typename Remove>
size_t remove(const Remove& r)
{
_context_t context(*this);
serialize(r, context);
return remove_impl(context.str());
}
template <typename Remove>
_prepared_statement_t prepare_remove(Remove& r)
{
_context_t context(*this);
serialize(r, context);
return prepare_impl(context.str(), r._get_no_of_parameters(), 0);
}
template <typename PreparedRemove>
size_t run_prepared_remove(const PreparedRemove& r)
{
r._bind_params();
return run_prepared_remove_impl(r._prepared_statement);
}
//! execute arbitrary command (e.g. create a table)
void execute(const std::string& command);
//! escape given string (does not quote, though)
std::string escape(const std::string& s) const;
//! call run on the argument
template <typename T>
auto run(const T& t) -> decltype(t._run(*this))
{
return t._run(*this);
}
//! call run on the argument
template <typename T>
auto _run(const T& t, ::sqlpp::consistent_t) -> decltype(t._run(*this))
{
return t._run(*this);
}
template <typename Check, typename T>
auto _run(const T& t, Check) -> Check;
template <typename T>
auto operator()(const T& t) -> decltype(this->_run(t, sqlpp::run_check_t<_serializer_context_t, T>{}))
{
return _run(t, sqlpp::run_check_t<_serializer_context_t, T>{});
}
//! call prepare on the argument
template <typename T>
auto _prepare(const T& t, ::sqlpp::consistent_t) -> decltype(t._prepare(*this))
{
return t._prepare(*this);
}
template <typename Check, typename T>
auto _prepare(const T& t, Check) -> Check;
template <typename T>
auto prepare(const T& t) -> decltype(this->_prepare(t, sqlpp::prepare_check_t<_serializer_context_t, T>{}))
{
return _prepare(t, sqlpp::prepare_check_t<_serializer_context_t, T>{});
}
//! start transaction
void start_transaction();
//! commit transaction (or throw if the transaction has been finished already)
void commit_transaction();
//! rollback transaction with or without reporting the rollback (or throw if the transaction has been finished
// already)
void rollback_transaction(bool report);
//! report a rollback failure (will be called by transactions in case of a rollback failure in the destructor)
void report_rollback_failure(const std::string message) noexcept;
MYSQL* get_handle();
};
inline std::string serializer_t::escape(std::string arg)
{
return _db.escape(arg);
}
}
}
#include <sqlpp11/mysql/serializer.h>
#endif

View File

@ -27,8 +27,14 @@
#ifndef SQLPP_MYSQL_PREPARED_STATEMENT_H #ifndef SQLPP_MYSQL_PREPARED_STATEMENT_H
#define SQLPP_MYSQL_PREPARED_STATEMENT_H #define SQLPP_MYSQL_PREPARED_STATEMENT_H
#include <date/date.h>
#include <sqlpp11/mysql/sqlpp_mysql.h>
#include <sqlpp11/mysql/bind_result.h>
#include <memory> #include <memory>
#include <iostream>
#include <string> #include <string>
#include <vector>
#include <sqlpp11/chrono.h> #include <sqlpp11/chrono.h>
namespace sqlpp namespace sqlpp
@ -37,11 +43,6 @@ namespace sqlpp
{ {
class connection; class connection;
namespace detail
{
struct prepared_statement_handle_t;
}
class prepared_statement_t class prepared_statement_t
{ {
friend ::sqlpp::mysql::connection; friend ::sqlpp::mysql::connection;
@ -49,7 +50,12 @@ namespace sqlpp
public: public:
prepared_statement_t() = delete; prepared_statement_t() = delete;
prepared_statement_t(std::shared_ptr<detail::prepared_statement_handle_t>&& handle); prepared_statement_t(std::shared_ptr<detail::prepared_statement_handle_t>&& handle) : _handle(std::move(handle))
{
if (_handle and _handle->debug)
std::cerr << "MySQL debug: Constructing prepared_statement, using handle at " << _handle.get() << std::endl;
}
prepared_statement_t(const prepared_statement_t&) = delete; prepared_statement_t(const prepared_statement_t&) = delete;
prepared_statement_t(prepared_statement_t&& rhs) = default; prepared_statement_t(prepared_statement_t&& rhs) = default;
prepared_statement_t& operator=(const prepared_statement_t&) = delete; prepared_statement_t& operator=(const prepared_statement_t&) = delete;
@ -63,14 +69,150 @@ namespace sqlpp
void _pre_bind(); void _pre_bind();
void _bind_boolean_parameter(size_t index, const signed char* value, bool is_null); void _bind_boolean_parameter(size_t index, const signed char* value, bool is_null)
void _bind_integral_parameter(size_t index, const int64_t* value, bool is_null); {
void _bind_unsigned_integral_parameter(size_t index, const uint64_t* value, bool is_null); if (_handle->debug)
void _bind_floating_point_parameter(size_t index, const double* value, bool is_null); std::cerr << "MySQL debug: binding boolean parameter " << (*value ? "true" : "false")
void _bind_text_parameter(size_t index, const std::string* value, bool is_null); << " at index: " << index << ", being " << (is_null ? "" : "not ") << "null" << std::endl;
void _bind_date_parameter(size_t index, const ::sqlpp::chrono::day_point* value, bool is_null); _handle->stmt_param_is_null[index] = is_null;
void _bind_date_time_parameter(size_t index, const ::sqlpp::chrono::microsecond_point* value, bool is_null); MYSQL_BIND& param = _handle->stmt_params[index];
}; param.buffer_type = MYSQL_TYPE_TINY;
param.buffer = const_cast<signed char*>(value);
param.buffer_length = sizeof(*value);
param.length = &param.buffer_length;
param.is_null = &_handle->stmt_param_is_null[index].value;
param.is_unsigned = false;
param.error = nullptr;
} }
}
void _bind_integral_parameter(size_t index, const int64_t* value, bool is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding integral parameter " << *value << " at index: " << index << ", being "
<< (is_null ? "" : "not ") << "null" << std::endl;
_handle->stmt_param_is_null[index] = is_null;
MYSQL_BIND& param = _handle->stmt_params[index];
param.buffer_type = MYSQL_TYPE_LONGLONG;
param.buffer = const_cast<int64_t*>(value);
param.buffer_length = sizeof(*value);
param.length = &param.buffer_length;
param.is_null = &_handle->stmt_param_is_null[index].value;
param.is_unsigned = false;
param.error = nullptr;
}
void _bind_unsigned_integral_parameter(size_t index, const uint64_t* value, bool is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding unsigned integral parameter " << *value << " at index: " << index
<< ", being " << (is_null ? "" : "not ") << "null" << std::endl;
_handle->stmt_param_is_null[index] = is_null;
MYSQL_BIND& param = _handle->stmt_params[index];
param.buffer_type = MYSQL_TYPE_LONGLONG;
param.buffer = const_cast<uint64_t*>(value);
param.buffer_length = sizeof(*value);
param.length = &param.buffer_length;
param.is_null = &_handle->stmt_param_is_null[index].value;
param.is_unsigned = true;
param.error = nullptr;
}
void _bind_floating_point_parameter(size_t index, const double* value, bool is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding floating_point parameter " << *value << " at index: " << index
<< ", being " << (is_null ? "" : "not ") << "null" << std::endl;
_handle->stmt_param_is_null[index] = is_null;
MYSQL_BIND& param = _handle->stmt_params[index];
param.buffer_type = MYSQL_TYPE_DOUBLE;
param.buffer = const_cast<double*>(value);
param.buffer_length = sizeof(*value);
param.length = &param.buffer_length;
param.is_null = &_handle->stmt_param_is_null[index].value;
param.is_unsigned = false;
param.error = nullptr;
}
void _bind_text_parameter(size_t index, const std::string* value, bool is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding text parameter " << *value << " at index: " << index << ", being "
<< (is_null ? "" : "not ") << "null" << std::endl;
_handle->stmt_param_is_null[index] = is_null;
MYSQL_BIND& param = _handle->stmt_params[index];
param.buffer_type = MYSQL_TYPE_STRING;
param.buffer = const_cast<char*>(value->data());
param.buffer_length = value->size();
param.length = &param.buffer_length;
param.is_null = &_handle->stmt_param_is_null[index].value;
param.is_unsigned = false;
param.error = nullptr;
}
void _bind_date_parameter(size_t index, const ::sqlpp::chrono::day_point* value, bool is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding date parameter "
<< " at index: " << index << ", being " << (is_null ? "" : "not ") << "null" << std::endl;
auto& bound_time = _handle->stmt_date_time_param_buffer[index];
if (not is_null)
{
const auto ymd = ::date::year_month_day{*value};
bound_time.year = static_cast<int>(ymd.year());
bound_time.month = static_cast<unsigned>(ymd.month());
bound_time.day = static_cast<unsigned>(ymd.day());
if (_handle->debug)
std::cerr << "bound values: " << bound_time.year << '-' << bound_time.month << '-' << bound_time.day << 'T'
<< bound_time.hour << ':' << bound_time.minute << ':' << bound_time.second << std::endl;
}
_handle->stmt_param_is_null[index] = is_null;
MYSQL_BIND& param = _handle->stmt_params[index];
param.buffer_type = MYSQL_TYPE_DATE;
param.buffer = &bound_time;
param.buffer_length = sizeof(MYSQL_TIME);
param.length = &param.buffer_length;
param.is_null = &_handle->stmt_param_is_null[index].value;
param.is_unsigned = false;
param.error = nullptr;
}
void _bind_date_time_parameter(size_t index, const ::sqlpp::chrono::microsecond_point* value, bool is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding date_time parameter "
<< " at index: " << index << ", being " << (is_null ? "" : "not ") << "null" << std::endl;
auto& bound_time = _handle->stmt_date_time_param_buffer[index];
if (not is_null)
{
const auto dp = ::sqlpp::chrono::floor<::date::days>(*value);
const auto time = ::date::make_time(*value - dp);
const auto ymd = ::date::year_month_day{dp};
bound_time.year = static_cast<int>(ymd.year());
bound_time.month = static_cast<unsigned>(ymd.month());
bound_time.day = static_cast<unsigned>(ymd.day());
bound_time.hour = time.hours().count();
bound_time.minute = time.minutes().count();
bound_time.second = time.seconds().count();
bound_time.second_part = time.subseconds().count();
if (_handle->debug)
std::cerr << "bound values: " << bound_time.year << '-' << bound_time.month << '-' << bound_time.day << 'T'
<< bound_time.hour << ':' << bound_time.minute << ':' << bound_time.second << std::endl;
}
_handle->stmt_param_is_null[index] = is_null;
MYSQL_BIND& param = _handle->stmt_params[index];
param.buffer_type = MYSQL_TYPE_DATETIME;
param.buffer = &bound_time;
param.buffer_length = sizeof(MYSQL_TIME);
param.length = &param.buffer_length;
param.is_null = &_handle->stmt_param_is_null[index].value;
param.is_unsigned = false;
param.error = nullptr;
}
};
} // namespace mysql
} // namespace sqlpp
#endif #endif

View File

@ -33,9 +33,11 @@
namespace sqlpp namespace sqlpp
{ {
namespace mysql{ namespace mysql
{
template <typename Database> template <typename Database>
using blank_remove_t = statement_t<Database, remove_t, no_from_t, no_using_t, no_where_t<true>, no_order_by_t, no_limit_t>; using blank_remove_t =
statement_t<Database, remove_t, no_from_t, no_using_t, no_where_t<true>, no_order_by_t, no_limit_t>;
inline auto remove() -> blank_remove_t<void> inline auto remove() -> blank_remove_t<void>
{ {
@ -49,19 +51,20 @@ namespace sqlpp
} }
template <typename Database> template <typename Database>
auto dynamic_remove(const Database & /*unused*/) -> decltype(blank_remove_t<Database>()) auto dynamic_remove(const Database& /*unused*/) -> decltype(blank_remove_t<Database>())
{ {
static_assert(std::is_base_of<connection, Database>::value, "Invalid database parameter"); static_assert(std::is_base_of<connection, Database>::value, "Invalid database parameter");
return {blank_remove_t<Database>()}; return {blank_remove_t<Database>()};
} }
template <typename Database, typename Table> template <typename Database, typename Table>
auto dynamic_remove_from(const Database& /*unused*/, Table table) -> decltype(blank_remove_t<Database>().from(table)) auto dynamic_remove_from(const Database& /*unused*/, Table table)
-> decltype(blank_remove_t<Database>().from(table))
{ {
static_assert(std::is_base_of<connection, Database>::value, "Invalid database parameter"); static_assert(std::is_base_of<connection, Database>::value, "Invalid database parameter");
return {blank_remove_t<Database>().from(table)}; return {blank_remove_t<Database>().from(table)};
} }
} //namespace mysql } // namespace mysql
} // namespace sqlpp } // namespace sqlpp

View File

@ -0,0 +1,42 @@
/*
* Copyright (c) 2018 - 2018, Roland Bock
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SQLPP_MYSQL_MYSQL_H
#define SQLPP_MYSQL_MYSQL_H
#include <mysql.h>
namespace sqlpp
{
namespace mysql
{
#if LIBMYSQL_VERSION_ID >= 80000
using my_bool = bool;
#endif
}
}
#endif

View File

@ -33,9 +33,16 @@
namespace sqlpp namespace sqlpp
{ {
namespace mysql{ namespace mysql
{
template <typename Database> template <typename Database>
using blank_update_t = statement_t<Database, update_t, no_single_table_t, no_update_list_t, no_where_t<true>, no_order_by_t, no_limit_t>; using blank_update_t = statement_t<Database,
update_t,
no_single_table_t,
no_update_list_t,
no_where_t<true>,
no_order_by_t,
no_limit_t>;
template <typename Table> template <typename Table>
constexpr auto update(Table table) -> decltype(blank_update_t<void>().single_table(table)) constexpr auto update(Table table) -> decltype(blank_update_t<void>().single_table(table))

View File

@ -21,10 +21,6 @@
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if(MYSQL_CONNECTOR OR MARIADB_CONNECTOR)
add_subdirectory(mysql)
endif()
if(SQLITE3_CONNECTOR OR SQLCIPHER_CONNECTOR) if(SQLITE3_CONNECTOR OR SQLCIPHER_CONNECTOR)
add_subdirectory(sqlite3) add_subdirectory(sqlite3)
endif() endif()

View File

@ -36,311 +36,6 @@ namespace sqlpp
{ {
namespace mysql namespace mysql
{ {
bind_result_t::bind_result_t(const std::shared_ptr<detail::prepared_statement_handle_t>& handle) : _handle(handle)
{
if (_handle and _handle->debug)
std::cerr << "MySQL debug: Constructing bind result, using handle at " << _handle.get() << std::endl;
}
bind_result_t::~bind_result_t()
{
if (_handle)
mysql_stmt_free_result(_handle->mysql_stmt);
}
bool bind_result_t::_invalid() const
{
return !_handle or !*_handle;
}
void bind_result_t::_bind_boolean_result(size_t index, signed char* value, bool* is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding boolean result " << static_cast<void*>(value) << " at index: " << index
<< std::endl;
detail::result_meta_data_t& meta_data = _handle->result_param_meta_data[index];
meta_data.index = index;
meta_data.len = nullptr;
meta_data.is_null = is_null;
MYSQL_BIND& param = _handle->result_params[index];
param.buffer_type = MYSQL_TYPE_TINY;
param.buffer = value;
param.buffer_length = sizeof(*value);
param.length = &meta_data.bound_len;
param.is_null = &meta_data.bound_is_null;
param.is_unsigned = false;
param.error = &meta_data.bound_error;
}
void bind_result_t::_bind_integral_result(size_t index, int64_t* value, bool* is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding integral result " << static_cast<void*>(value) << " at index: " << index
<< std::endl;
detail::result_meta_data_t& meta_data = _handle->result_param_meta_data[index];
meta_data.index = index;
meta_data.len = nullptr;
meta_data.is_null = is_null;
MYSQL_BIND& param = _handle->result_params[index];
param.buffer_type = MYSQL_TYPE_LONGLONG;
param.buffer = value;
param.buffer_length = sizeof(*value);
param.length = &meta_data.bound_len;
param.is_null = &meta_data.bound_is_null;
param.is_unsigned = false;
param.error = &meta_data.bound_error;
}
void bind_result_t::_bind_unsigned_integral_result(size_t index, uint64_t* value, bool* is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding unsigned integral result " << static_cast<void*>(value)
<< " at index: " << index << std::endl;
detail::result_meta_data_t& meta_data = _handle->result_param_meta_data[index];
meta_data.index = index;
meta_data.len = nullptr;
meta_data.is_null = is_null;
MYSQL_BIND& param = _handle->result_params[index];
param.buffer_type = MYSQL_TYPE_LONGLONG;
param.buffer = value;
param.buffer_length = sizeof(*value);
param.length = &meta_data.bound_len;
param.is_null = &meta_data.bound_is_null;
param.is_unsigned = true;
param.error = &meta_data.bound_error;
}
void bind_result_t::_bind_floating_point_result(size_t index, double* value, bool* is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding floating point result " << static_cast<void*>(value)
<< " at index: " << index << std::endl;
detail::result_meta_data_t& meta_data = _handle->result_param_meta_data[index];
meta_data.index = index;
meta_data.len = nullptr;
meta_data.is_null = is_null;
MYSQL_BIND& param = _handle->result_params[index];
param.buffer_type = MYSQL_TYPE_DOUBLE;
param.buffer = value;
param.buffer_length = sizeof(*value);
param.length = &meta_data.bound_len;
param.is_null = &meta_data.bound_is_null;
param.is_unsigned = false;
param.error = &meta_data.bound_error;
}
void bind_result_t::_bind_text_result(size_t index, const char** value, size_t* len)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding text result " << static_cast<const void*>(*value) << " at index: " << index
<< std::endl;
detail::result_meta_data_t& meta_data = _handle->result_param_meta_data[index];
meta_data.index = index;
meta_data.len = len;
meta_data.is_null = nullptr;
meta_data.text_buffer = value;
if (meta_data.bound_text_buffer.empty())
meta_data.bound_text_buffer.resize(8);
MYSQL_BIND& param = _handle->result_params[index];
param.buffer_type = MYSQL_TYPE_STRING;
param.buffer = meta_data.bound_text_buffer.data();
param.buffer_length = meta_data.bound_text_buffer.size();
param.length = &meta_data.bound_len;
param.is_null = &meta_data.bound_is_null;
param.is_unsigned = false;
param.error = &meta_data.bound_error;
}
void bind_result_t::_bind_blob_result(size_t index, const char** value, size_t* len)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding text result " << static_cast<const void*>(*value) << " at index: " << index
<< std::endl;
detail::result_meta_data_t& meta_data = _handle->result_param_meta_data[index];
meta_data.index = index;
meta_data.len = len;
meta_data.is_null = nullptr;
meta_data.text_buffer = value;
if (meta_data.bound_text_buffer.empty())
meta_data.bound_text_buffer.resize(8);
MYSQL_BIND& param = _handle->result_params[index];
param.buffer_type = MYSQL_TYPE_BLOB;
param.buffer = meta_data.bound_text_buffer.data();
param.buffer_length = meta_data.bound_text_buffer.size();
param.length = &meta_data.bound_len;
param.is_null = &meta_data.bound_is_null;
param.is_unsigned = false;
param.error = &meta_data.bound_error;
}
void bind_result_t::_bind_date_result(size_t index, ::sqlpp::chrono::day_point* value, bool* is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding date result " << static_cast<void*>(value) << " at index: " << index
<< std::endl;
detail::result_meta_data_t& meta_data = _handle->result_param_meta_data[index];
meta_data.index = index;
meta_data.len = nullptr;
meta_data.is_null = is_null;
meta_data.text_buffer = nullptr;
meta_data.bound_text_buffer.resize(sizeof(MYSQL_TIME));
MYSQL_BIND& param = _handle->result_params[index];
param.buffer_type = MYSQL_TYPE_DATE;
param.buffer = meta_data.bound_text_buffer.data();
param.buffer_length = meta_data.bound_text_buffer.size();
param.length = &meta_data.bound_len;
param.is_null = &meta_data.bound_is_null;
param.is_unsigned = false;
param.error = &meta_data.bound_error;
}
void bind_result_t::_bind_date_time_result(size_t index, ::sqlpp::chrono::microsecond_point* value, bool* is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding date time result " << static_cast<void*>(value) << " at index: " << index
<< std::endl;
detail::result_meta_data_t& meta_data = _handle->result_param_meta_data[index];
meta_data.index = index;
meta_data.len = nullptr;
meta_data.is_null = is_null;
meta_data.text_buffer = nullptr;
meta_data.bound_text_buffer.resize(sizeof(MYSQL_TIME));
MYSQL_BIND& param = _handle->result_params[index];
param.buffer_type = MYSQL_TYPE_DATETIME;
param.buffer = meta_data.bound_text_buffer.data();
param.buffer_length = meta_data.bound_text_buffer.size();
param.length = &meta_data.bound_len;
param.is_null = &meta_data.bound_is_null;
param.is_unsigned = false;
param.error = &meta_data.bound_error;
}
void bind_result_t::_post_bind_date_result(size_t index, ::sqlpp::chrono::day_point* value, bool* is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: post binding date result " << static_cast<void*>(value) << " at index: " << index
<< std::endl;
if (not *is_null)
{
const auto& dt =
*reinterpret_cast<const MYSQL_TIME*>(_handle->result_param_meta_data[index].bound_text_buffer.data());
*is_null = false;
*value = ::date::year(dt.year) / ::date::month(dt.month) / ::date::day(dt.day);
}
}
void bind_result_t::_post_bind_date_time_result(size_t index,
::sqlpp::chrono::microsecond_point* value,
bool* is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding date time result " << static_cast<void*>(value) << " at index: " << index
<< std::endl;
if (not *is_null)
{
const auto& dt =
*reinterpret_cast<const MYSQL_TIME*>(_handle->result_param_meta_data[index].bound_text_buffer.data());
*is_null = false;
*value = ::sqlpp::chrono::day_point(::date::year(dt.year) / ::date::month(dt.month) / ::date::day(dt.day)) +
std::chrono::hours(dt.hour) + std::chrono::minutes(dt.minute) + std::chrono::seconds(dt.second) +
std::chrono::microseconds(dt.second_part);
}
}
void bind_result_t::bind_impl()
{
if (_handle->debug)
std::cerr << "MySQL debug: Binding results for handle at " << _handle.get() << std::endl;
if (mysql_stmt_bind_result(_handle->mysql_stmt, _handle->result_params.data()))
{
throw sqlpp::exception(std::string("MySQL: mysql_stmt_bind_result: ") + mysql_stmt_error(_handle->mysql_stmt));
}
}
bool bind_result_t::next_impl()
{
if (_handle->debug)
std::cerr << "MySQL debug: Accessing next row of handle at " << _handle.get() << std::endl;
auto flag = mysql_stmt_fetch(_handle->mysql_stmt);
switch (flag)
{
case 0:
case MYSQL_DATA_TRUNCATED:
{
bool need_to_rebind = false;
for (auto& r : _handle->result_param_meta_data)
{
if (r.len)
{
if (r.bound_is_null)
{
*r.text_buffer = nullptr;
*r.len = 0;
}
else
{
if (r.bound_len > r.bound_text_buffer.size())
{
if (_handle->debug)
std::cerr << "MySQL debug: Need to reallocate buffer " << static_cast<const void*>(*r.text_buffer)
<< " at index " << r.index << " for handle at " << _handle.get() << std::endl;
need_to_rebind = true;
r.bound_text_buffer.resize(r.bound_len);
MYSQL_BIND& param = _handle->result_params[r.index];
param.buffer = r.bound_text_buffer.data();
param.buffer_length = r.bound_text_buffer.size();
auto err =
mysql_stmt_fetch_column(_handle->mysql_stmt, &param, r.index, 0);
if (err)
throw sqlpp::exception(std::string("MySQL: Fetch column after reallocate failed: ") +
"error-code: " + std::to_string(err) + ", stmt-error: " +
mysql_stmt_error(_handle->mysql_stmt) + ", stmt-errno: " +
std::to_string(mysql_stmt_errno(_handle->mysql_stmt)));
}
*r.text_buffer = r.bound_text_buffer.data();
if (_handle->debug)
std::cerr << "MySQL debug: New buffer " << static_cast<const void*>(*r.text_buffer) << " at index "
<< r.index << " for handle at " << _handle.get() << std::endl;
*r.len = r.bound_len;
}
}
if (r.is_null)
*r.is_null = r.bound_is_null;
}
if (need_to_rebind)
bind_impl();
}
return true;
case 1:
throw sqlpp::exception(std::string("MySQL: Could not fetch next result: ") +
mysql_stmt_error(_handle->mysql_stmt));
case MYSQL_NO_DATA:
return false;
default:
throw sqlpp::exception("MySQL: Unexpected return value for mysql_stmt_fetch()");
}
}
} }
} }

View File

@ -36,149 +36,10 @@ namespace sqlpp
{ {
namespace mysql namespace mysql
{ {
char_result_t::char_result_t()
{
}
char_result_t::char_result_t(std::unique_ptr<detail::result_handle>&& handle) : _handle(std::move(handle))
{
if (_invalid())
throw sqlpp::exception("MySQL: Constructing char_result without valid handle");
if (_handle->debug)
std::cerr << "MySQL debug: Constructing result, using handle at " << _handle.get() << std::endl;
}
char_result_t::~char_result_t() = default;
char_result_t::char_result_t(char_result_t&& rhs) = default;
char_result_t& char_result_t::operator=(char_result_t&&) = default;
namespace namespace
{ {
const auto date_digits = std::vector<char>{1, 1, 1, 1, 0, 1, 1, 0, 1, 1}; // 2015-10-28
const auto time_digits = std::vector<char>{0, 1, 1, 0, 1, 1, 0, 1, 1}; // T23:00:12
auto check_digits(const char* text, const std::vector<char>& digitFlags) -> bool
{
for (const auto digitFlag : digitFlags)
{
if (digitFlag)
{
if (not std::isdigit(*text))
{
return false;
}
}
else
{
if (std::isdigit(*text) or *text == '\0')
{
return false;
}
}
++text;
}
return true;
}
} }
bool char_result_t::_invalid() const
{
return !_handle or !*_handle;
}
void char_result_t::_bind_date_result(size_t index, ::sqlpp::chrono::day_point* value, bool* is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: parsing date result at index: " << index << std::endl;
*is_null = (_char_result_row.data == nullptr or _char_result_row.data[index] == nullptr);
if (*is_null)
{
*value = {};
return;
}
const auto date_string = _char_result_row.data[index];
if (_handle->debug)
std::cerr << "MySQL debug: date string: " << date_string << std::endl;
if (check_digits(date_string, date_digits))
{
const auto ymd = ::date::year(std::atoi(date_string)) / atoi(date_string + 5) / atoi(date_string + 8);
*value = ::sqlpp::chrono::day_point(ymd);
}
else
{
if (_handle->debug)
std::cerr << "MySQL debug: invalid date result: " << date_string << std::endl;
*value = {};
}
}
void char_result_t::_bind_date_time_result(size_t index, ::sqlpp::chrono::microsecond_point* value, bool* is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: parsing date result at index: " << index << std::endl;
*is_null = (_char_result_row.data == nullptr or _char_result_row.data[index] == nullptr);
if (*is_null)
{
*value = {};
return;
}
const auto date_time_string = _char_result_row.data[index];
if (_handle->debug)
std::cerr << "MySQL debug: date_time string: " << date_time_string << std::endl;
if (check_digits(date_time_string, date_digits))
{
const auto ymd =
::date::year(std::atoi(date_time_string)) / atoi(date_time_string + 5) / atoi(date_time_string + 8);
*value = ::sqlpp::chrono::day_point(ymd);
}
else
{
if (_handle->debug)
std::cerr << "MySQL debug: invalid date_time result: " << date_time_string << std::endl;
*value = {};
return;
}
const auto time_string = date_time_string + 10;
if (check_digits(time_string, time_digits))
{
*value += ::std::chrono::hours(std::atoi(time_string + 1)) + std::chrono::minutes(std::atoi(time_string + 4)) +
std::chrono::seconds(std::atoi(time_string + 7));
}
else
{
return;
}
const auto mu_string = time_string + 9;
if (mu_string[0] == '\0')
{
return;
}
auto factor = 100 * 1000;
for (auto i = 1u; i <= 6u and std::isdigit(mu_string[i]); ++i, factor /= 10)
{
*value += ::std::chrono::microseconds(factor * (mu_string[i] - '0'));
}
}
bool char_result_t::next_impl()
{
if (_handle->debug)
std::cerr << "MySQL debug: Accessing next row of handle at " << _handle.get() << std::endl;
_char_result_row.data = const_cast<const char**>(mysql_fetch_row(_handle->mysql_res));
_char_result_row.len = mysql_fetch_lengths(_handle->mysql_res);
return _char_result_row.data;
}
} }
} }

View File

@ -36,253 +36,6 @@ namespace sqlpp
{ {
namespace mysql namespace mysql
{ {
scoped_library_initializer_t::scoped_library_initializer_t(int argc, char** argv, char** groups)
{
mysql_library_init(argc, argv, groups);
}
scoped_library_initializer_t::~scoped_library_initializer_t()
{
mysql_library_end();
}
void global_library_init(int argc, char** argv, char** groups)
{
static const auto global_init_and_end = scoped_library_initializer_t(argc, argv, groups);
}
namespace
{
struct MySqlThreadInitializer
{
MySqlThreadInitializer()
{
if (!mysql_thread_safe())
{
throw sqlpp::exception("MySQL error: Operating on a non-threadsafe client");
}
mysql_thread_init();
}
~MySqlThreadInitializer()
{
mysql_thread_end();
}
};
void thread_init()
{
thread_local MySqlThreadInitializer threadInitializer;
}
void execute_statement(detail::connection_handle_t& handle, const std::string& statement)
{
thread_init();
if (handle.config->debug)
std::cerr << "MySQL debug: Executing: '" << statement << "'" << std::endl;
if (mysql_query(handle.mysql.get(), statement.c_str()))
{
throw sqlpp::exception("MySQL error: Could not execute MySQL-statement: " +
std::string(mysql_error(handle.mysql.get())) + " (statement was >>" + statement +
"<<\n");
}
}
void execute_prepared_statement(detail::prepared_statement_handle_t& prepared_statement)
{
thread_init();
if (prepared_statement.debug)
std::cerr << "MySQL debug: Executing prepared_statement" << std::endl;
if (mysql_stmt_bind_param(prepared_statement.mysql_stmt, prepared_statement.stmt_params.data()))
{
throw sqlpp::exception(std::string("MySQL error: Could not bind parameters to statement") +
mysql_stmt_error(prepared_statement.mysql_stmt));
}
if (mysql_stmt_execute(prepared_statement.mysql_stmt))
{
throw sqlpp::exception(std::string("MySQL error: Could not execute prepared statement: ") +
mysql_stmt_error(prepared_statement.mysql_stmt));
}
}
std::shared_ptr<detail::prepared_statement_handle_t> prepare_statement(detail::connection_handle_t& handle,
const std::string& statement,
size_t no_of_parameters,
size_t no_of_columns)
{
thread_init();
if (handle.config->debug)
std::cerr << "MySQL debug: Preparing: '" << statement << "'" << std::endl;
auto prepared_statement = std::make_shared<detail::prepared_statement_handle_t>(
mysql_stmt_init(handle.mysql.get()), no_of_parameters, no_of_columns, handle.config->debug);
if (not prepared_statement)
{
throw sqlpp::exception("MySQL error: Could not allocate prepared statement\n");
}
if (mysql_stmt_prepare(prepared_statement->mysql_stmt, statement.data(), statement.size()))
{
throw sqlpp::exception("MySQL error: Could not prepare statement: " +
std::string(mysql_error(handle.mysql.get())) + " (statement was >>" + statement +
"<<\n");
}
return prepared_statement;
}
}
connection::connection(const std::shared_ptr<connection_config>& config)
: _handle(new detail::connection_handle_t(config))
{
}
connection::~connection()
{
}
connection::connection(connection&& other)
{
this->_transaction_active = other._transaction_active;
this->_handle = std::move(other._handle);
}
bool connection::is_valid()
{
return _handle->is_valid();
}
void connection::reconnect()
{
return _handle->reconnect();
}
const std::shared_ptr<connection_config> connection::get_config()
{
return _handle->config;
}
char_result_t connection::select_impl(const std::string& statement)
{
execute_statement(*_handle, statement);
std::unique_ptr<detail::result_handle> result_handle(
new detail::result_handle(mysql_store_result(_handle->mysql.get()), _handle->config->debug));
if (!*result_handle)
{
throw sqlpp::exception("MySQL error: Could not store result set: " +
std::string(mysql_error(_handle->mysql.get())));
}
return {std::move(result_handle)};
}
bind_result_t connection::run_prepared_select_impl(prepared_statement_t& prepared_statement)
{
execute_prepared_statement(*prepared_statement._handle);
return prepared_statement._handle;
}
size_t connection::run_prepared_insert_impl(prepared_statement_t& prepared_statement)
{
execute_prepared_statement(*prepared_statement._handle);
return mysql_stmt_insert_id(prepared_statement._handle->mysql_stmt);
}
size_t connection::run_prepared_update_impl(prepared_statement_t& prepared_statement)
{
execute_prepared_statement(*prepared_statement._handle);
return mysql_stmt_affected_rows(prepared_statement._handle->mysql_stmt);
}
size_t connection::run_prepared_remove_impl(prepared_statement_t& prepared_statement)
{
execute_prepared_statement(*prepared_statement._handle);
return mysql_stmt_affected_rows(prepared_statement._handle->mysql_stmt);
}
prepared_statement_t connection::prepare_impl(const std::string& statement,
size_t no_of_parameters,
size_t no_of_columns)
{
return prepare_statement(*_handle, statement, no_of_parameters, no_of_columns);
}
size_t connection::insert_impl(const std::string& statement)
{
execute_statement(*_handle, statement);
return mysql_insert_id(_handle->mysql.get());
}
void connection::execute(const std::string& command)
{
execute_statement(*_handle, command);
}
size_t connection::update_impl(const std::string& statement)
{
execute_statement(*_handle, statement);
return mysql_affected_rows(_handle->mysql.get());
}
size_t connection::remove_impl(const std::string& statement)
{
execute_statement(*_handle, statement);
return mysql_affected_rows(_handle->mysql.get());
}
std::string connection::escape(const std::string& s) const
{
std::unique_ptr<char[]> dest(new char[s.size() * 2 + 1]);
mysql_real_escape_string(_handle->mysql.get(), dest.get(), s.c_str(), s.size());
return dest.get();
}
void connection::start_transaction()
{
if (_transaction_active)
{
throw sqlpp::exception("MySQL: Cannot have more than one open transaction per connection");
}
execute_statement(*_handle, "START TRANSACTION");
_transaction_active = true;
}
void connection::commit_transaction()
{
if (not _transaction_active)
{
throw sqlpp::exception("MySQL: Cannot commit a finished or failed transaction");
}
_transaction_active = false;
execute_statement(*_handle, "COMMIT");
}
void connection::rollback_transaction(bool report)
{
if (not _transaction_active)
{
throw sqlpp::exception("MySQL: Cannot rollback a finished or failed transaction");
}
if (report)
{
std::cerr << "MySQL warning: Rolling back unfinished transaction" << std::endl;
}
_transaction_active = false;
execute_statement(*_handle, "ROLLBACK");
}
void connection::report_rollback_failure(const std::string message) noexcept
{
std::cerr << "MySQL message:" << message << std::endl;
}
MYSQL* connection::get_handle(){
return _handle->mysql.get();
}
} }
} }

View File

@ -35,65 +35,6 @@ namespace sqlpp
{ {
namespace detail namespace detail
{ {
void connect(MYSQL* mysql, const connection_config& config)
{
if (!mysql_real_connect(mysql, config.host.empty() ? nullptr : config.host.c_str(),
config.user.empty() ? nullptr : config.user.c_str(),
config.password.empty() ? nullptr : config.password.c_str(), nullptr, config.port,
config.unix_socket.empty() ? nullptr : config.unix_socket.c_str(), config.client_flag))
{
throw sqlpp::exception("MySQL: could not connect to server: " + std::string(mysql_error(mysql)));
}
if (mysql_set_character_set(mysql, config.charset.c_str()))
{
throw sqlpp::exception("MySQL error: can't set character set " + config.charset);
}
if (not config.database.empty() and mysql_select_db(mysql, config.database.c_str()))
{
throw sqlpp::exception("MySQL error: can't select database '" + config.database + "'");
}
}
void handle_cleanup(MYSQL* mysql)
{
mysql_close(mysql);
}
connection_handle_t::connection_handle_t(const std::shared_ptr<connection_config>& conf)
: config(conf), mysql(mysql_init(nullptr), handle_cleanup)
{
if (not mysql)
{
throw sqlpp::exception("MySQL: could not init mysql data structure");
}
if (config->auto_reconnect)
{
my_bool my_true = true;
if (mysql_options(mysql.get(), MYSQL_OPT_RECONNECT, &my_true))
{
throw sqlpp::exception("MySQL: could not set option MYSQL_OPT_RECONNECT");
}
}
connect(mysql.get(), *config);
}
connection_handle_t::~connection_handle_t()
{
}
bool connection_handle_t::is_valid()
{
return mysql_ping(mysql.get()) == 0;
}
void connection_handle_t::reconnect()
{
connect(mysql.get(), *config);
}
} }
} }
} }

View File

@ -38,25 +38,8 @@ namespace sqlpp
namespace detail namespace detail
{ {
void handle_cleanup(MYSQL* mysql); } // namespace detail
} // namespace mysql
struct connection_handle_t
{
const std::shared_ptr<connection_config> config;
std::unique_ptr<MYSQL, void (*)(MYSQL*)> mysql;
connection_handle_t(const std::shared_ptr<connection_config>& config);
~connection_handle_t();
connection_handle_t(const connection_handle_t&) = delete;
connection_handle_t(connection_handle_t&&) = delete;
connection_handle_t& operator=(const connection_handle_t&) = delete;
connection_handle_t& operator=(connection_handle_t&&) = delete;
bool is_valid();
void reconnect();
};
}
}
} }
#endif #endif

View File

@ -48,157 +48,5 @@ namespace sqlpp
{ {
namespace mysql namespace mysql
{ {
prepared_statement_t::prepared_statement_t(std::shared_ptr<detail::prepared_statement_handle_t>&& handle)
: _handle(std::move(handle))
{
if (_handle and _handle->debug)
std::cerr << "MySQL debug: Constructing prepared_statement, using handle at " << _handle.get() << std::endl;
}
void prepared_statement_t::_bind_boolean_parameter(size_t index, const signed char* value, bool is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding boolean parameter " << (*value ? "true" : "false") << " at index: " << index
<< ", being " << (is_null ? "" : "not ") << "null" << std::endl;
_handle->stmt_param_is_null[index] = is_null;
MYSQL_BIND& param = _handle->stmt_params[index];
param.buffer_type = MYSQL_TYPE_TINY;
param.buffer = const_cast<signed char*>(value);
param.buffer_length = sizeof(*value);
param.length = &param.buffer_length;
param.is_null = &_handle->stmt_param_is_null[index].value;
param.is_unsigned = false;
param.error = nullptr;
}
void prepared_statement_t::_bind_integral_parameter(size_t index, const int64_t* value, bool is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding integral parameter " << *value << " at index: " << index << ", being "
<< (is_null ? "" : "not ") << "null" << std::endl;
_handle->stmt_param_is_null[index] = is_null;
MYSQL_BIND& param = _handle->stmt_params[index];
param.buffer_type = MYSQL_TYPE_LONGLONG;
param.buffer = const_cast<int64_t*>(value);
param.buffer_length = sizeof(*value);
param.length = &param.buffer_length;
param.is_null = &_handle->stmt_param_is_null[index].value;
param.is_unsigned = false;
param.error = nullptr;
}
void prepared_statement_t::_bind_unsigned_integral_parameter(size_t index, const uint64_t* value, bool is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding unsigned integral parameter " << *value << " at index: " << index
<< ", being " << (is_null ? "" : "not ") << "null" << std::endl;
_handle->stmt_param_is_null[index] = is_null;
MYSQL_BIND& param = _handle->stmt_params[index];
param.buffer_type = MYSQL_TYPE_LONGLONG;
param.buffer = const_cast<uint64_t*>(value);
param.buffer_length = sizeof(*value);
param.length = &param.buffer_length;
param.is_null = &_handle->stmt_param_is_null[index].value;
param.is_unsigned = true;
param.error = nullptr;
}
void prepared_statement_t::_bind_floating_point_parameter(size_t index, const double* value, bool is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding floating_point parameter " << *value << " at index: " << index << ", being "
<< (is_null ? "" : "not ") << "null" << std::endl;
_handle->stmt_param_is_null[index] = is_null;
MYSQL_BIND& param = _handle->stmt_params[index];
param.buffer_type = MYSQL_TYPE_DOUBLE;
param.buffer = const_cast<double*>(value);
param.buffer_length = sizeof(*value);
param.length = &param.buffer_length;
param.is_null = &_handle->stmt_param_is_null[index].value;
param.is_unsigned = false;
param.error = nullptr;
}
void prepared_statement_t::_bind_text_parameter(size_t index, const std::string* value, bool is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding text parameter " << *value << " at index: " << index << ", being "
<< (is_null ? "" : "not ") << "null" << std::endl;
_handle->stmt_param_is_null[index] = is_null;
MYSQL_BIND& param = _handle->stmt_params[index];
param.buffer_type = MYSQL_TYPE_STRING;
param.buffer = const_cast<char*>(value->data());
param.buffer_length = value->size();
param.length = &param.buffer_length;
param.is_null = &_handle->stmt_param_is_null[index].value;
param.is_unsigned = false;
param.error = nullptr;
}
void prepared_statement_t::_bind_date_parameter(size_t index, const ::sqlpp::chrono::day_point* value, bool is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding date parameter "
<< " at index: " << index << ", being " << (is_null ? "" : "not ") << "null" << std::endl;
auto& bound_time = _handle->stmt_date_time_param_buffer[index];
if (not is_null)
{
const auto ymd = ::date::year_month_day{*value};
bound_time.year = static_cast<int>(ymd.year());
bound_time.month = static_cast<unsigned>(ymd.month());
bound_time.day = static_cast<unsigned>(ymd.day());
if (_handle->debug)
std::cerr << "bound values: " << bound_time.year << '-' << bound_time.month << '-' << bound_time.day << 'T'
<< bound_time.hour << ':' << bound_time.minute << ':' << bound_time.second << std::endl;
}
_handle->stmt_param_is_null[index] = is_null;
MYSQL_BIND& param = _handle->stmt_params[index];
param.buffer_type = MYSQL_TYPE_DATE;
param.buffer = &bound_time;
param.buffer_length = sizeof(MYSQL_TIME);
param.length = &param.buffer_length;
param.is_null = &_handle->stmt_param_is_null[index].value;
param.is_unsigned = false;
param.error = nullptr;
}
void prepared_statement_t::_bind_date_time_parameter(size_t index,
const ::sqlpp::chrono::microsecond_point* value,
bool is_null)
{
if (_handle->debug)
std::cerr << "MySQL debug: binding date_time parameter "
<< " at index: " << index << ", being " << (is_null ? "" : "not ") << "null" << std::endl;
auto& bound_time = _handle->stmt_date_time_param_buffer[index];
if (not is_null)
{
const auto dp = ::sqlpp::chrono::floor<::date::days>(*value);
const auto time = date::make_time(*value - dp);
const auto ymd = ::date::year_month_day{dp};
bound_time.year = static_cast<int>(ymd.year());
bound_time.month = static_cast<unsigned>(ymd.month());
bound_time.day = static_cast<unsigned>(ymd.day());
bound_time.hour = time.hours().count();
bound_time.minute = time.minutes().count();
bound_time.second = time.seconds().count();
bound_time.second_part = time.subseconds().count();
if (_handle->debug)
std::cerr << "bound values: " << bound_time.year << '-' << bound_time.month << '-' << bound_time.day << 'T'
<< bound_time.hour << ':' << bound_time.minute << ':' << bound_time.second << std::endl;
}
_handle->stmt_param_is_null[index] = is_null;
MYSQL_BIND& param = _handle->stmt_params[index];
param.buffer_type = MYSQL_TYPE_DATETIME;
param.buffer = &bound_time;
param.buffer_length = sizeof(MYSQL_TIME);
param.length = &param.buffer_length;
param.is_null = &_handle->stmt_param_is_null[index].value;
param.is_unsigned = false;
param.error = nullptr;
}
} }
} }

View File

@ -2,8 +2,10 @@ find_package(Threads REQUIRED)
macro(build_and_run arg) macro(build_and_run arg)
add_executable(Sqlpp11MySQL${arg} ${arg}.cpp) add_executable(Sqlpp11MySQL${arg} ${arg}.cpp)
target_link_libraries(Sqlpp11MySQL${arg} PRIVATE sqlpp-mysql)
target_link_libraries(Sqlpp11MySQL${arg} PRIVATE Threads::Threads) target_link_libraries(Sqlpp11MySQL${arg} PRIVATE Threads::Threads)
target_link_libraries(Sqlpp11MySQL${arg} PRIVATE sqlpp11::sqlpp11)
target_link_libraries(Sqlpp11MySQL${arg} PRIVATE MySQL::MySQL)
target_link_libraries(Sqlpp11MySQL${arg} PRIVATE date::date)
if(${arg} STREQUAL "JsonTest") if(${arg} STREQUAL "JsonTest")
target_link_libraries(Sqlpp11MySQL${arg} PRIVATE MySQL::MySQL) target_link_libraries(Sqlpp11MySQL${arg} PRIVATE MySQL::MySQL)
endif() endif()

View File

@ -37,7 +37,7 @@ SQLPP_ALIAS_PROVIDER(right)
namespace mysql = sqlpp::mysql; namespace mysql = sqlpp::mysql;
int main() int main()
{ {
mysql::global_library_init(); sqlpp::mysql::global_library_init();
auto config = std::make_shared<mysql::connection_config>(); auto config = std::make_shared<mysql::connection_config>();
config->user = "root"; config->user = "root";