diff --git a/CMakeLists.txt b/CMakeLists.txt index 849669c8..c7094556 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,10 @@ cmake_minimum_required(VERSION 3.14) project(sqlpp11 VERSION 0.1 LANGUAGES CXX) +### Project Wide Setup +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/") + +option(MARIADB_CONNECTOR "Build MariaDB Connector" OFF) option(MYSQL_CONNECTOR "Build MySQL Connector" OFF) option(POSTGRESQL_CONNECTOR "Build PostgreSQL Connector" OFF) option(SQLITE3_CONNECTOR "Build SQLite3 Connector" OFF) @@ -38,6 +42,12 @@ else() message(STATUS "Not building MYSQL_CONNECTOR") endif() +if(MARIADB_CONNECTOR) + find_package(MariaDB REQUIRED) +else() + message(STATUS "Not building MARIAB_CONNECTOR") +endif() + if(POSTGRESQL_CONNECTOR) find_package(PostgreSQL REQUIRED) else() @@ -56,9 +66,6 @@ else() message(STATUS "Not building SQLCIPHER_CONNECTOR") endif() -### Project Wide Setup -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/") - include(CTest) option(USE_SYSTEM_DATE "\ @@ -70,7 +77,7 @@ if(USE_SYSTEM_DATE) find_package(date REQUIRED) endif() -### Dependencies +### Dependencies add_subdirectory(dependencies) ### Core targets diff --git a/cmake/FindMariaDB.cmake b/cmake/FindMariaDB.cmake new file mode 100644 index 00000000..89c2f82a --- /dev/null +++ b/cmake/FindMariaDB.cmake @@ -0,0 +1,35 @@ +# FindMySQL.cmake + +if(DEFINED MSVC) + find_path(MySQL_INCLUDE_DIR + NAMES mariadb_version.h + PATH_SUFFIXES include + ) + find_library(MySQL_LIBRARY + NAMES libmariadb + PATH_SUFFIXES lib + ) +else() + find_path(MySQL_INCLUDE_DIR + NAMES mariadb_version.h + PATH_SUFFIXES mariadb mysql + ) + find_library(MySQL_LIBRARY NAMES mariadb) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + MySQL + MySQL_INCLUDE_DIR + MySQL_LIBRARY +) + +if(MySQL_FOUND AND NOT TARGET MySQL::MySQL) + add_library(MySQL::MySQL UNKNOWN IMPORTED) + target_include_directories(MySQL::MySQL INTERFACE "${MySQL_INCLUDE_DIR}") + set_target_properties(MySQL::MySQL PROPERTIES + IMPORTED_LOCATION "${MySQL_LIBRARY}" + IMPORTED_LINK_INTERFACE_LANGUAGES "C") +endif() + +mark_as_advanced(MySQL_INCLUDE_DIR MySQL_LIBRARY) diff --git a/cmake/FindMySQL.cmake b/cmake/FindMySQL.cmake new file mode 100644 index 00000000..197f084b --- /dev/null +++ b/cmake/FindMySQL.cmake @@ -0,0 +1,45 @@ +# FindMySQL.cmake + +if(DEFINED MSVC) + set(SEARCH_PATHS + "$ENV{ProgramFiles}/MySQL/MySQL Server 8.0" + "$ENV{ProgramFiles}/MySQL/MySQL Server 5.7" + "$ENV{ProgramFiles}/MySQL/MySQL Server 5.6" + "$ENV{ProgramFiles\(x86\)}/MySQL/MySQL Server 8.0" + "$ENV{ProgramFiles\(x86\)}/MySQL/MySQL Server 5.7" + "$ENV{ProgramFiles\(x86\)}/MySQL/MySQL Server 5.6" + ) + find_path(MySQL_INCLUDE_DIR + NAMES mysql_version.h + PATHS ${SEARCH_PATHS} + PATH_SUFFIXES include + ) + find_library(MySQL_LIBRARY + NAMES libmysql + PATHS ${SEARCH_PATHS} + PATH_SUFFIXES lib + ) +else() + find_path(MySQL_INCLUDE_DIR + NAMES mysql_version.h + PATH_SUFFIXES mysql + ) + find_library(MySQL_LIBRARY NAMES mysqlclient mysqlclient_r) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + MySQL + MySQL_INCLUDE_DIR + MySQL_LIBRARY +) + +if(MySQL_FOUND AND NOT TARGET MySQL::MySQL) + add_library(MySQL::MySQL UNKNOWN IMPORTED) + target_include_directories(MySQL::MySQL INTERFACE "${MySQL_INCLUDE_DIR}") + set_target_properties(MySQL::MySQL PROPERTIES + IMPORTED_LOCATION "${MySQL_LIBRARY}" + IMPORTED_LINK_INTERFACE_LANGUAGES "C") +endif() + +mark_as_advanced(MySQL_INCLUDE_DIR MySQL_LIBRARY) diff --git a/include/sqlpp11/mysql/.serializer.h.swp b/include/sqlpp11/mysql/.serializer.h.swp new file mode 100644 index 00000000..531fa9f5 Binary files /dev/null and b/include/sqlpp11/mysql/.serializer.h.swp differ diff --git a/include/sqlpp11/mysql/bind_result.h b/include/sqlpp11/mysql/bind_result.h new file mode 100644 index 00000000..e0dce2c3 --- /dev/null +++ b/include/sqlpp11/mysql/bind_result.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2013 - 2015, 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_BIND_RESULT_H +#define SQLPP_MYSQL_BIND_RESULT_H + +#include +#include + +namespace sqlpp +{ + namespace mysql + { + namespace detail + { + struct prepared_statement_handle_t; + } + + class bind_result_t + { + std::shared_ptr _handle; + void* _result_row_address = nullptr; + + public: + bind_result_t() = default; + bind_result_t(const std::shared_ptr& handle); + 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(); + + bool operator==(const bind_result_t& rhs) const + { + return _handle == rhs._handle; + } + + template + void next(ResultRow& result_row) + { + if (_invalid()) + { + result_row._invalidate(); + return; + } + + if (&result_row != _result_row_address) + { + result_row._bind(*this); // sets row data to mysql bind data + bind_impl(); // binds mysql statement to data + _result_row_address = &result_row; + } + if (next_impl()) + { + if (not result_row) + { + result_row._validate(); + } + result_row._post_bind(*this); // translates bind_data to row data where required + } + else + { + if (result_row) + result_row._invalidate(); + } + } + + bool _invalid() const; + + 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 _post_bind_boolean_result(size_t /* index */, signed char* /* value */, bool* /* is_null */) + { + } + void _post_bind_floating_point_result(size_t /* index */, double* /* value */, bool* /* is_null */) + { + } + void _post_bind_integral_result(size_t /* index */, int64_t* /* value */, bool* /* is_null */) + { + } + void _post_bind_unsigned_integral_result(size_t /* index */, uint64_t* /* value */, bool* /* is_null */) + { + } + 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); + + private: + void bind_impl(); + bool next_impl(); + }; + } +} +#endif diff --git a/include/sqlpp11/mysql/char_result.h b/include/sqlpp11/mysql/char_result.h new file mode 100644 index 00000000..665a3976 --- /dev/null +++ b/include/sqlpp11/mysql/char_result.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2013 - 2015, 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_CHAR_RESULT_H +#define SQLPP_MYSQL_CHAR_RESULT_H + +#include +#include +#include +#include +#include +#include + +namespace sqlpp +{ + namespace mysql + { + namespace detail + { + struct result_handle; + } + + class char_result_t + { + std::unique_ptr _handle; + char_result_row_t _char_result_row; + + public: + char_result_t(); + char_result_t(std::unique_ptr&& handle); + char_result_t(const char_result_t&) = delete; + char_result_t(char_result_t&& rhs); + char_result_t& operator=(const char_result_t&) = delete; + char_result_t& operator=(char_result_t&&); + ~char_result_t(); + + bool operator==(const char_result_t& rhs) const + { + return _handle == rhs._handle; + } + + template + void next(ResultRow& result_row) + { + if (_invalid()) + { + result_row._invalidate(); + return; + } + + if (next_impl()) + { + if (not result_row) + { + result_row._validate(); + } + result_row._bind(*this); + } + else + { + if (result_row) + result_row._invalidate(); + } + } + + bool _invalid() const; + + void _bind_boolean_result(size_t index, signed char* value, bool* is_null) + { + *is_null = (_char_result_row.data == nullptr or _char_result_row.data[index] == nullptr); + *value = + (*is_null ? false : (_char_result_row.data[index][0] == 't' or _char_result_row.data[index][0] == '1')); + } + + void _bind_floating_point_result(size_t index, double* value, bool* is_null) + { + *is_null = (_char_result_row.data == nullptr or _char_result_row.data[index] == nullptr); + *value = (*is_null ? 0 : std::strtod(_char_result_row.data[index], nullptr)); + } + + void _bind_integral_result(size_t index, int64_t* value, bool* is_null) + { + *is_null = (_char_result_row.data == nullptr or _char_result_row.data[index] == nullptr); + *value = (*is_null ? 0 : std::strtoll(_char_result_row.data[index], nullptr, 10)); + } + + void _bind_unsigned_integral_result(size_t index, uint64_t* value, bool* is_null) + { + *is_null = (_char_result_row.data == nullptr or _char_result_row.data[index] == nullptr); + *value = (*is_null ? 0 : std::strtoull(_char_result_row.data[index], nullptr, 10)); + } + + void _bind_blob_result(size_t index, const uint8_t** value, size_t* len) + { + bool is_null = (_char_result_row.data == nullptr or _char_result_row.data[index] == nullptr); + *value = (uint8_t*)(is_null ? nullptr : _char_result_row.data[index]); + *len = (is_null ? 0 : _char_result_row.len[index]); + } + + void _bind_text_result(size_t index, const char** value, size_t* len) + { + bool is_null = (_char_result_row.data == nullptr or _char_result_row.data[index] == nullptr); + *value = (is_null ? nullptr : _char_result_row.data[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_time_result(size_t index, ::sqlpp::chrono::microsecond_point* value, bool* is_null); + + private: + bool next_impl(); + }; + } +} +#endif diff --git a/include/sqlpp11/mysql/char_result_row.h b/include/sqlpp11/mysql/char_result_row.h new file mode 100644 index 00000000..c4ccc460 --- /dev/null +++ b/include/sqlpp11/mysql/char_result_row.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2013-2015, 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_CHAR_RESULT_ROW_H +#define SQLPP_MYSQL_CHAR_RESULT_ROW_H + +namespace sqlpp +{ + namespace mysql + { + struct char_result_row_t + { + const char** data; + unsigned long* len; + + bool operator==(const char_result_row_t& rhs) const + { + return data == rhs.data && len == rhs.len; + } + }; + } +} + +#endif diff --git a/include/sqlpp11/mysql/connection.h b/include/sqlpp11/mysql/connection.h new file mode 100644 index 00000000..0edc8efb --- /dev/null +++ b/include/sqlpp11/mysql/connection.h @@ -0,0 +1,316 @@ +/* + * 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 + +#if LIBMYSQL_VERSION_ID < 80000 +typedef struct st_mysql MYSQL; +#else +struct MYSQL; +#endif + +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 serialize(t, context); + } + + template + static _context_t& _interpret_interpretable(const T& t, _context_t& context) + { + return 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/connection.h.orig b/include/sqlpp11/mysql/connection.h.orig new file mode 100644 index 00000000..7cc8c36a --- /dev/null +++ b/include/sqlpp11/mysql/connection.h.orig @@ -0,0 +1,320 @@ +/* + * 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/connection_config.h b/include/sqlpp11/mysql/connection_config.h new file mode 100644 index 00000000..cd695510 --- /dev/null +++ b/include/sqlpp11/mysql/connection_config.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2013 - 2015, 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_CONFIG_H +#define SQLPP_MYSQL_CONNECTION_CONFIG_H + +#include + +namespace sqlpp +{ + namespace mysql + { + class connection; + struct connection_config + { + typedef ::sqlpp::mysql::connection connection; + std::string host = "localhost"; + std::string user; + std::string password; + std::string database; + unsigned int port = 0; + std::string unix_socket; + unsigned long client_flag = 0; + std::string charset = "utf8"; + bool auto_reconnect = true; + bool debug = false; + + bool operator==(const connection_config& other) const + { + return (other.host == host and other.user == user and other.password == password and + other.database == database and other.charset == charset and other.auto_reconnect == auto_reconnect and + other.debug == debug); + } + + bool operator!=(const connection_config& other) const + { + return !operator==(other); + } + }; + } +} + +#endif diff --git a/include/sqlpp11/mysql/mysql.h b/include/sqlpp11/mysql/mysql.h new file mode 100644 index 00000000..f817507a --- /dev/null +++ b/include/sqlpp11/mysql/mysql.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2013 - 2015, 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_H +#define SQLPP_MYSQL_H + +#include +#include + +#endif diff --git a/include/sqlpp11/mysql/prepared_statement.h b/include/sqlpp11/mysql/prepared_statement.h new file mode 100644 index 00000000..dd11f027 --- /dev/null +++ b/include/sqlpp11/mysql/prepared_statement.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2013 - 2015, 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_PREPARED_STATEMENT_H +#define SQLPP_MYSQL_PREPARED_STATEMENT_H + +#include +#include +#include + +namespace sqlpp +{ + namespace mysql + { + class connection; + + namespace detail + { + struct prepared_statement_handle_t; + } + + class prepared_statement_t + { + friend ::sqlpp::mysql::connection; + std::shared_ptr _handle; + + public: + prepared_statement_t() = delete; + prepared_statement_t(std::shared_ptr&& handle); + 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; + prepared_statement_t& operator=(prepared_statement_t&&) = default; + ~prepared_statement_t() = default; + + bool operator==(const prepared_statement_t& rhs) const + { + return _handle == rhs._handle; + } + + 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); + }; + } +} +#endif diff --git a/include/sqlpp11/mysql/serializer.h b/include/sqlpp11/mysql/serializer.h new file mode 100644 index 00000000..e7d09f71 --- /dev/null +++ b/include/sqlpp11/mysql/serializer.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2013 - 2015, 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_SERIALIZER_H +#define SQLPP_MYSQL_SERIALIZER_H + +#include +#include + +namespace sqlpp +{ + template + mysql::serializer_t& serialize(const concat_t& t, mysql::serializer_t& context) + { + context << "CONCAT("; + interpret_tuple(t._args, ',', context); + context << ')'; + return context; + } + + inline mysql::serializer_t& serialize(const insert_default_values_data_t&, mysql::serializer_t& context) + { + context << " () VALUES()"; + return context; + } +} + +#endif diff --git a/src/mysql/CMakeLists.txt b/src/mysql/CMakeLists.txt new file mode 100644 index 00000000..65c0f1e6 --- /dev/null +++ b/src/mysql/CMakeLists.txt @@ -0,0 +1,78 @@ +set(PUBLIC_HEADERS_DIR "${PROJECT_SOURCE_DIR}/include/") + +add_library(sqlpp-mysql + bind_result.cpp + char_result.cpp + connection.cpp + prepared_statement.cpp + detail/connection_handle.cpp +) + +target_include_directories(sqlpp-mysql PUBLIC + $ +) + +find_package(date REQUIRED) +find_package(Sqlpp11 REQUIRED) + +target_link_libraries(sqlpp-mysql PUBLIC sqlpp11::sqlpp11) +target_link_libraries(sqlpp-mysql PRIVATE MySQL::MySQL) +target_link_libraries(sqlpp-mysql PRIVATE date::date) + +# Installation + +include(GNUInstallDirs) + +add_library(sqlpp11::mysql ALIAS sqlpp-mysql) +set_target_properties(sqlpp-mysql PROPERTIES EXPORT_NAME sqlpp11::mysql) + +install( + TARGETS sqlpp-mysql + EXPORT sqlpp-mysql-targets + COMPONENT sqlpp11::mysql + INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" +) + +install( + DIRECTORY ${PUBLIC_HEADERS_DIR} + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + COMPONENT sqlpp11::mysql +) + +include(CMakePackageConfigHelpers) + +set(cmake_config_path "${CMAKE_INSTALL_LIBDIR}/cmake/sqlpp-mysql") + +configure_package_config_file( + sqlpp-mysql-config.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/sqlpp-mysql-config.cmake" + INSTALL_DESTINATION "${cmake_config_path}" +) + +write_basic_package_version_file( + sqlpp-mysql-config-version.cmake + VERSION ${VERSION} + COMPATIBILITY SameMajorVersion + ${extra_version_file_args} +) + +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/sqlpp-mysql-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/sqlpp-mysql-config-version.cmake" + DESTINATION "${cmake_config_path}" + COMPONENT sqlpp11::mysql +) + +install(FILES + "${PROJECT_SOURCE_DIR}/cmake/FindMySQL.cmake" + DESTINATION "${cmake_config_path}" + COMPONENT sqlpp11::mysql +) + +install( + EXPORT sqlpp-mysql-targets + FILE sqlpp-mysql-targets.cmake + DESTINATION "${cmake_config_path}" + COMPONENT sqlpp11::mysql +) diff --git a/src/mysql/bind_result.cpp b/src/mysql/bind_result.cpp new file mode 100644 index 00000000..4bcf26f5 --- /dev/null +++ b/src/mysql/bind_result.cpp @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2013 - 2016, 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. + */ + +#include "detail/prepared_statement_handle.h" +#include +#include +#include +#include "sqlpp_mysql.h" +#include +#include + +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 new file mode 100644 index 00000000..8da0e600 --- /dev/null +++ b/src/mysql/char_result.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2013 - 2015, 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. + */ + +#include "detail/result_handle.h" +#include +#include +#include +#include +#include +#include + +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 new file mode 100644 index 00000000..1919b540 --- /dev/null +++ b/src/mysql/connection.cpp @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2013 - 2015, 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. + */ + +#include +#include +#include "detail/connection_handle.h" +#include "detail/prepared_statement_handle.h" +#include "detail/result_handle.h" +#include +#include + +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/connection.cpp.orig b/src/mysql/connection.cpp.orig new file mode 100644 index 00000000..4b7403dc --- /dev/null +++ b/src/mysql/connection.cpp.orig @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2013 - 2015, 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. + */ + +#include +#include +#include "detail/connection_handle.h" +#include "detail/prepared_statement_handle.h" +#include "detail/result_handle.h" +#include +#include + +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; + } + +<<<<<<< Updated upstream + MYSQL* connection::get_handle(){ +======= + MYSQL* connection::get_handle() + { +>>>>>>> Stashed changes + return _handle->mysql.get(); + } + } +} diff --git a/src/mysql/detail/connection_handle.cpp b/src/mysql/detail/connection_handle.cpp new file mode 100644 index 00000000..434fc215 --- /dev/null +++ b/src/mysql/detail/connection_handle.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2013 - 2015, 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. + */ + +#include "connection_handle.h" +#include +#include +#include + +namespace sqlpp +{ + namespace mysql + { + 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 new file mode 100644 index 00000000..3f6bc90f --- /dev/null +++ b/src/mysql/detail/connection_handle.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2013 - 2015, 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_HANDLE_H +#define SQLPP_MYSQL_CONNECTION_HANDLE_H + +#include +#include "../sqlpp_mysql.h" + +namespace sqlpp +{ + namespace mysql + { + struct connection_config; + + 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(); + }; + } + } +} + +#endif diff --git a/src/mysql/detail/prepared_statement_handle.h b/src/mysql/detail/prepared_statement_handle.h new file mode 100644 index 00000000..9d27ca9f --- /dev/null +++ b/src/mysql/detail/prepared_statement_handle.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2013 - 2015, 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_DETAIL_PREPARED_STATEMENT_HANDLE_H +#define SQLPP_MYSQL_DETAIL_PREPARED_STATEMENT_HANDLE_H + +#include "../sqlpp_mysql.h" +#include + +namespace sqlpp +{ + namespace mysql + { + namespace detail + { + 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; + } + }; + } + } +} + +#endif diff --git a/src/mysql/detail/result_handle.h b/src/mysql/detail/result_handle.h new file mode 100644 index 00000000..2aa0f52b --- /dev/null +++ b/src/mysql/detail/result_handle.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2013 - 2015, 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_DETAIL_RESULT_HANDLE_H +#define SQLPP_MYSQL_DETAIL_RESULT_HANDLE_H + +#include "../sqlpp_mysql.h" + +namespace sqlpp +{ + namespace mysql + { + namespace detail + { + 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; + } + }; + } + } +} + +#endif diff --git a/src/mysql/prepared_statement.cpp b/src/mysql/prepared_statement.cpp new file mode 100644 index 00000000..8ea175d1 --- /dev/null +++ b/src/mysql/prepared_statement.cpp @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2013 - 2016, 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. + */ + +#ifdef _WIN32 +// It seems that Windows.h will be included directly and indirectly. +// These defines prevent min/max macros and a bunch of other stuff +// to be defined in that header. +#ifndef NOMINMAX +#define NOMINMAX +#endif +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#endif // _WIN32 + +#include "detail/prepared_statement_handle.h" +#include +#include +#include +#include +#include + +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/src/mysql/sqlpp-mysql-config.cmake.in b/src/mysql/sqlpp-mysql-config.cmake.in new file mode 100644 index 00000000..598d410a --- /dev/null +++ b/src/mysql/sqlpp-mysql-config.cmake.in @@ -0,0 +1,12 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") + +find_dependency(Sqlpp11 REQUIRED) +find_dependency(MySQL REQUIRED) + +include("${CMAKE_CURRENT_LIST_DIR}/sqlpp-mysql-targets.cmake") + +check_required_components(sqlpp-mysql) diff --git a/src/mysql/sqlpp_mysql.h b/src/mysql/sqlpp_mysql.h new file mode 100644 index 00000000..f497e44c --- /dev/null +++ b/src/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/tests/mysql/CMakeLists.txt b/tests/mysql/CMakeLists.txt new file mode 100644 index 00000000..dca5a095 --- /dev/null +++ b/tests/mysql/CMakeLists.txt @@ -0,0 +1,26 @@ +# Copyright (c) 2021-2021, 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: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. 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. +add_subdirectory(usage) + + diff --git a/tests/mysql/usage/CMakeLists.txt b/tests/mysql/usage/CMakeLists.txt new file mode 100644 index 00000000..f6df8364 --- /dev/null +++ b/tests/mysql/usage/CMakeLists.txt @@ -0,0 +1,25 @@ +find_package(Threads REQUIRED) + +macro(build_and_run arg) + add_executable(Sqlpp11MySQL${arg} ${arg}.cpp) + target_link_libraries(Sqlpp11MySQL${arg} PRIVATE sqlpp-mysql) + target_link_libraries(Sqlpp11MySQL${arg} PRIVATE Threads::Threads) + if(${arg} STREQUAL "JsonTest") + target_link_libraries(Sqlpp11MySQL${arg} PRIVATE MySQL::MySQL) + endif() + if(NOT MSVC) + target_compile_options(Sqlpp11MySQL${arg} PRIVATE -Wall -Wextra -pedantic) + endif() + add_test(${arg} Sqlpp11MySQL${arg}) +endmacro() + +build_and_run(JsonTest) +build_and_run(CustomQuery) +build_and_run(DateTimeTest) +build_and_run(SampleTest) +build_and_run(SelectTest) +build_and_run(UnionTest) +build_and_run(DynamicSelectTest) +build_and_run(MoveConstructorTest) +build_and_run(PreparedTest) +build_and_run(TruncatedTest) diff --git a/tests/mysql/usage/CustomQuery.cpp b/tests/mysql/usage/CustomQuery.cpp new file mode 100644 index 00000000..af0995fa --- /dev/null +++ b/tests/mysql/usage/CustomQuery.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2013-2021, 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. + */ + +#include +#include "TabSample.h" +#include +#include +#include + +SQLPP_ALIAS_PROVIDER(left) +SQLPP_ALIAS_PROVIDER(right) +namespace +{ + struct on_duplicate_key_update + { + std::string _serialized; + + template + on_duplicate_key_update(Db& db, Assignment assignment) + { + typename Db::_serializer_context_t context{db}; + _serialized = " ON DUPLICATE KEY UPDATE " + serialize(assignment, context).str(); + } + + template + auto operator()(Db& db, Assignment assignment) -> on_duplicate_key_update& + { + typename Db::_serializer_context_t context{db}; + _serialized += ", " + serialize(assignment, context).str(); + return *this; + } + + auto get() const -> sqlpp::verbatim_t<::sqlpp::no_value_t> + { + return ::sqlpp::verbatim(_serialized); + } + }; +} // namespace + +const auto tab = TabSample{}; + +namespace mysql = sqlpp::mysql; +int main() +{ + mysql::global_library_init(); + + auto config = std::make_shared(); + config->user = "root"; + config->database = "sqlpp_mysql"; + config->debug = true; + try + { + mysql::connection db(config); + } + catch (const sqlpp::exception& e) + { + std::cerr << "For testing, you'll need to create a database sqlpp_mysql for user root (no password)" << std::endl; + std::cerr << e.what() << std::endl; + return 1; + } + try + { + mysql::connection db(config); + db.execute(R"(DROP TABLE IF EXISTS tab_sample)"); + db.execute(R"(CREATE TABLE tab_sample ( + alpha bigint(20) AUTO_INCREMENT, + beta varchar(255) DEFAULT NULL, + gamma bool DEFAULT NULL, + PRIMARY KEY (alpha) + ))"); + db.execute(R"(DROP TABLE IF EXISTS tab_foo)"); + db.execute(R"(CREATE TABLE tab_foo ( + omega bigint(20) DEFAULT NULL + ))"); + + // Create a MYSQL style custom "insert on duplicate update" + db(custom_query(sqlpp::insert_into(tab).set(tab.beta = "sample", tab.gamma = true), + on_duplicate_key_update(db, tab.beta = "sample")(db, tab.gamma = false).get())); + } + catch (const std::exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + return 1; + } + return 0; +} diff --git a/tests/mysql/usage/DateTimeTest.cpp b/tests/mysql/usage/DateTimeTest.cpp new file mode 100644 index 00000000..34523913 --- /dev/null +++ b/tests/mysql/usage/DateTimeTest.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2013 - 2016, 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. + */ + +#include "TabSample.h" +#include +#include + +#include +#include +#include + +const auto library_raii = sqlpp::mysql::scoped_library_initializer_t{}; + +namespace +{ + const auto now = ::sqlpp::chrono::floor<::std::chrono::milliseconds>(std::chrono::system_clock::now()); + const auto today = ::sqlpp::chrono::floor<::sqlpp::chrono::days>(now); + const auto yesterday = today - ::sqlpp::chrono::days{1}; + + template + auto require_equal(int line, const L& l, const R& r) -> void + { + if (l != r) + { + std::cerr << line << ": "; + serialize(::sqlpp::wrap_operand_t{l}, std::cerr); + std::cerr << " != "; + serialize(::sqlpp::wrap_operand_t{r}, std::cerr); + throw std::runtime_error("Unexpected result"); + } + } +} + +namespace mysql = sqlpp::mysql; +int main() +{ + auto config = std::make_shared(); + config->user = "root"; + config->database = "sqlpp_mysql"; + config->debug = true; + try + { + mysql::connection db(config); + } + catch (const std::exception& e) + { + std::cerr << "For testing, you'll need to create a database sqlpp_mysql for user root (no password)" << std::endl; + std::cerr << e.what() << std::endl; + return 1; + } + catch (...) + { + std::cerr << "Unknown exception during connect" << std::endl; + return 1; + } + + try + { + using days_type = std::chrono::duration>; + + mysql::connection db(config); + db.execute(R"(DROP TABLE IF EXISTS tab_date_time)"); + db.execute(R"(CREATE TABLE tab_date_time ( + col_day_point date, + col_time_point datetime(3), + col_date_time_point datetime DEFAULT CURRENT_TIMESTAMP + ))"); + + const auto tab = TabDateTime{}; + db(insert_into(tab).default_values()); + for (const auto& row : db(select(all_of(tab)).from(tab).unconditionally())) + { + require_equal(__LINE__, row.colDayPoint.is_null(), true); + require_equal(__LINE__, row.colDayPoint.value(), ::sqlpp::chrono::day_point{}); + require_equal(__LINE__, row.colTimePoint.is_null(), true); + require_equal(__LINE__, row.colTimePoint.value(), ::sqlpp::chrono::microsecond_point{}); + require_equal(__LINE__, std::chrono::time_point_cast(row.colDateTimePoint.value()), std::chrono::time_point_cast(std::chrono::system_clock::now())); + } + + auto statement = db.prepare(select(tab.colDateTimePoint).from(tab).unconditionally()); + for (const auto& row : db(statement)) + { + require_equal(__LINE__, row.colDateTimePoint.is_null(), false); + require_equal(__LINE__, std::chrono::time_point_cast(row.colDateTimePoint.value()), std::chrono::time_point_cast(std::chrono::system_clock::now())); + } + + db(update(tab).set(tab.colDayPoint = today, tab.colTimePoint = now).unconditionally()); + + for (const auto& row : db(select(all_of(tab)).from(tab).unconditionally())) + { + require_equal(__LINE__, row.colDayPoint.value(), today); + require_equal(__LINE__, row.colTimePoint.value(), now); + } + + db(update(tab).set(tab.colDayPoint = yesterday, tab.colTimePoint = today).unconditionally()); + + for (const auto& row : db(select(all_of(tab)).from(tab).unconditionally())) + { + require_equal(__LINE__, row.colDayPoint.value(), yesterday); + require_equal(__LINE__, row.colTimePoint.value(), today); + } + + auto x = update(tab) + .set(tab.colDayPoint = parameter(tab.colDayPoint), tab.colTimePoint = parameter(tab.colTimePoint)) + .unconditionally(); + + auto prepared_update = db.prepare( + update(tab) + .set(tab.colDayPoint = parameter(tab.colDayPoint), tab.colTimePoint = parameter(tab.colTimePoint)) + .unconditionally()); + prepared_update.params.colDayPoint = today; + prepared_update.params.colTimePoint = now; + std::cout << "---- running prepared update ----" << std::endl; + db(prepared_update); + std::cout << "---- finished prepared update ----" << std::endl; + for (const auto& row : db(select(all_of(tab)).from(tab).unconditionally())) + { + require_equal(__LINE__, row.colDayPoint.value(), today); + require_equal(__LINE__, row.colTimePoint.value(), now); + } + } + catch (const std::exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + return 1; + } + catch (...) + { + std::cerr << "Unkown exception" << std::endl; + } +} diff --git a/tests/mysql/usage/DynamicSelectTest.cpp b/tests/mysql/usage/DynamicSelectTest.cpp new file mode 100644 index 00000000..6253c5e5 --- /dev/null +++ b/tests/mysql/usage/DynamicSelectTest.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2013 - 2016, 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. + */ + +#include "TabSample.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +const auto library_raii = sqlpp::mysql::scoped_library_initializer_t{}; + +namespace mysql = sqlpp::mysql; +int main() +{ + auto config = std::make_shared(); + config->user = "root"; + config->database = "sqlpp_mysql"; + config->debug = true; + try + { + mysql::connection db(config); + } + catch (const sqlpp::exception& e) + { + std::cerr << "For testing, you'll need to create a database sqlpp_mysql for user root (no password)" << std::endl; + std::cerr << e.what() << std::endl; + return 1; + } + + try + { + mysql::connection db(config); + db.execute(R"(DROP TABLE IF EXISTS tab_sample)"); + db.execute(R"(CREATE TABLE tab_sample ( + alpha bigint(20) DEFAULT NULL, + beta varchar(255) DEFAULT NULL, + gamma bool DEFAULT NULL + ))"); + + const auto tab = TabSample{}; + db(insert_into(tab).set(tab.gamma = true)); + auto i = insert_into(tab).columns(tab.beta, tab.gamma); + i.values.add(tab.beta = "rhabarbertorte", tab.gamma = false); + i.values.add(tab.beta = "cheesecake", tab.gamma = false); + i.values.add(tab.beta = "kaesekuchen", tab.gamma = true); + db(i); + + auto s = dynamic_select(db).dynamic_columns(tab.alpha).from(tab).unconditionally(); + s.selected_columns.add(tab.beta); + + for (const auto& row : db(s)) + { + std::cerr << "row.alpha: " << row.alpha << ", row.beta: " << row.at("beta") << std::endl; + }; + } + catch (const std::exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + return 1; + } +} diff --git a/tests/mysql/usage/JsonTest.cpp b/tests/mysql/usage/JsonTest.cpp new file mode 100644 index 00000000..ad1b4136 --- /dev/null +++ b/tests/mysql/usage/JsonTest.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2019 - 2019, 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. + */ + +#include +#include + +// JSON support only in MYSQL 5.7.8 and later +#if !USE_MARIADB && (LIBMYSQL_VERSION_ID < 50708) +int main() +{ + std::cerr << "Warning: not testing Json, because the MySQL version id is less than 50708" << std::endl; +} +#else +// JSON support only in MariaDB 10.2.7 and later +#if USE_MARIADB && (MARIADB_VERSION_ID < 100207) +int main() +{ + std::cerr << "Warning: not testing Json, because the MariaDB version id is less than 100207" << std::endl; +} +#else + +#include "TabJson.h" +#include +#include + +namespace test +{ + SQLPP_ALIAS_PROVIDER(value) +} + +namespace mysql = sqlpp::mysql; +int main() +{ + mysql::global_library_init(); + + auto config = std::make_shared(); + config->user = "root"; + config->database = "sqlpp_mysql"; + config->debug = true; + try + { + mysql::connection db(config); + } + catch (const sqlpp::exception& e) + { + std::cerr << "For testing, you'll need to create a database sqlpp_mysql for user root (no password)" << std::endl; + std::cerr << e.what() << std::endl; + return 1; + } + try + { + mysql::connection db(config); + db.execute(R"(DROP TABLE IF EXISTS tab_json)"); + db.execute(R"(CREATE TABLE tab_json ( + data JSON NOT NULL + ))"); + + const auto tab = test::TabJson{}; + db(insert_into(tab).set(tab.data = R"--({"key" : "value"})--")); + + const auto query = + select(sqlpp::verbatim(R"--(JSON_UNQUOTE(JSON_EXTRACT(data, "$.key")))--").as(test::value)) + .from(tab) + .unconditionally(); + + auto result = db(query); + if (result.empty()) + throw std::runtime_error{"selection result is empty"}; + + const std::string value = result.front().value; + + if (value != "value") + throw std::runtime_error{std::string{"unexpected value: "} + value}; + } + catch (const std::exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + return 1; + } +} +#endif +#endif diff --git a/tests/mysql/usage/MoveConstructorTest.cpp b/tests/mysql/usage/MoveConstructorTest.cpp new file mode 100644 index 00000000..4e47dee7 --- /dev/null +++ b/tests/mysql/usage/MoveConstructorTest.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2013 - 2015, 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. + */ + +#include "TabSample.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace mysql = sqlpp::mysql; +int main() +{ + mysql::global_library_init(); + + auto config = std::make_shared(); + config->user = "root"; + config->database = "sqlpp_mysql"; + config->debug = true; + try + { + mysql::connection db(config); + } + catch (const sqlpp::exception& e) + { + std::cerr << "For testing, you'll need to create a database sqlpp_mysql for user root (no password)" << std::endl; + std::cerr << e.what() << std::endl; + return 1; + } + try + { + std::vector connections; + connections.emplace_back(sqlpp::mysql::connection(config)); + + connections.at(0).execute(R"(DROP TABLE IF EXISTS tab_sample)"); + connections.at(0).execute(R"(CREATE TABLE tab_sample ( + alpha bigint(20) DEFAULT NULL, + beta varchar(255) DEFAULT NULL, + gamma bool DEFAULT NULL + ))"); + + connections.at(0).start_transaction(); + auto db = std::move(connections.at(0)); + assert(db.is_transaction_active()); + const auto tab = TabSample{}; + db(insert_into(tab).set(tab.gamma = true)); + auto i = insert_into(tab).columns(tab.beta, tab.gamma); + i.values.add(tab.beta = "rhabarbertorte", tab.gamma = false); + i.values.add(tab.beta = "cheesecake", tab.gamma = false); + i.values.add(tab.beta = "kaesekuchen", tab.gamma = true); + db(i); + + auto s = dynamic_select(db).dynamic_columns(tab.alpha).from(tab).unconditionally(); + s.selected_columns.add(tab.beta); + + for (const auto& row : db(s)) + { + std::cerr << "row.alpha: " << row.alpha << ", row.beta: " << row.at("beta") << std::endl; + }; + db.commit_transaction(); + } + catch (const std::exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + return 1; + } +} diff --git a/tests/mysql/usage/PreparedTest.cpp b/tests/mysql/usage/PreparedTest.cpp new file mode 100644 index 00000000..3a484b49 --- /dev/null +++ b/tests/mysql/usage/PreparedTest.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2013 - 2016, 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. + */ + +#include "TabSample.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +const auto library_raii = sqlpp::mysql::scoped_library_initializer_t{0, nullptr, nullptr}; + +SQLPP_ALIAS_PROVIDER(left) + +namespace sql = sqlpp::mysql; +const auto tab = TabSample{}; + +void testPreparedStatementResult (sql::connection& db) +{ + auto preparedSelectAll = db.prepare(sqlpp::select(count(tab.alpha)).from(tab).unconditionally()); + auto preparedUpdateAll = db.prepare(sqlpp::update(tab).set(tab.gamma = false).unconditionally()); + + uint32_t count = 0; + { + // explicit result scope + // if results are released update should execute without exception + auto result = db(preparedSelectAll); + count = result.front().count; + } + + db(preparedUpdateAll); +} + +int main() +{ + auto config = std::make_shared(); + config->user = "root"; + config->database = "sqlpp_mysql"; + config->debug = true; + try + { + sql::connection db(config); + } + catch (const sqlpp::exception& e) + { + std::cerr << "For testing, you'll need to create a database sqlpp_mysql for user root (no password)" << std::endl; + std::cerr << e.what() << std::endl; + return 1; + } + try + { + sql::connection db(config); + db.execute(R"(DROP TABLE IF EXISTS tab_sample)"); + db.execute(R"(CREATE TABLE tab_sample ( + alpha bigint(20) AUTO_INCREMENT, + beta varchar(255) DEFAULT NULL, + gamma bool DEFAULT NULL, + PRIMARY KEY (alpha) + ))"); + db.execute(R"(DROP TABLE IF EXISTS tab_foo)"); + db.execute(R"(CREATE TABLE tab_foo ( + omega bigint(20) DEFAULT NULL + ))"); + + testPreparedStatementResult(db); + } + catch (const std::exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + return 1; + } +} diff --git a/tests/mysql/usage/SampleTest.cpp b/tests/mysql/usage/SampleTest.cpp new file mode 100644 index 00000000..72e8dd3d --- /dev/null +++ b/tests/mysql/usage/SampleTest.cpp @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2013 - 2016, 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. + */ + +#include "TabSample.h" +#include +#include + +#include +#include +#include + +SQLPP_ALIAS_PROVIDER(left) +SQLPP_ALIAS_PROVIDER(right) + +namespace mysql = sqlpp::mysql; +int main() +{ + mysql::global_library_init(); + + auto config = std::make_shared(); + config->user = "root"; + config->database = "sqlpp_mysql"; + config->debug = true; + try + { + mysql::connection db(config); + } + catch (const sqlpp::exception& e) + { + std::cerr << "For testing, you'll need to create a database sqlpp_mysql for user root (no password)" << std::endl; + std::cerr << e.what() << std::endl; + return 1; + } + try + { + mysql::connection db(config); + db.execute(R"(DROP TABLE IF EXISTS tab_sample)"); + db.execute(R"(CREATE TABLE tab_sample ( + alpha bigint(20) AUTO_INCREMENT, + beta varchar(255) DEFAULT NULL, + gamma bool DEFAULT NULL, + PRIMARY KEY (alpha) + ))"); + db.execute(R"(DROP TABLE IF EXISTS tab_foo)"); + db.execute(R"(CREATE TABLE tab_foo ( + omega bigint(20) DEFAULT NULL + ))"); + + assert(not db(select(sqlpp::value(false).as(sqlpp::alias::a))).front().a); + + const auto tab = TabSample{}; + // clear the table + db(remove_from(tab).unconditionally()); + + // Several ways of ensuring that tab is empty + assert(not db(select(exists(select(tab.alpha).from(tab).unconditionally()))) + .front() + .exists); // this is probably the fastest + assert(not db(select(count(tab.alpha)).from(tab).unconditionally()).front().count); + assert(db(select(tab.alpha).from(tab).unconditionally()).empty()); + + // explicit all_of(tab) + std::cerr << __FILE__ << ": " << __LINE__ << std::endl; + select(all_of(tab)).from(tab); + std::cerr << __FILE__ << ": " << __LINE__ << std::endl; + db(select(all_of(tab)).from(tab).unconditionally()); + std::cerr << __FILE__ << ": " << __LINE__ << std::endl; + for (const auto& row : db(select(all_of(tab)).from(tab).unconditionally())) + { + std::cerr << __FILE__ << ": " << __LINE__ << std::endl; + std::cerr << "row.alpha: " << row.alpha << ", row.beta: " << row.beta << ", row.gamma: " << row.gamma + << std::endl; + }; + // insert + db(insert_into(tab).default_values()); + const auto x = select(all_of(tab)).from(tab).unconditionally(); + const auto y = db.prepare(x); + for (const auto& row : db(db.prepare(select(all_of(tab)).from(tab).unconditionally()))) + { + std::cerr << "alpha: " << row.alpha.is_null() << std::endl; + std::cerr << "beta: " << row.beta.is_null() << std::endl; + std::cerr << "gamma: " << row.gamma.is_null() << std::endl; + } + db(insert_into(tab).set(tab.beta = "kaesekuchen", tab.gamma = true)); + db(insert_into(tab).default_values()); + db(insert_into(tab).set(tab.beta = "", tab.gamma = true)); + + // update + db(update(tab).set(tab.gamma = false).where(tab.alpha.in(sqlpp::value_list(std::vector{1, 2, 3, 4})))); + db(update(tab).set(tab.gamma = true).where(tab.alpha.in(1))); + + // remove + db(remove_from(tab).where(tab.alpha == tab.alpha + 3)); + + std::cerr << "+++++++++++++++++++++++++++" << std::endl; + for (const auto& row : db(select(all_of(tab)).from(tab).unconditionally())) + { + std::cerr << __LINE__ << " row.beta: " << row.beta << std::endl; + } + std::cerr << "+++++++++++++++++++++++++++" << std::endl; + decltype(db(select(all_of(tab)).from(tab).unconditionally())) result; + result = db(select(all_of(tab)).from(tab).unconditionally()); + std::cerr << "Accessing a field directly from the result (using the current row): " << result.begin()->alpha + << std::endl; + std::cerr << "Can do that again, no problem: " << result.begin()->alpha << std::endl; + + auto tx = start_transaction(db); + if (const auto& row = + *db(select(all_of(tab), select(max(tab.alpha)).from(tab)).from(tab).unconditionally()).begin()) + { + int a = row.alpha; + int m = row.max; + std::cerr << __LINE__ << " row.alpha: " << a << ", row.max: " << m << std::endl; + } + tx.commit(); + + TabFoo foo; + for (const auto& row : db(select(tab.alpha).from(tab.join(foo).on(tab.alpha == foo.omega)).unconditionally())) + { + std::cerr << row.alpha << std::endl; + } + + for (const auto& row : + db(select(tab.alpha).from(tab.left_outer_join(foo).on(tab.alpha == foo.omega)).unconditionally())) + { + std::cerr << row.alpha << std::endl; + } + + auto ps = db.prepare(select(all_of(tab)) + .from(tab) + .where(tab.alpha != parameter(tab.alpha) and tab.beta != parameter(tab.beta) and + tab.gamma != parameter(tab.gamma))); + ps.params.alpha = 7; + ps.params.beta = "wurzelbrunft"; + ps.params.gamma = true; + for (const auto& row : db(ps)) + { + std::cerr << "bound result: alpha: " << row.alpha << std::endl; + std::cerr << "bound result: beta: " << row.beta << std::endl; + std::cerr << "bound result: gamma: " << row.gamma << std::endl; + } + + std::cerr << "--------" << std::endl; + ps.params.gamma = "false"; + for (const auto& row : db(ps)) + { + std::cerr << "bound result: alpha: " << row.alpha << std::endl; + std::cerr << "bound result: beta: " << row.beta << std::endl; + std::cerr << "bound result: gamma: " << row.gamma << std::endl; + } + + std::cerr << "--------" << std::endl; + ps.params.beta = "kaesekuchen"; + for (const auto& row : db(ps)) + { + std::cerr << "bound result: alpha: " << row.alpha << std::endl; + std::cerr << "bound result: beta: " << row.beta << std::endl; + std::cerr << "bound result: gamma: " << row.gamma << std::endl; + } + + auto pi = db.prepare(insert_into(tab).set(tab.beta = parameter(tab.beta), tab.gamma = true)); + pi.params.beta = "prepared cake"; + std::cerr << "Inserted: " << db(pi) << std::endl; + + auto pu = db.prepare(update(tab).set(tab.gamma = parameter(tab.gamma)).where(tab.beta == "prepared cake")); + pu.params.gamma = false; + std::cerr << "Updated: " << db(pu) << std::endl; + + auto pr = db.prepare(remove_from(tab).where(tab.beta != parameter(tab.beta))); + pr.params.beta = "prepared cake"; + std::cerr << "Deleted lines: " << db(pr) << std::endl; + + for (const auto& row : db(select(case_when(tab.gamma).then(tab.alpha).else_(foo.omega).as(tab.alpha)) + .from(tab.cross_join(foo)) + .unconditionally())) + { + std::cerr << row.alpha << std::endl; + } + } + catch (const std::exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + return 1; + } +} diff --git a/tests/mysql/usage/SelectTest.cpp b/tests/mysql/usage/SelectTest.cpp new file mode 100644 index 00000000..65cb994e --- /dev/null +++ b/tests/mysql/usage/SelectTest.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2013 - 2016, 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. + */ + +#include "TabSample.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +const auto library_raii = sqlpp::mysql::scoped_library_initializer_t{0, nullptr, nullptr}; + +SQLPP_ALIAS_PROVIDER(left) + +namespace sql = sqlpp::mysql; +const auto tab = TabSample{}; + +void testSelectAll(sql::connection& db, int expectedRowCount) +{ + std::cerr << "--------------------------------------" << std::endl; + int i = 0; + for (const auto& row : db(sqlpp::select(all_of(tab)).from(tab).unconditionally())) + { + ++i; + std::cerr << ">>> row.alpha: " << row.alpha << ", row.beta: " << row.beta << ", row.gamma: " << row.gamma + << std::endl; + assert(i == row.alpha); + }; + assert(i == expectedRowCount); + + auto preparedSelectAll = db.prepare(sqlpp::select(all_of(tab)).from(tab).unconditionally()); + std::cerr << "--------------------------------------" << std::endl; + i = 0; + for (const auto& row : db(preparedSelectAll)) + { + ++i; + std::cerr << ">>> row.alpha: " << row.alpha << ", row.beta: " << row.beta << ", row.gamma: " << row.gamma + << std::endl; + assert(i == row.alpha); + }; + assert(i == expectedRowCount); + + // Try running the same prepared statement again + std::cerr << "--------------------------------------" << std::endl; + i = 0; + for (const auto& row : db(preparedSelectAll)) + { + ++i; + std::cerr << ">>> row.alpha: " << row.alpha << ", row.beta: " << row.beta << ", row.gamma: " << row.gamma + << std::endl; + assert(i == row.alpha); + }; + assert(i == expectedRowCount); + std::cerr << "--------------------------------------" << std::endl; +} + +int main() +{ + auto config = std::make_shared(); + config->user = "root"; + config->database = "sqlpp_mysql"; + config->debug = true; + try + { + sql::connection db(config); + } + catch (const sqlpp::exception& e) + { + std::cerr << "For testing, you'll need to create a database sqlpp_mysql for user root (no password)" << std::endl; + std::cerr << e.what() << std::endl; + return 1; + } + try + { + sql::connection db(config); + db.execute(R"(DROP TABLE IF EXISTS tab_sample)"); + db.execute(R"(CREATE TABLE tab_sample ( + alpha bigint(20) AUTO_INCREMENT, + beta varchar(255) DEFAULT NULL, + gamma bool DEFAULT NULL, + PRIMARY KEY (alpha) + ))"); + db.execute(R"(DROP TABLE IF EXISTS tab_foo)"); + db.execute(R"(CREATE TABLE tab_foo ( + omega bigint(20) DEFAULT NULL + ))"); + + testSelectAll(db, 0); + db(insert_into(tab).default_values()); + testSelectAll(db, 1); + db(insert_into(tab).set(tab.gamma = true, tab.beta = "cheesecake")); + testSelectAll(db, 2); + db(insert_into(tab).set(tab.gamma = true, tab.beta = "cheesecake")); + testSelectAll(db, 3); + + // test functions and operators + db(select(all_of(tab)).from(tab).where(tab.alpha.is_null())); + db(select(all_of(tab)).from(tab).where(tab.alpha.is_not_null())); + db(select(all_of(tab)).from(tab).where(tab.alpha.in(1, 2, 3))); + db(select(all_of(tab)).from(tab).where(tab.alpha.in(sqlpp::value_list(std::vector{1, 2, 3, 4})))); + db(select(all_of(tab)).from(tab).where(tab.alpha.not_in(1, 2, 3))); + db(select(all_of(tab)).from(tab).where(tab.alpha.not_in(sqlpp::value_list(std::vector{1, 2, 3, 4})))); + db(select(count(tab.alpha)).from(tab).unconditionally()); + db(select(avg(tab.alpha)).from(tab).unconditionally()); + db(select(max(tab.alpha)).from(tab).unconditionally()); + db(select(min(tab.alpha)).from(tab).unconditionally()); + db(select(exists(select(tab.alpha).from(tab).where(tab.alpha > 7))).from(tab).unconditionally()); + db(select(all_of(tab)).from(tab).where(tab.alpha == any(select(tab.alpha).from(tab).where(tab.alpha < 3)))); + db(select(all_of(tab)).from(tab).where(tab.alpha == some(select(tab.alpha).from(tab).where(tab.alpha < 3)))); + + db(select(all_of(tab)).from(tab).where(tab.alpha + tab.alpha > 3)); + db(select(all_of(tab)).from(tab).where((tab.beta + tab.beta) == "")); + db(select(all_of(tab)).from(tab).where((tab.beta + tab.beta).like("%'\"%"))); + + // insert + db(insert_into(tab).set(tab.gamma = true)); + + // update + db(update(tab).set(tab.gamma = false).where(tab.alpha.in(1))); + db(update(tab).set(tab.gamma = false).where(tab.alpha.in(sqlpp::value_list(std::vector{1, 2, 3, 4})))); + + // remove + db(remove_from(tab).where(tab.alpha == tab.alpha + 3)); + + auto result = db(select(all_of(tab)).from(tab).unconditionally()); + std::cerr << "Accessing a field directly from the result (using the current row): " << result.begin()->alpha + << std::endl; + std::cerr << "Can do that again, no problem: " << result.begin()->alpha << std::endl; + + auto tx = start_transaction(db); + if (const auto& row = + *db(select(all_of(tab), select(max(tab.alpha)).from(tab)).from(tab).unconditionally()).begin()) + { + int a = row.alpha; + int m = row.max; + std::cerr << "-----------------------------" << a << ", " << m << std::endl; + } + tx.commit(); + } + catch (const std::exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + return 1; + } +} diff --git a/tests/mysql/usage/TabJson.h b/tests/mysql/usage/TabJson.h new file mode 100644 index 00000000..40f80ead --- /dev/null +++ b/tests/mysql/usage/TabJson.h @@ -0,0 +1,48 @@ +// generated by ../../sqlpp11/scripts/ddl2cpp ../tests/TabJson.sql ../tests/TabJson test +#ifndef TEST_TABJSON_H +#define TEST_TABJSON_H + +#include +#include +#include + +namespace test +{ + namespace TabJson_ + { + struct Data + { + struct _alias_t + { + static constexpr const char _literal[] = "data"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T data; + T& operator()() { return data; } + const T& operator()() const { return data; } + }; + }; + using _traits = sqlpp::make_traits; + }; + } // namespace TabJson_ + + struct TabJson: sqlpp::table_t + { + struct _alias_t + { + static constexpr const char _literal[] = "tab_json"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T tabJson; + T& operator()() { return tabJson; } + const T& operator()() const { return tabJson; } + }; + }; + }; +} // namespace test +#endif diff --git a/tests/mysql/usage/TabJson.sql b/tests/mysql/usage/TabJson.sql new file mode 100644 index 00000000..3c28342d --- /dev/null +++ b/tests/mysql/usage/TabJson.sql @@ -0,0 +1,3 @@ +CREATE TABLE tab_json ( + data JSON NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/tests/mysql/usage/TabSample.h b/tests/mysql/usage/TabSample.h new file mode 100644 index 00000000..8bb8114a --- /dev/null +++ b/tests/mysql/usage/TabSample.h @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2013 - 2015, 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_TAB_SAMPLE_H +#define SQLPP_TAB_SAMPLE_H + +#include +#include +#include + +namespace TabFoo_ +{ + struct Omega + { + struct _alias_t + { + static constexpr const char _literal[] = "omega"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T omega; + T& operator()() + { + return omega; + } + const T& operator()() const + { + return omega; + } + }; + }; + using _traits = ::sqlpp::make_traits<::sqlpp::bigint>; + }; +} + +struct TabFoo : sqlpp::table_t +{ + using _value_type = sqlpp::no_value_t; + struct _alias_t + { + static constexpr const char _literal[] = "tab_foo"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T tabFoo; + T& operator()() + { + return tabFoo; + } + const T& operator()() const + { + return tabFoo; + } + }; + }; +}; + +namespace TabSample_ +{ + struct Alpha + { + struct _alias_t + { + static constexpr const char _literal[] = "alpha"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T alpha; + T& operator()() + { + return alpha; + } + const T& operator()() const + { + return alpha; + } + }; + }; + using _traits = ::sqlpp::make_traits<::sqlpp::bigint, ::sqlpp::tag::must_not_insert, ::sqlpp::tag::must_not_update>; + }; + + struct Beta + { + struct _alias_t + { + static constexpr const char _literal[] = "beta"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T beta; + T& operator()() + { + return beta; + } + const T& operator()() const + { + return beta; + } + }; + }; + using _traits = ::sqlpp::make_traits<::sqlpp::varchar, ::sqlpp::tag::must_not_update, ::sqlpp::tag::can_be_null>; + }; + + struct Gamma + { + struct _alias_t + { + static constexpr const char _literal[] = "gamma"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T gamma; + T& operator()() + { + return gamma; + } + const T& operator()() const + { + return gamma; + } + }; + }; + using _traits = ::sqlpp::make_traits<::sqlpp::boolean>; + }; +} + +struct TabSample : sqlpp::table_t +{ + using _value_type = sqlpp::no_value_t; + struct _alias_t + { + static constexpr const char _literal[] = "tab_sample"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T tabSample; + T& operator()() + { + return tabSample; + } + const T& operator()() const + { + return tabSample; + } + }; + }; +}; + +namespace TabDateTime_ +{ + struct ColDayPoint + { + struct _alias_t + { + static constexpr const char _literal[] = "col_day_point"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T colDayPoint; + T& operator()() + { + return colDayPoint; + } + const T& operator()() const + { + return colDayPoint; + } + }; + }; + using _traits = sqlpp::make_traits; + }; + struct ColTimePoint + { + struct _alias_t + { + static constexpr const char _literal[] = "col_time_point"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T colTimePoint; + T& operator()() + { + return colTimePoint; + } + const T& operator()() const + { + return colTimePoint; + } + }; + }; + using _traits = sqlpp::make_traits; + }; + struct ColDateTimePoint + { + struct _alias_t + { + static constexpr const char _literal[] = "col_date_time_point"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T colDateTimePoint; + T& operator()() { return colDateTimePoint; } + const T& operator()() const { return colDateTimePoint; } + }; + }; + using _traits = sqlpp::make_traits; + }; +} + +struct TabDateTime : sqlpp::table_t +{ + struct _alias_t + { + static constexpr const char _literal[] = "tab_date_time"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T tabDateTime; + T& operator()() + { + return tabDateTime; + } + const T& operator()() const + { + return tabDateTime; + } + }; + }; +}; + +#endif diff --git a/tests/mysql/usage/TabSample.sql b/tests/mysql/usage/TabSample.sql new file mode 100644 index 00000000..380cf58e --- /dev/null +++ b/tests/mysql/usage/TabSample.sql @@ -0,0 +1,5 @@ +CREATE TABLE tab_sample ( + alpha bigint(20) DEFAULT NULL AUTO_INCREMENT, + beta tinyint(1) DEFAULT NULL, + gamma varchar(255) DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=latin1; diff --git a/tests/mysql/usage/TruncatedTest.cpp b/tests/mysql/usage/TruncatedTest.cpp new file mode 100644 index 00000000..cb57f10c --- /dev/null +++ b/tests/mysql/usage/TruncatedTest.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2013 - 2016, 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. + */ + +#include "TabSample.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +const auto library_raii = sqlpp::mysql::scoped_library_initializer_t{0, nullptr, nullptr}; + +namespace sql = sqlpp::mysql; +const auto tab = TabSample{}; + +int main() +{ + auto config = std::make_shared(); + config->user = "root"; + config->database = "sqlpp_mysql"; + config->debug = true; + config->charset = "utf8"; + try + { + sql::connection db(config); + } + catch (const sqlpp::exception& e) + { + std::cerr << "For testing, you'll need to create a database sqlpp_mysql for user root (no password)" << std::endl; + std::cerr << e.what() << std::endl; + return 1; + } + try + { + sql::connection db(config); + db.execute(R"(DROP TABLE IF EXISTS tab_sample)"); + db.execute(R"(CREATE TABLE tab_sample ( + alpha bigint(20) AUTO_INCREMENT, + beta varchar(255) DEFAULT NULL, + gamma bool DEFAULT NULL, + PRIMARY KEY (alpha) + ))"); + db.execute(R"(DROP TABLE IF EXISTS tab_foo)"); + db.execute(R"(CREATE TABLE tab_foo ( + omega bigint(20) DEFAULT NULL + ))"); + + db(insert_into(tab).set(tab.gamma = true, tab.beta = "cheese")); + db(insert_into(tab).set(tab.gamma = true, tab.beta = "cheesecake")); + + { + for (const auto& row : db(db.prepare(sqlpp::select(all_of(tab)).from(tab).unconditionally()))) + { + std::cerr << ">>> row.alpha: " << row.alpha << ", row.beta: " << row.beta << ", row.gamma: " << row.gamma << std::endl; + } + } + + { + auto result = db(db.prepare(sqlpp::select(all_of(tab)).from(tab).where(tab.alpha == 1).limit(1u))); + auto& row = result.front(); + + std::cerr << ">>> row.alpha: " << row.alpha << ", row.beta: " << row.beta << ", row.gamma: " << row.gamma << std::endl; + assert(row.beta == "cheese"); + } + + { + auto result = db(db.prepare(sqlpp::select(all_of(tab)).from(tab).where(tab.alpha == 2).limit(1u))); + auto& row = result.front(); + + std::cerr << ">>> row.alpha: " << row.alpha << ", row.beta: " << row.beta << ", row.gamma: " << row.gamma << std::endl; + assert(row.beta == "cheesecake"); + } + } + catch (const std::exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + return 1; + } +} diff --git a/tests/mysql/usage/UnionTest.cpp b/tests/mysql/usage/UnionTest.cpp new file mode 100644 index 00000000..5e58c7f1 --- /dev/null +++ b/tests/mysql/usage/UnionTest.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2013 - 2016, 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. + */ + +#include "TabSample.h" +#include +#include + +#include + +const auto library_raii = sqlpp::mysql::scoped_library_initializer_t{0, nullptr, nullptr}; + +namespace sql = sqlpp::mysql; +const auto tab = TabSample{}; + +int main() +{ + auto config = std::make_shared(); + config->user = "root"; + config->database = "sqlpp_mysql"; + config->debug = true; + try + { + sql::connection db(config); + } + catch (const sqlpp::exception& e) + { + std::cerr << "For testing, you'll need to create a database sqlpp_mysql for user root (no password)" << std::endl; + std::cerr << e.what() << std::endl; + return 1; + } + try + { + sql::connection db(config); + db.execute(R"(DROP TABLE IF EXISTS tab_sample)"); + db.execute(R"(CREATE TABLE tab_sample ( + alpha bigint(20) AUTO_INCREMENT, + beta bool DEFAULT NULL, + gamma varchar(255) DEFAULT NULL, + PRIMARY KEY (alpha) + ))"); + + auto u = select(all_of(tab)).from(tab).unconditionally().union_all(select(all_of(tab)).from(tab).unconditionally()); + + for (const auto& row : db(u)) + { + std::cout << row.alpha << row.beta << row.gamma << std::endl; + } + + for (const auto& row : db(u.union_distinct(select(all_of(tab)).from(tab).unconditionally()))) + { + std::cout << row.alpha << row.beta << row.gamma << std::endl; + } + } + catch (const std::exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + return 1; + } +}