diff --git a/include/sqlpp11/mysql/.serializer.h.swp b/include/sqlpp11/mysql/.serializer.h.swp deleted file mode 100644 index 531fa9f5..00000000 Binary files a/include/sqlpp11/mysql/.serializer.h.swp and /dev/null differ diff --git a/include/sqlpp11/mysql/bind_result.h b/include/sqlpp11/mysql/bind_result.h index e0dce2c3..f0901e19 100644 --- a/include/sqlpp11/mysql/bind_result.h +++ b/include/sqlpp11/mysql/bind_result.h @@ -27,8 +27,10 @@ #ifndef SQLPP_MYSQL_BIND_RESULT_H #define SQLPP_MYSQL_BIND_RESULT_H +#include #include #include +#include namespace sqlpp { @@ -36,8 +38,73 @@ namespace sqlpp { 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 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 stmt_params; + std::vector stmt_date_time_param_buffer; + std::vector stmt_param_is_null; // my_bool is bool after 8.0, and vector is bad + std::vector result_params; + std::vector 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 { @@ -46,12 +113,20 @@ namespace sqlpp public: bind_result_t() = default; - bind_result_t(const std::shared_ptr& handle); + bind_result_t(const std::shared_ptr& 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(bind_result_t&& rhs) = default; bind_result_t& operator=(const bind_result_t&) = delete; 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 { @@ -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_floating_point_result(size_t index, double* value, bool* is_null); - void _bind_integral_result(size_t index, int64_t* value, bool* is_null); - void _bind_unsigned_integral_result(size_t index, uint64_t* value, bool* is_null); - void _bind_text_result(size_t index, const char** text, size_t* len); - 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); - void _bind_date_time_result(size_t index, ::sqlpp::chrono::microsecond_point* value, bool* is_null); + void _bind_boolean_result(size_t index, signed char* value, bool* is_null) + { + if (_handle->debug) + std::cerr << "MySQL debug: binding boolean result " << static_cast(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_integral_result(size_t index, int64_t* value, bool* is_null) + { + if (_handle->debug) + std::cerr << "MySQL debug: binding integral result " << static_cast(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(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(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(*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(*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(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(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 */) { @@ -114,13 +360,118 @@ namespace sqlpp 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_time_result(size_t index, ::sqlpp::chrono::microsecond_point* value, bool* is_null); + void _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(value) << " at index: " << index + << std::endl; + + if (not *is_null) + { + const auto& dt = + *reinterpret_cast(_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(value) << " at index: " << index + << std::endl; + + if (not *is_null) + { + const auto& dt = + *reinterpret_cast(_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: - void bind_impl(); - bool next_impl(); + void 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 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(*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(*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 diff --git a/include/sqlpp11/mysql/char_result.h b/include/sqlpp11/mysql/char_result.h index 665a3976..ad2fa265 100644 --- a/include/sqlpp11/mysql/char_result.h +++ b/include/sqlpp11/mysql/char_result.h @@ -29,9 +29,11 @@ #include #include +#include #include #include #include +#include #include namespace sqlpp @@ -40,8 +42,58 @@ namespace sqlpp { 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{1, 1, 1, 1, 0, 1, 1, 0, 1, 1}; // 2015-10-28 + const auto time_digits = std::vector{0, 1, 1, 0, 1, 1, 0, 1, 1}; // T23:00:12 + + auto check_digits(const char* text, const std::vector& 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 { @@ -49,13 +101,21 @@ namespace sqlpp char_result_row_t _char_result_row; public: - char_result_t(); - char_result_t(std::unique_ptr&& handle); + char_result_t() = default; + char_result_t(std::unique_ptr&& 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(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=(char_result_t&&); - ~char_result_t(); + char_result_t& operator=(char_result_t&&) = default; + ~char_result_t() = default; 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) { @@ -127,12 +190,101 @@ namespace sqlpp *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_time_result(size_t index, ::sqlpp::chrono::microsecond_point* value, bool* is_null); + void _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 (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: - 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(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 diff --git a/include/sqlpp11/mysql/connection.h b/include/sqlpp11/mysql/connection.h index c6674a36..9bb45d16 100644 --- a/include/sqlpp11/mysql/connection.h +++ b/include/sqlpp11/mysql/connection.h @@ -35,34 +35,187 @@ #include #include #include +#include +#include #include #include -#if LIBMYSQL_VERSION_ID < 80000 -typedef struct st_mysql MYSQL; -#else -struct MYSQL; -#endif - namespace sqlpp { 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 config; + std::unique_ptr mysql; + + connection_handle_t(const std::shared_ptr& 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 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( + 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 { - // 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 - void global_library_init(int argc = 0, char** argv = nullptr, char** groups = nullptr); - - namespace detail + void global_library_init(int argc = 0, char** argv = nullptr, char** groups = nullptr) { - struct connection_handle_t; + static const auto global_init_and_end = scoped_library_initializer_t(argc, argv, groups); } class connection; @@ -100,17 +253,68 @@ namespace sqlpp 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); + char_result_t select_impl(const std::string& statement) + { + execute_statement(*_handle, statement); + std::unique_ptr 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_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); + prepared_statement_t 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); + } + + 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: using _prepared_statement_t = ::sqlpp::mysql::prepared_statement_t; @@ -135,21 +339,41 @@ namespace sqlpp return serialize(t, context); } - connection(const std::shared_ptr& config); - ~connection(); + connection(const std::shared_ptr& config) : _handle(new detail::connection_handle_t(config)) + { + } + + ~connection() = default; + connection(const connection&) = delete; connection& operator=(const connection&) = delete; 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(); - void reconnect(); - const std::shared_ptr get_config(); + bool is_valid() + { + return _handle->is_valid(); + } + + void reconnect() + { + return _handle->reconnect(); + } + + const std::shared_ptr get_config() + { + return _handle->config; + } bool is_transaction_active() { return _transaction_active; } + template char_result_t select(const Select& s) { @@ -246,10 +470,18 @@ namespace sqlpp } //! 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) - std::string escape(const std::string& s) const; + std::string escape(const std::string& s) const + { + std::unique_ptr 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 template @@ -291,27 +523,60 @@ namespace sqlpp } //! 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) - 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 - // already) - void rollback_transaction(bool report); + //! rollback transaction (or throw if the transaction has been finished already) + 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) - 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) { return _db.escape(arg); } - } -} + } // namespace mysql +} // namespace sqlpp #include diff --git a/include/sqlpp11/mysql/connection.h.orig b/include/sqlpp11/mysql/connection.h.orig deleted file mode 100644 index 7cc8c36a..00000000 --- a/include/sqlpp11/mysql/connection.h.orig +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include -#include - -<<<<<<< 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 - 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 get_quote_left(const serializer_t&); - - std::integral_constant get_quote_right(const serializer_t&); - - class connection : public sqlpp::connection - { - std::unique_ptr _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 - static _context_t& _serialize_interpretable(const T& t, _context_t& context) - { - return ::sqlpp::serialize(t, context); - } - - template - static _context_t& _interpret_interpretable(const T& t, _context_t& context) - { - return ::sqlpp::serialize(t, context); - } - - connection(const std::shared_ptr& 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 get_config(); - - bool is_transaction_active() - { - return _transaction_active; - } - template - char_result_t select(const Select& s) - { - _context_t context(*this); - serialize(s, context); - return select_impl(context.str()); - } - - template - _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 - 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 - size_t insert(const Insert& i) - { - _context_t context(*this); - serialize(i, context); - return insert_impl(context.str()); - } - - template - _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 - 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 - size_t update(const Update& u) - { - _context_t context(*this); - serialize(u, context); - return update_impl(context.str()); - } - - template - _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 - 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 - size_t remove(const Remove& r) - { - _context_t context(*this); - serialize(r, context); - return remove_impl(context.str()); - } - - template - _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 - 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 - auto run(const T& t) -> decltype(t._run(*this)) - { - return t._run(*this); - } - - //! call run on the argument - template - auto _run(const T& t, ::sqlpp::consistent_t) -> decltype(t._run(*this)) - { - return t._run(*this); - } - - template - auto _run(const T& t, Check) -> Check; - - template - 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 - auto _prepare(const T& t, ::sqlpp::consistent_t) -> decltype(t._prepare(*this)) - { - return t._prepare(*this); - } - - template - auto _prepare(const T& t, Check) -> Check; - - template - 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 - -#endif diff --git a/include/sqlpp11/mysql/prepared_statement.h b/include/sqlpp11/mysql/prepared_statement.h index dd11f027..cadb3f2a 100644 --- a/include/sqlpp11/mysql/prepared_statement.h +++ b/include/sqlpp11/mysql/prepared_statement.h @@ -27,8 +27,14 @@ #ifndef SQLPP_MYSQL_PREPARED_STATEMENT_H #define SQLPP_MYSQL_PREPARED_STATEMENT_H +#include + +#include +#include #include +#include #include +#include #include namespace sqlpp @@ -37,11 +43,6 @@ namespace sqlpp { class connection; - namespace detail - { - struct prepared_statement_handle_t; - } - class prepared_statement_t { friend ::sqlpp::mysql::connection; @@ -49,7 +50,12 @@ namespace sqlpp public: prepared_statement_t() = delete; - prepared_statement_t(std::shared_ptr&& handle); + prepared_statement_t(std::shared_ptr&& 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(prepared_statement_t&& rhs) = default; prepared_statement_t& operator=(const prepared_statement_t&) = delete; @@ -63,14 +69,150 @@ namespace sqlpp void _pre_bind(); - 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); - void _bind_floating_point_parameter(size_t index, const double* value, bool is_null); - void _bind_text_parameter(size_t index, const std::string* value, bool is_null); - void _bind_date_parameter(size_t index, const ::sqlpp::chrono::day_point* value, bool is_null); - void _bind_date_time_parameter(size_t index, const ::sqlpp::chrono::microsecond_point* value, bool is_null); + void _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(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(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(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(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(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(ymd.year()); + bound_time.month = static_cast(ymd.month()); + bound_time.day = static_cast(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(ymd.year()); + bound_time.month = static_cast(ymd.month()); + bound_time.day = static_cast(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 diff --git a/include/sqlpp11/mysql/remove.h b/include/sqlpp11/mysql/remove.h index a8935177..eaf317ae 100644 --- a/include/sqlpp11/mysql/remove.h +++ b/include/sqlpp11/mysql/remove.h @@ -33,9 +33,11 @@ namespace sqlpp { - namespace mysql{ + namespace mysql + { template - using blank_remove_t = statement_t, no_order_by_t, no_limit_t>; + using blank_remove_t = + statement_t, no_order_by_t, no_limit_t>; inline auto remove() -> blank_remove_t { @@ -49,19 +51,20 @@ namespace sqlpp } template - auto dynamic_remove(const Database & /*unused*/) -> decltype(blank_remove_t()) + auto dynamic_remove(const Database& /*unused*/) -> decltype(blank_remove_t()) { static_assert(std::is_base_of::value, "Invalid database parameter"); return {blank_remove_t()}; } template - auto dynamic_remove_from(const Database& /*unused*/, Table table) -> decltype(blank_remove_t().from(table)) + auto dynamic_remove_from(const Database& /*unused*/, Table table) + -> decltype(blank_remove_t().from(table)) { static_assert(std::is_base_of::value, "Invalid database parameter"); return {blank_remove_t().from(table)}; } - } //namespace mysql + } // namespace mysql } // namespace sqlpp diff --git a/include/sqlpp11/mysql/sqlpp_mysql.h b/include/sqlpp11/mysql/sqlpp_mysql.h new file mode 100644 index 00000000..f497e44c --- /dev/null +++ b/include/sqlpp11/mysql/sqlpp_mysql.h @@ -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 + +namespace sqlpp +{ + namespace mysql + { +#if LIBMYSQL_VERSION_ID >= 80000 + using my_bool = bool; +#endif + } +} + +#endif diff --git a/include/sqlpp11/mysql/update.h b/include/sqlpp11/mysql/update.h index c48d085f..3eefc048 100644 --- a/include/sqlpp11/mysql/update.h +++ b/include/sqlpp11/mysql/update.h @@ -33,9 +33,16 @@ namespace sqlpp { - namespace mysql{ + namespace mysql + { template - using blank_update_t = statement_t, no_order_by_t, no_limit_t>; + using blank_update_t = statement_t, + no_order_by_t, + no_limit_t>; template constexpr auto update(Table table) -> decltype(blank_update_t().single_table(table)) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ed4e6ba9..6219cf70 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,10 +21,6 @@ # 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. -if(MYSQL_CONNECTOR OR MARIADB_CONNECTOR) - add_subdirectory(mysql) -endif() - if(SQLITE3_CONNECTOR OR SQLCIPHER_CONNECTOR) add_subdirectory(sqlite3) endif() diff --git a/src/mysql/bind_result.cpp b/src/mysql/bind_result.cpp index 4bcf26f5..5cf09d32 100644 --- a/src/mysql/bind_result.cpp +++ b/src/mysql/bind_result.cpp @@ -36,311 +36,6 @@ namespace sqlpp { namespace mysql { - bind_result_t::bind_result_t(const std::shared_ptr& 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(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(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(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(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(*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(*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(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(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(value) << " at index: " << index - << std::endl; - - if (not *is_null) - { - const auto& dt = - *reinterpret_cast(_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(value) << " at index: " << index - << std::endl; - - if (not *is_null) - { - const auto& dt = - *reinterpret_cast(_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(*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(*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()"); - } - } } } diff --git a/src/mysql/char_result.cpp b/src/mysql/char_result.cpp index 8da0e600..413734c2 100644 --- a/src/mysql/char_result.cpp +++ b/src/mysql/char_result.cpp @@ -36,149 +36,10 @@ namespace sqlpp { namespace mysql { - char_result_t::char_result_t() - { - } - - char_result_t::char_result_t(std::unique_ptr&& 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 { - const auto date_digits = std::vector{1, 1, 1, 1, 0, 1, 1, 0, 1, 1}; // 2015-10-28 - const auto time_digits = std::vector{0, 1, 1, 0, 1, 1, 0, 1, 1}; // T23:00:12 - - auto check_digits(const char* text, const std::vector& 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(mysql_fetch_row(_handle->mysql_res)); - _char_result_row.len = mysql_fetch_lengths(_handle->mysql_res); - - return _char_result_row.data; - } } } diff --git a/src/mysql/connection.cpp b/src/mysql/connection.cpp index 1919b540..157fbb45 100644 --- a/src/mysql/connection.cpp +++ b/src/mysql/connection.cpp @@ -36,253 +36,6 @@ namespace sqlpp { 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 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( - 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& 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::get_config() - { - return _handle->config; - } - - char_result_t connection::select_impl(const std::string& statement) - { - execute_statement(*_handle, statement); - std::unique_ptr 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 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(); - } } } diff --git a/src/mysql/detail/connection_handle.cpp b/src/mysql/detail/connection_handle.cpp index 434fc215..5cae0ae8 100644 --- a/src/mysql/detail/connection_handle.cpp +++ b/src/mysql/detail/connection_handle.cpp @@ -35,65 +35,6 @@ namespace sqlpp { 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& 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); - } } } } diff --git a/src/mysql/detail/connection_handle.h b/src/mysql/detail/connection_handle.h index 3f6bc90f..6791bc4d 100644 --- a/src/mysql/detail/connection_handle.h +++ b/src/mysql/detail/connection_handle.h @@ -38,25 +38,8 @@ namespace sqlpp namespace detail { - void handle_cleanup(MYSQL* mysql); - - struct connection_handle_t - { - const std::shared_ptr config; - std::unique_ptr mysql; - - connection_handle_t(const std::shared_ptr& 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(); - }; - } - } + } // namespace detail + } // namespace mysql } #endif diff --git a/src/mysql/prepared_statement.cpp b/src/mysql/prepared_statement.cpp index 8ea175d1..baabdbdd 100644 --- a/src/mysql/prepared_statement.cpp +++ b/src/mysql/prepared_statement.cpp @@ -48,157 +48,5 @@ namespace sqlpp { namespace mysql { - prepared_statement_t::prepared_statement_t(std::shared_ptr&& 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(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(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(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(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(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(ymd.year()); - bound_time.month = static_cast(ymd.month()); - bound_time.day = static_cast(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(ymd.year()); - bound_time.month = static_cast(ymd.month()); - bound_time.day = static_cast(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; - } } } diff --git a/tests/mysql/usage/CMakeLists.txt b/tests/mysql/usage/CMakeLists.txt index 410045d7..bbea5292 100644 --- a/tests/mysql/usage/CMakeLists.txt +++ b/tests/mysql/usage/CMakeLists.txt @@ -1,9 +1,11 @@ find_package(Threads REQUIRED) macro(build_and_run arg) - add_executable(Sqlpp11MySQL${arg} ${arg}.cpp) - target_link_libraries(Sqlpp11MySQL${arg} PRIVATE sqlpp-mysql) + add_executable(Sqlpp11MySQL${arg} ${arg}.cpp) 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") target_link_libraries(Sqlpp11MySQL${arg} PRIVATE MySQL::MySQL) endif() diff --git a/tests/mysql/usage/SampleTest.cpp b/tests/mysql/usage/SampleTest.cpp index ba99d6de..b547cddf 100644 --- a/tests/mysql/usage/SampleTest.cpp +++ b/tests/mysql/usage/SampleTest.cpp @@ -37,7 +37,7 @@ SQLPP_ALIAS_PROVIDER(right) namespace mysql = sqlpp::mysql; int main() { - mysql::global_library_init(); + sqlpp::mysql::global_library_init(); auto config = std::make_shared(); config->user = "root";