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:
parent
7aa4edc0ea
commit
660e3bd1b6
Binary file not shown.
@ -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, ¶m, 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
|
||||||
|
@ -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,8 +42,58 @@ 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
|
||||||
{
|
{
|
||||||
@ -49,13 +101,21 @@ namespace sqlpp
|
|||||||
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
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
@ -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
|
|
@ -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 = ¶m.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 = ¶m.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 = ¶m.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 = ¶m.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 = ¶m.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 = ¶m.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 = ¶m.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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
42
include/sqlpp11/mysql/sqlpp_mysql.h
Normal file
42
include/sqlpp11/mysql/sqlpp_mysql.h
Normal 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
|
@ -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))
|
||||||
|
@ -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()
|
||||||
|
@ -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, ¶m, 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()");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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 = ¶m.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 = ¶m.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 = ¶m.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 = ¶m.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 = ¶m.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 = ¶m.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 = ¶m.buffer_length;
|
|
||||||
param.is_null = &_handle->stmt_param_is_null[index].value;
|
|
||||||
param.is_unsigned = false;
|
|
||||||
param.error = nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
find_package(Threads REQUIRED)
|
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()
|
||||||
|
@ -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";
|
||||||
|
Loading…
Reference in New Issue
Block a user