diff --git a/connector_api/connection.h b/connector_api/connection.h index d3455fde..cb594b55 100644 --- a/connector_api/connection.h +++ b/connector_api/connection.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2013-2015, Roland Bock + * Copyright (c) 2023, Vesselin Atanasov * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, @@ -37,6 +38,44 @@ namespace sqlpp { namespace database { + // Connection configuration that is used to create new database connections + struct connection_config + { + // Put the configuration properties here, e.g. + // + // std::string host; + // unsigned port; + // std::string username; + // std::string password; + // std::string db_name; + }; + + // The connection handle is a low-level representation of a database connection. + // Pre-connected handles are stored in the connection pool and are used to create + // full connection objects on request. + class connection_handle + { + public: + // Connection handles can be created from connection configurations + connection_handle(const std::shared_ptr& config); + + // Connection handles cannot be copied + connection_handle(const connection_handle&) = delete; + connection_handle& operator=(const connection_handle&) = delete; + + // Connection handles can be moved + connection_handle(connection_handle&&); + connection_handle& operator=(connection_handle&&); + + // Used by the connection pool to check if the connection handle is still + // connected to the database server + bool check_connection(); + + // Optional method that returns a native (low-level) database handle. + // Used by the test code to test the connection pool + native_db_handle native_handle(); + }; + // The context is not a requirement, but if the database requires // any deviations from the SQL standard, you should use your own // context in order to specialize the behaviour, see also interpreter.h @@ -48,9 +87,25 @@ namespace sqlpp std::string escape(std::string arg); }; - class connection : public sqlpp::connection // this inheritance helps with ADL for dynamic_select, for instance + // The base database-specific connection class. Non-pooled and pooled connection classes derive from it + class connection_base : public sqlpp::connection // this inheritance helps with ADL for dynamic_select, for instance { public: + // Base configuration + using _connection_base_t = connection_base; + + // Type of configuration instances + using _config_t = connection_config; + + // Shared pointer wrapping a configuration instance + using _config_ptr_t = std::shared_ptr; + + // Type of connection handles + using _handle_t = connection_handle; + + // Unique pointer wrapping a connection handle + using _handle_ptr_t = std::unique_ptr<_handle_t>; + using _traits = ::sqlpp::make_traits< ::sqlpp::no_value_t, ::sqlpp::tag::enforce_null_result_treatment // If that is what you really want, leave it out otherwise @@ -63,13 +118,6 @@ namespace sqlpp // serializer and interpreter are typically the same for string based connectors // the types are required for dynamic statement components, see sqlpp11/interpretable.h - connection(...); - ~connection(); - connection(const connection&) = delete; - connection(connection&&) = delete; - connection& operator=(const connection&) = delete; - connection& operator=(connection&&) = delete; - //! "direct" select template << bind_result_t >> @@ -153,9 +201,26 @@ namespace sqlpp //! 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; + + protected: + // Low-level connection handle + _handle_ptr_t _handle; + + // The constructors are private because the base class instances are never created directly, + // The constructors are called from the constructors of the derived classes + connection_base() = default; + connection_base(_handle_ptr_t&& handle) : _handle{std::move(handle)} + { + } }; - } -} + + // Normal non-pooled connections. + using connection = sqlpp::normal_connection; + + // Pooled connections that are created by the thread pool + using pooled_connection = sqlpp::pooled_connection; + } // namespace database +} // namespace sqlpp #include diff --git a/connector_api/connection_pool.h b/connector_api/connection_pool.h new file mode 100644 index 00000000..cbf7120a --- /dev/null +++ b/connector_api/connection_pool.h @@ -0,0 +1,39 @@ +#pragma once + +/* +Copyright (c) 2017 - 2018, Roland Bock +Copyright (c) 2023, Vesselin Atanasov +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. +*/ + +#include +#include + +namespace sqlpp +{ + namespace database + { + using connection_pool = sqlpp::connection_pool; + } // namespace database +} // namespace sqlpp diff --git a/include/sqlpp11/connection.h b/include/sqlpp11/connection.h index b569fd4a..f870fc47 100644 --- a/include/sqlpp11/connection.h +++ b/include/sqlpp11/connection.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2013-2015, Roland Bock + * Copyright (c) 2023, Vesselin Atanasov * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, @@ -27,11 +28,133 @@ #ifndef SQLPP11_CONNECTION_H #define SQLPP11_CONNECTION_H +#include +#include + namespace sqlpp { struct connection { }; + + // Normal (non-pooled) connection + template + class normal_connection : public ConnectionBase + { + public: + using _config_t = typename ConnectionBase::_config_t; + using _config_ptr_t = typename ConnectionBase::_config_ptr_t; + + // Constructors + normal_connection() = default; + normal_connection(const _config_t& config); + normal_connection(const _config_ptr_t& config); + normal_connection(const normal_connection&) = delete; + normal_connection(normal_connection&&) = default; + + // Assigment operators + normal_connection& operator=(const normal_connection&) = delete; + normal_connection& operator=(normal_connection&&) = default; + + // creates a connection handle and connects to database + void connectUsing(const _config_ptr_t& config) noexcept(false); + + private: + using _handle_t = typename ConnectionBase::_handle_t; + }; + + template + normal_connection::normal_connection(const _config_t& config) : + normal_connection{std::make_shared<_config_t>(config)} + { + } + + template + normal_connection::normal_connection(const _config_ptr_t& config) : + ConnectionBase{std::make_unique<_handle_t>(config)} + { + } + + template + void normal_connection::connectUsing(const _config_ptr_t& config) noexcept(false) + { + ConnectionBase::_handle = std::make_unique<_handle_t>(config); + } + + // Forward declaration + template + class connection_pool; + + // Pooled connection + template + class pooled_connection : public ConnectionBase + { + friend class connection_pool::pool_core; + + public: + using _config_ptr_t = typename ConnectionBase::_config_ptr_t; + using _handle_t = typename ConnectionBase::_handle_t; + using _handle_ptr_t = typename ConnectionBase::_handle_ptr_t; + using _pool_core_ptr_t = std::shared_ptr::pool_core>; + + // Copy/Move constructors + pooled_connection(const pooled_connection&) = delete; + pooled_connection(pooled_connection&& other) = default; + ~pooled_connection(); + + // Assigment operators + pooled_connection& operator=(const pooled_connection&) = delete; + pooled_connection& operator=(pooled_connection&& other); + + private: + _pool_core_ptr_t _pool_core; + + // Constructors used by the connection pool + pooled_connection(_handle_ptr_t&& handle, _pool_core_ptr_t pool_core); + pooled_connection(const _config_ptr_t& config, _pool_core_ptr_t pool_core); + + void conn_release(); + }; + + template + pooled_connection::~pooled_connection() + { + conn_release(); + } + + template + pooled_connection& pooled_connection::operator=(pooled_connection&& other) + { + if (this != &other) { + conn_release(); + static_cast(*this) = std::move(static_cast(other)); + _pool_core = std::move(other._pool_core); + } + return *this; + } + + template + pooled_connection::pooled_connection(_handle_ptr_t&& handle, _pool_core_ptr_t pool_core) : + ConnectionBase{std::move(handle)}, + _pool_core{pool_core} + { + } + + template + pooled_connection::pooled_connection(const _config_ptr_t& config, _pool_core_ptr_t pool_core) : + ConnectionBase{std::make_unique<_handle_t>(config)}, + _pool_core{pool_core} + { + } + + template + void pooled_connection::conn_release() + { + if (_pool_core) { + _pool_core->put(ConnectionBase::_handle); + _pool_core = nullptr; + } + } } // namespace sqlpp #endif diff --git a/include/sqlpp11/connection_pool.h b/include/sqlpp11/connection_pool.h new file mode 100644 index 00000000..c583b7a2 --- /dev/null +++ b/include/sqlpp11/connection_pool.h @@ -0,0 +1,151 @@ +#pragma once + +/* +Copyright (c) 2017 - 2018, Roland Bock +Copyright (c) 2023, Vesselin Atanasov +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. +*/ + +#include + +#include + +namespace sqlpp +{ + template + class connection_pool + { + public: + using _config_ptr_t = typename ConnectionBase::_config_ptr_t; + using _handle_ptr_t = typename ConnectionBase::_handle_ptr_t; + using _pooled_connection_t = sqlpp::pooled_connection; + + class pool_core : public std::enable_shared_from_this + { + public: + pool_core(const _config_ptr_t& connection_config, std::size_t capacity); + pool_core() = delete; + pool_core(const pool_core &) = delete; + pool_core(pool_core &&) = delete; + + pool_core& operator=(const pool_core&) = delete; + pool_core& operator=(pool_core&&) = delete; + + _pooled_connection_t get(); + void put(_handle_ptr_t& handle); + // Returns number of connections available in the pool. Only used in tests. + std::size_t available(); + + private: + _config_ptr_t _connection_config; + sqlpp::detail::circular_buffer<_handle_ptr_t> _handles; + std::mutex _mutex; + }; + + connection_pool() = default; + connection_pool(const _config_ptr_t& connection_config, std::size_t capacity); + connection_pool(const connection_pool&) = delete; + connection_pool(connection_pool&&) = default; + + connection_pool& operator=(const connection_pool&) = delete; + connection_pool& operator=(connection_pool&&) = default; + + void initialize(const _config_ptr_t& connection_config, std::size_t capacity); + _pooled_connection_t get(); + // Returns number of connections available in the pool. Only used in tests. + std::size_t available(); + + private: + std::shared_ptr _core; + }; + + template + connection_pool::pool_core::pool_core(const _config_ptr_t& connection_config, std::size_t capacity) : + _connection_config{connection_config}, + _handles{capacity} + { + } + + template + typename connection_pool::_pooled_connection_t connection_pool::pool_core::get() + { + std::unique_lock lock{_mutex}; + if (_handles.empty()) { + lock.unlock(); + return _pooled_connection_t{_connection_config, this->shared_from_this()}; + } + auto handle = std::move(_handles.front()); + _handles.pop_front(); + lock.unlock(); + // If the fetched connection is dead, drop it and create a new one on the fly + return + handle->check_connection() ? + _pooled_connection_t{std::move(handle), this->shared_from_this()} : + _pooled_connection_t{_connection_config, this->shared_from_this()}; + } + + template + void connection_pool::pool_core::put(_handle_ptr_t& handle) + { + std::unique_lock lock{_mutex}; + if (_handles.full ()) { + _handles.set_capacity (_handles.capacity () + 5); + } + _handles.push_back(std::move(handle)); + } + + template + std::size_t connection_pool::pool_core::available() + { + std::unique_lock lock{_mutex}; + return _handles.size(); + } + + template + connection_pool::connection_pool(const _config_ptr_t& connection_config, std::size_t capacity) : + _core{std::make_shared(connection_config, capacity)} + { + } + + template + void connection_pool::initialize(const _config_ptr_t& connection_config, std::size_t capacity) + { + if (_core) { + throw std::runtime_error{"Connection pool already initialized"}; + } + _core = std::make_shared(connection_config, capacity); + } + + template + typename connection_pool::_pooled_connection_t connection_pool::get() + { + return _core->get(); + } + + template + std::size_t connection_pool::available() + { + return _core->available(); + } +} // namespace sqlpp diff --git a/include/sqlpp11/detail/circular_buffer.h b/include/sqlpp11/detail/circular_buffer.h new file mode 100644 index 00000000..4ee00752 --- /dev/null +++ b/include/sqlpp11/detail/circular_buffer.h @@ -0,0 +1,162 @@ +#pragma once + +/* +Copyright (c) 2017 - 2018, Roland Bock +Copyright (c) 2023, Vesselin Atanasov +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. +*/ + +#include +#include +#include + +namespace sqlpp +{ + namespace detail + { + // This class is modelled after boost::circular_buffer + template + class circular_buffer + { + public: + circular_buffer(std::size_t capacity); + std::size_t capacity() const; + void set_capacity(std::size_t capacity); + std::size_t size() const; + bool empty() const; + bool full() const; + T& front(); + void pop_front(); + void push_back(T&& t); + + private: + std::vector _data; + std::size_t _capacity; + std::size_t _size; + std::size_t _head; + std::size_t _tail; + + void increment(std::size_t& pos); + }; + + template + circular_buffer::circular_buffer(std::size_t capacity) : + _data(capacity), + _capacity{capacity}, + _size{0}, + _head{0}, + _tail{0} + { + } + + template + std::size_t circular_buffer::capacity() const + { + return _capacity; + } + + template + void circular_buffer::set_capacity(std::size_t capacity) + { + if (capacity == _capacity) { + return; + } + if (_tail >= _head) { + if (empty () == false) { + std::rotate(_data.begin(), _data.begin()+_tail, _data.end()); + } + _head = (capacity > _size) ? _size : 0; + _tail = 0; + } else { + if (capacity < _head) { + std::rotate(_data.begin(), _data.begin()+_tail, _data.begin()+_head); + _head = (capacity > _size) ? _size : 0; + _tail = 0; + } else if (capacity == _head) { + _head = 0; + } + } + _data.resize(capacity); + _capacity = capacity; + if (_size > capacity) { + _size = capacity; + } + } + + template + std::size_t circular_buffer::size() const + { + return _size; + } + + template + bool circular_buffer::empty() const + { + return _size == 0; + } + + template + bool circular_buffer::full() const + { + return _size == _capacity; + } + + template + T& circular_buffer::front() + { + if (empty()) { + throw std::runtime_error{"circular_buffer::front() called on empty buffer"}; + } + return _data[_tail]; + } + + template + void circular_buffer::pop_front() + { + if (empty()) { + throw std::runtime_error{"circular_buffer::pop_front() called on empty buffer"}; + } + _data[_tail] = {}; + increment(_tail); + --_size; + } + + template + void circular_buffer::push_back(T&& t) + { + if (full()) { + throw std::runtime_error{"circular_buffer::push_back() called on full buffer"}; + } + _data[_head] = std::move(t); + increment(_head); + ++_size; + } + + template + void circular_buffer::increment(std::size_t& pos) + { + pos = (pos + 1) % _capacity; + } + } // namespace detail +} // namespace sqlpp diff --git a/include/sqlpp11/eval.h b/include/sqlpp11/eval.h index 361c7831..65747b0e 100644 --- a/include/sqlpp11/eval.h +++ b/include/sqlpp11/eval.h @@ -51,7 +51,7 @@ namespace sqlpp template ::value, int>::type = 0> - auto eval(Db& db, Expr expr) -> typename eval_t::type + auto eval(Db& db, Expr expr) -> typename eval_t::type { return db(select(expr.as(alias::a))).front().a; } diff --git a/include/sqlpp11/mysql/connection.h b/include/sqlpp11/mysql/connection.h index ada50374..a2a72c34 100644 --- a/include/sqlpp11/mysql/connection.h +++ b/include/sqlpp11/mysql/connection.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2013 - 2017, Roland Bock + * Copyright (c) 2023, Vesselin Atanasov * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, @@ -104,11 +105,12 @@ namespace sqlpp struct connection_handle_t { - std::shared_ptr config; + std::shared_ptr config; std::unique_ptr mysql; - connection_handle_t(const std::shared_ptr& conf) - : config(conf), mysql(mysql_init(nullptr), handle_cleanup) + connection_handle_t(const std::shared_ptr& conf) : + config(conf), + mysql(mysql_init(nullptr), handle_cleanup) { if (not mysql) { @@ -118,43 +120,48 @@ namespace sqlpp if (config->auto_reconnect) { my_bool my_true = true; - if (mysql_options(mysql.get(), MYSQL_OPT_RECONNECT, &my_true)) + if (mysql_options(native_handle(), MYSQL_OPT_RECONNECT, &my_true)) { throw sqlpp::exception("MySQL: could not set option MYSQL_OPT_RECONNECT"); } } - connect(mysql.get(), *config); + connect(native_handle(), *config); } - ~connection_handle_t() noexcept = default; connection_handle_t(const connection_handle_t&) = delete; connection_handle_t(connection_handle_t&&) = default; connection_handle_t& operator=(const connection_handle_t&) = delete; connection_handle_t& operator=(connection_handle_t&&) = default; - bool is_valid() + MYSQL* native_handle() const { - return mysql_ping(mysql.get()) == 0; + return mysql.get(); + } + + bool check_connection() const + { + auto nh = native_handle(); + return nh && (mysql_ping(nh) == 0); } void reconnect() { - connect(mysql.get(), *config); + connect(native_handle(), *config); } }; - inline void execute_statement(detail::connection_handle_t& handle, const std::string& statement) + inline void execute_statement(std::unique_ptr& handle, const std::string& statement) { thread_init(); - if (handle.config->debug) + if (handle->config->debug) std::cerr << "MySQL debug: Executing: '" << statement << "'" << std::endl; - if (mysql_query(handle.mysql.get(), statement.c_str())) + if (mysql_query(handle->native_handle(), statement.c_str())) { throw sqlpp::exception( - "MySQL error: Could not execute MySQL-statement: " + std::string(mysql_error(handle.mysql.get())) + + "MySQL error: Could not execute MySQL-statement: " + std::string(mysql_error(handle->native_handle())) + " (statement was >>" + statement + "<<\n"); } } @@ -179,18 +186,18 @@ namespace sqlpp } } - inline std::shared_ptr prepare_statement(detail::connection_handle_t& handle, + inline std::shared_ptr prepare_statement(std::unique_ptr& handle, const std::string& statement, size_t no_of_parameters, size_t no_of_columns) { thread_init(); - if (handle.config->debug) + 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); + mysql_stmt_init(handle->native_handle()), no_of_parameters, no_of_columns, handle->config->debug); if (not prepared_statement) { throw sqlpp::exception("MySQL error: Could not allocate prepared statement\n"); @@ -198,7 +205,7 @@ namespace sqlpp 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())) + + "MySQL error: Could not prepare statement: " + std::string(mysql_error(handle->native_handle())) + " (statement was >>" + statement + "<<\n"); } @@ -226,13 +233,15 @@ namespace sqlpp static const auto global_init_and_end = scoped_library_initializer_t(argc, argv, groups); } - class connection; + // Forward declaration + class connection_base; struct serializer_t { - serializer_t(const connection& db) : _db(db) + serializer_t(const connection_base& db) : _db(db) { } + serializer_t(const connection_base&&) = delete; template std::ostream& operator<<(T t) @@ -247,7 +256,7 @@ namespace sqlpp return _os.str(); } - const connection& _db; + const connection_base& _db; sqlpp::detail::float_safe_ostringstream _os; }; @@ -255,9 +264,9 @@ namespace sqlpp std::integral_constant get_quote_right(const serializer_t&); - class connection : public sqlpp::connection + class connection_base : public sqlpp::connection { - detail::connection_handle_t _handle; + private: bool _transaction_active = false; // direct execution @@ -265,11 +274,11 @@ namespace sqlpp { execute_statement(_handle, statement); std::unique_ptr result_handle( - new detail::result_handle(mysql_store_result(_handle.mysql.get()), _handle.config->debug)); + new detail::result_handle(mysql_store_result(_handle->native_handle()), _handle->config->debug)); if (!*result_handle) { throw sqlpp::exception("MySQL error: Could not store result set: " + - std::string(mysql_error(_handle.mysql.get()))); + std::string(mysql_error(_handle->native_handle()))); } return {std::move(result_handle)}; @@ -279,19 +288,19 @@ namespace sqlpp { execute_statement(_handle, statement); - return mysql_insert_id(_handle.mysql.get()); + return mysql_insert_id(_handle->native_handle()); } size_t update_impl(const std::string& statement) { execute_statement(_handle, statement); - return mysql_affected_rows(_handle.mysql.get()); + return mysql_affected_rows(_handle->native_handle()); } size_t remove_impl(const std::string& statement) { execute_statement(_handle, statement); - return mysql_affected_rows(_handle.mysql.get()); + return mysql_affected_rows(_handle->native_handle()); } // prepared execution @@ -325,6 +334,12 @@ namespace sqlpp } public: + using _connection_base_t = connection_base; + using _config_t = connection_config; + using _config_ptr_t = std::shared_ptr; + using _handle_t = detail::connection_handle_t; + using _handle_ptr_t = std::unique_ptr<_handle_t>; + using _prepared_statement_t = ::sqlpp::mysql::prepared_statement_t; using _context_t = serializer_t; using _serializer_context_t = _context_t; @@ -347,30 +362,19 @@ namespace sqlpp return serialize(t, context); } - connection(const std::shared_ptr& config) : _handle{config} + bool is_valid() const { - } - - ~connection() = default; - - connection(const connection&) = delete; - connection& operator=(const connection&) = delete; - connection& operator=(connection&&) = default; - connection(connection&& other) = default; - - bool is_valid() - { - return _handle.is_valid(); + return _handle->check_connection(); } void reconnect() { - return _handle.reconnect(); + return _handle->reconnect(); } - const std::shared_ptr& get_config() + const std::shared_ptr& get_config() { - return _handle.config; + return _handle->config; } bool is_transaction_active() @@ -483,7 +487,7 @@ namespace sqlpp std::string escape(const std::string& s) const { std::unique_ptr dest(new char[s.size() * 2 + 1]); - mysql_real_escape_string(_handle.mysql.get(), dest.get(), s.c_str(), s.size()); + mysql_real_escape_string(_handle->native_handle(), dest.get(), s.c_str(), s.size()); return dest.get(); } @@ -569,16 +573,35 @@ namespace sqlpp std::cerr << "MySQL message:" << message << std::endl; } + MYSQL* native_handle() + { + return _handle->native_handle(); + } + + // Kept for compatibility with old code MYSQL* get_handle() { - return _handle.mysql.get(); + return native_handle(); + } + + protected: + _handle_ptr_t _handle; + + // Constructors + connection_base() = default; + connection_base(_handle_ptr_t&& handle) : _handle{std::move(handle)} + { } }; + // Method definition moved outside of class because it needs connection_base inline std::string serializer_t::escape(std::string arg) { return _db.escape(arg); } + + using connection = sqlpp::normal_connection; + using pooled_connection = sqlpp::pooled_connection; } // namespace mysql } // namespace sqlpp diff --git a/include/sqlpp11/mysql/connection_config.h b/include/sqlpp11/mysql/connection_config.h index 72d78748..f3c0371a 100644 --- a/include/sqlpp11/mysql/connection_config.h +++ b/include/sqlpp11/mysql/connection_config.h @@ -33,10 +33,8 @@ namespace sqlpp { namespace mysql { - class connection; struct connection_config { - typedef ::sqlpp::mysql::connection connection; std::string host = "localhost"; std::string user; std::string password; diff --git a/include/sqlpp11/mysql/connection_pool.h b/include/sqlpp11/mysql/connection_pool.h new file mode 100644 index 00000000..de62390a --- /dev/null +++ b/include/sqlpp11/mysql/connection_pool.h @@ -0,0 +1,39 @@ +#pragma once + +/* +Copyright (c) 2017 - 2018, Roland Bock +Copyright (c) 2023, Vesselin Atanasov +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. +*/ + +#include +#include + +namespace sqlpp +{ + namespace mysql + { + using connection_pool = sqlpp::connection_pool; + } // namespace mysql +} // namespace sqlpp diff --git a/include/sqlpp11/mysql/mysql.h b/include/sqlpp11/mysql/mysql.h index f817507a..2702fab0 100644 --- a/include/sqlpp11/mysql/mysql.h +++ b/include/sqlpp11/mysql/mysql.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2013 - 2015, Roland Bock + * Copyright (c) 2023, Vesselin Atanasov * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, @@ -28,6 +29,7 @@ #define SQLPP_MYSQL_H #include +#include #include #endif diff --git a/include/sqlpp11/mysql/prepared_statement.h b/include/sqlpp11/mysql/prepared_statement.h index d3920603..a00475e2 100644 --- a/include/sqlpp11/mysql/prepared_statement.h +++ b/include/sqlpp11/mysql/prepared_statement.h @@ -41,11 +41,11 @@ namespace sqlpp { namespace mysql { - class connection; + class connection_base; class prepared_statement_t { - friend ::sqlpp::mysql::connection; + friend ::sqlpp::mysql::connection_base; std::shared_ptr _handle; public: diff --git a/include/sqlpp11/postgresql/connection.h b/include/sqlpp11/postgresql/connection.h index aeca6c50..9856332a 100644 --- a/include/sqlpp11/postgresql/connection.h +++ b/include/sqlpp11/postgresql/connection.h @@ -1,5 +1,6 @@ /** * Copyright © 2014-2015, Matthijs Möhlmann + * Copyright (c) 2023, Vesselin Atanasov * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -63,39 +64,39 @@ namespace sqlpp namespace detail { // Forward declaration - inline std::unique_ptr prepare_statement(detail::connection_handle& handle, + inline std::unique_ptr prepare_statement(std::unique_ptr& handle, const std::string& stmt, const size_t& paramCount) { - if (handle.config->debug) + if (handle->config->debug) { std::cerr << "PostgreSQL debug: preparing: " << stmt << std::endl; } - return std::unique_ptr(new detail::prepared_statement_handle_t - (handle, stmt, paramCount)); + return std::unique_ptr(new detail::prepared_statement_handle_t + (*handle, stmt, paramCount)); } - inline void execute_prepared_statement(detail::connection_handle& handle, detail::prepared_statement_handle_t& prepared) + inline void execute_prepared_statement(std::unique_ptr& handle, std::shared_ptr& prepared) { - if (handle.config->debug) + if (handle->config->debug) { - std::cerr << "PostgreSQL debug: executing: " << prepared.name() << std::endl; + std::cerr << "PostgreSQL debug: executing: " << prepared->name() << std::endl; } - prepared.execute(); + prepared->execute(); } } // Forward declaration - class connection; + class connection_base; // Context struct context_t { - context_t(const connection& db) : _db(db) + context_t(const connection_base& db) : _db(db) { } - context_t(const connection&&) = delete; + context_t(const connection_base&&) = delete; template std::ostream& operator<<(T t) @@ -108,7 +109,7 @@ namespace sqlpp return _os << (t ? "TRUE" : "FALSE"); } - std::string escape(const std::string& arg); + std::string escape(const std::string& arg) const; std::string str() const { @@ -125,22 +126,22 @@ namespace sqlpp ++_count; } - const connection& _db; + const connection_base& _db; sqlpp::detail::float_safe_ostringstream _os; size_t _count{1}; }; - // Connection - class connection : public sqlpp::connection + // Base connection class + class connection_base : public sqlpp::connection { private: - std::unique_ptr _handle; bool _transaction_active{false}; void validate_connection_handle() const { - if (!_handle) + if (!_handle) { throw std::logic_error("connection handle used, but not initialized"); + } } // direct execution @@ -158,6 +159,12 @@ namespace sqlpp size_t run_prepared_remove_impl(prepared_statement_t& prep); public: + using _connection_base_t = connection_base; + using _config_t = connection_config; + using _config_ptr_t = std::shared_ptr; + using _handle_t = detail::connection_handle; + using _handle_ptr_t = std::unique_ptr<_handle_t>; + using _prepared_statement_t = prepared_statement_t; using _context_t = context_t; using _serializer_context_t = _context_t; @@ -180,18 +187,6 @@ namespace sqlpp return ::sqlpp::serialize(t, context); } - // ctor / dtor - connection(); - connection(const std::shared_ptr& config); - ~connection(); - connection(const connection&) = delete; - connection(connection&&); - connection& operator=(const connection&) = delete; - connection& operator=(connection&&); - - // creates a connection handle and connects to database - void connectUsing(const std::shared_ptr& config) noexcept(false); - // Select stmt (returns a result) template bind_result_t select(const Select& s) @@ -384,45 +379,19 @@ namespace sqlpp //! get the last inserted id for a certain table uint64_t last_insert_id(const std::string& table, const std::string& fieldname); - ::PGconn* native_handle(); + ::PGconn* native_handle() const; + + protected: + _handle_ptr_t _handle; + + // Constructors + connection_base() = default; + connection_base(_handle_ptr_t&& handle) : _handle{std::move(handle)} + { + } }; - inline connection::connection() : _handle() - { - } - - inline connection::connection(const std::shared_ptr& config) - : _handle(new detail::connection_handle(config)) - { - } - - inline connection::~connection() - { - } - - inline connection::connection(connection&& other) - { - this->_transaction_active = other._transaction_active; - this->_handle = std::move(other._handle); - } - - inline connection& connection::operator=(connection&& other) - { - if (this != &other) - { - // TODO: check this logic - this->_transaction_active = other._transaction_active; - this->_handle = std::move(other._handle); - } - return *this; - } - - inline void connection::connectUsing(const std::shared_ptr& config) noexcept(false) - { - this->_handle.reset(new detail::connection_handle(config)); - } - - inline std::shared_ptr connection::execute(const std::string& stmt) + inline std::shared_ptr connection_base::execute(const std::string& stmt) { validate_connection_handle(); if (_handle->config->debug) @@ -431,75 +400,76 @@ namespace sqlpp } auto result = std::make_shared(*_handle); - result->result = PQexec(_handle->native(), stmt.c_str()); + result->result = PQexec(native_handle(), stmt.c_str()); result->valid = true; return result; } + // direct execution - inline bind_result_t connection::select_impl(const std::string& stmt) + inline bind_result_t connection_base::select_impl(const std::string& stmt) { return execute(stmt); } - inline size_t connection::insert_impl(const std::string& stmt) + inline size_t connection_base::insert_impl(const std::string& stmt) { return static_cast(execute(stmt)->result.affected_rows()); } - inline size_t connection::update_impl(const std::string& stmt) + inline size_t connection_base::update_impl(const std::string& stmt) { return static_cast(execute(stmt)->result.affected_rows()); } - inline size_t connection::remove_impl(const std::string& stmt) + inline size_t connection_base::remove_impl(const std::string& stmt) { return static_cast(execute(stmt)->result.affected_rows()); } // prepared execution - inline prepared_statement_t connection::prepare_impl(const std::string& stmt, const size_t& paramCount) + inline prepared_statement_t connection_base::prepare_impl(const std::string& stmt, const size_t& paramCount) { validate_connection_handle(); - return {prepare_statement(*_handle, stmt, paramCount)}; + return {prepare_statement(_handle, stmt, paramCount)}; } - inline bind_result_t connection::run_prepared_select_impl(prepared_statement_t& prep) + inline bind_result_t connection_base::run_prepared_select_impl(prepared_statement_t& prep) { validate_connection_handle(); - execute_prepared_statement(*_handle, *prep._handle.get()); + execute_prepared_statement(_handle, prep._handle); return {prep._handle}; } - inline size_t connection::run_prepared_execute_impl(prepared_statement_t& prep) + inline size_t connection_base::run_prepared_execute_impl(prepared_statement_t& prep) { validate_connection_handle(); - execute_prepared_statement(*_handle, *prep._handle.get()); + execute_prepared_statement(_handle, prep._handle); return static_cast(prep._handle->result.affected_rows()); } - inline size_t connection::run_prepared_insert_impl(prepared_statement_t& prep) + inline size_t connection_base::run_prepared_insert_impl(prepared_statement_t& prep) { validate_connection_handle(); - execute_prepared_statement(*_handle, *prep._handle.get()); + execute_prepared_statement(_handle, prep._handle); return static_cast(prep._handle->result.affected_rows()); } - inline size_t connection::run_prepared_update_impl(prepared_statement_t& prep) + inline size_t connection_base::run_prepared_update_impl(prepared_statement_t& prep) { validate_connection_handle(); - execute_prepared_statement(*_handle, *prep._handle.get()); + execute_prepared_statement(_handle, prep._handle); return static_cast(prep._handle->result.affected_rows()); } - inline size_t connection::run_prepared_remove_impl(prepared_statement_t& prep) + inline size_t connection_base::run_prepared_remove_impl(prepared_statement_t& prep) { validate_connection_handle(); - execute_prepared_statement(*_handle, *prep._handle.get()); + execute_prepared_statement(_handle, prep._handle); return static_cast(prep._handle->result.affected_rows()); } - inline void connection::set_default_isolation_level(isolation_level level) + inline void connection_base::set_default_isolation_level(isolation_level level) { std::string level_str = "read uncommmitted"; switch (level) @@ -524,7 +494,8 @@ namespace sqlpp execute(cmd); } - inline isolation_level connection::get_default_isolation_level() + //! get the currently set default transaction isolation level + inline isolation_level connection_base::get_default_isolation_level() { auto res = execute("SHOW default_transaction_isolation;"); auto status = res->result.status(); @@ -554,7 +525,7 @@ namespace sqlpp } // TODO: Fix escaping. - inline std::string connection::escape(const std::string& s) const + inline std::string connection_base::escape(const std::string& s) const { validate_connection_handle(); // Escape strings @@ -562,13 +533,13 @@ namespace sqlpp result.resize((s.size() * 2) + 1); int err; - size_t length = PQescapeStringConn(_handle->native(), &result[0], s.c_str(), s.size(), &err); + size_t length = PQescapeStringConn(native_handle(), &result[0], s.c_str(), s.size(), &err); result.resize(length); return result; } //! start transaction - inline void connection::start_transaction(sqlpp::isolation_level level) + inline void connection_base::start_transaction(isolation_level level) { if (_transaction_active) { @@ -606,28 +577,28 @@ namespace sqlpp } //! create savepoint - inline void connection::savepoint(const std::string& name) + inline void connection_base::savepoint(const std::string& name) { /// NOTE prevent from sql injection? execute("SAVEPOINT " + name); } //! ROLLBACK TO SAVEPOINT - inline void connection::rollback_to_savepoint(const std::string& name) + inline void connection_base::rollback_to_savepoint(const std::string& name) { /// NOTE prevent from sql injection? execute("ROLLBACK TO SAVEPOINT " + name); } //! release_savepoint - inline void connection::release_savepoint(const std::string& name) + inline void connection_base::release_savepoint(const std::string& name) { /// NOTE prevent from sql injection? execute("RELEASE SAVEPOINT " + name); } //! commit transaction (or throw transaction if transaction has finished already) - inline void connection::commit_transaction() + inline void connection_base::commit_transaction() { if (!_transaction_active) { @@ -639,7 +610,7 @@ namespace sqlpp } //! rollback transaction - inline void connection::rollback_transaction(bool report) + inline void connection_base::rollback_transaction(bool report) { if (!_transaction_active) { @@ -655,15 +626,15 @@ namespace sqlpp } //! report rollback failure - inline void connection::report_rollback_failure(const std::string& message) noexcept + inline void connection_base::report_rollback_failure(const std::string& message) noexcept { std::cerr << "PostgreSQL error: " << message << std::endl; } - inline uint64_t connection::last_insert_id(const std::string& table, const std::string& fieldname) + inline uint64_t connection_base::last_insert_id(const std::string& table, const std::string& fieldname) { std::string sql = "SELECT currval('" + table + "_" + fieldname + "_seq')"; - PGresult* res = PQexec(_handle->native(), sql.c_str()); + PGresult* res = PQexec(native_handle(), sql.c_str()); if (PQresultStatus(res) != PGRES_TUPLES_OK) { std::string err{PQresultErrorMessage(res)}; @@ -677,17 +648,20 @@ namespace sqlpp return std::stoul(in); } - inline ::PGconn* connection::native_handle() + inline ::PGconn* connection_base::native_handle() const { - return _handle->native(); + return _handle->native_handle(); } - inline std::string context_t::escape(const std::string& arg) + inline std::string context_t::escape(const std::string& arg) const { return _db.escape(arg); } - } -} + + using connection = sqlpp::normal_connection; + using pooled_connection = sqlpp::pooled_connection; + } // namespace postgresql +} // namespace sqlpp #include diff --git a/include/sqlpp11/postgresql/connection_config.h b/include/sqlpp11/postgresql/connection_config.h index e7dac271..9fd12a1a 100644 --- a/include/sqlpp11/postgresql/connection_config.h +++ b/include/sqlpp11/postgresql/connection_config.h @@ -35,12 +35,8 @@ namespace sqlpp { namespace postgresql { - class connection; struct DLL_PUBLIC connection_config { - // Needed for the connection pool - typedef ::sqlpp::postgresql::connection connection; - enum class sslmode_t { disable, diff --git a/include/sqlpp11/postgresql/connection_pool.h b/include/sqlpp11/postgresql/connection_pool.h new file mode 100644 index 00000000..52bba3df --- /dev/null +++ b/include/sqlpp11/postgresql/connection_pool.h @@ -0,0 +1,39 @@ +#pragma once + +/* +Copyright (c) 2017 - 2018, Roland Bock +Copyright (c) 2023, Vesselin Atanasov +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. +*/ + +#include +#include + +namespace sqlpp +{ + namespace postgresql + { + using connection_pool = sqlpp::connection_pool; + } // namespace postgresql +} // namespace sqlpp diff --git a/include/sqlpp11/postgresql/detail/connection_handle.h b/include/sqlpp11/postgresql/detail/connection_handle.h index b23ab279..a52fb551 100644 --- a/include/sqlpp11/postgresql/detail/connection_handle.h +++ b/include/sqlpp11/postgresql/detail/connection_handle.h @@ -67,16 +67,13 @@ namespace sqlpp connection_handle(const std::shared_ptr& config); connection_handle(const connection_handle&) = delete; connection_handle(connection_handle&&) = default; + ~connection_handle(); connection_handle& operator=(const connection_handle&) = delete; connection_handle& operator=(connection_handle&&) = default; - ~connection_handle(); - - PGconn* native() const - { - return postgres.get(); - } void deallocate_prepared_statement(const std::string& name); + PGconn* native_handle() const; + bool check_connection() const; }; inline connection_handle::connection_handle(const std::shared_ptr& conf) @@ -206,9 +203,9 @@ namespace sqlpp if (!postgres) throw std::bad_alloc(); - if (PQstatus(postgres.get()) != CONNECTION_OK) + if (check_connection() == false) { - std::string msg(PQerrorMessage(postgres.get())); + std::string msg(PQerrorMessage(native_handle())); throw broken_connection(std::move(msg)); } } @@ -225,10 +222,21 @@ namespace sqlpp inline void connection_handle::deallocate_prepared_statement(const std::string& name) { std::string cmd = "DEALLOCATE \"" + name + "\""; - PGresult* result = PQexec(postgres.get(), cmd.c_str()); + PGresult* result = PQexec(native_handle(), cmd.c_str()); PQclear(result); prepared_statement_names.erase(name); } + + inline PGconn* connection_handle::native_handle() const + { + return postgres.get(); + } + + inline bool connection_handle::check_connection() const + { + auto nh = native_handle(); + return nh && (PQstatus(nh) == CONNECTION_OK); + } } } } diff --git a/include/sqlpp11/postgresql/detail/prepared_statement_handle.h b/include/sqlpp11/postgresql/detail/prepared_statement_handle.h index d3e2fa97..a8fbb706 100644 --- a/include/sqlpp11/postgresql/detail/prepared_statement_handle.h +++ b/include/sqlpp11/postgresql/detail/prepared_statement_handle.h @@ -164,7 +164,7 @@ namespace sqlpp valid = false; count = 0; totalCount = 0; - result = PQexecPrepared(connection.native(), _name.data(), static_cast(size), values.data(), nullptr, nullptr, 0); + result = PQexecPrepared(connection.native_handle(), _name.data(), static_cast(size), values.data(), nullptr, nullptr, 0); /// @todo validate result? is it really valid valid = true; } @@ -188,7 +188,7 @@ namespace sqlpp inline void prepared_statement_handle_t::prepare(std::string stmt) { // Create the prepared statement - result = PQprepare(connection.native(), _name.c_str(), stmt.c_str(), 0, nullptr); + result = PQprepare(connection.native_handle(), _name.c_str(), stmt.c_str(), 0, nullptr); valid = true; } } diff --git a/include/sqlpp11/postgresql/postgresql.h b/include/sqlpp11/postgresql/postgresql.h index 84e4cfa9..e68f75fa 100644 --- a/include/sqlpp11/postgresql/postgresql.h +++ b/include/sqlpp11/postgresql/postgresql.h @@ -1,5 +1,6 @@ /** * Copyright © 2014-2015, Matthijs Möhlmann + * Copyright (c) 2023, Vesselin Atanasov * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,6 +30,7 @@ #define SQLPP_POSTGRESQL_H #include +#include #include #include #include diff --git a/include/sqlpp11/postgresql/prepared_statement.h b/include/sqlpp11/postgresql/prepared_statement.h index e8bc4c70..ab5f1c14 100644 --- a/include/sqlpp11/postgresql/prepared_statement.h +++ b/include/sqlpp11/postgresql/prepared_statement.h @@ -44,7 +44,7 @@ namespace sqlpp #endif // Forward declaration - class connection; + class connection_base; // Detail namespace namespace detail @@ -54,9 +54,9 @@ namespace sqlpp class prepared_statement_t { - friend sqlpp::postgresql::connection; - private: + friend class sqlpp::postgresql::connection_base; + std::shared_ptr _handle; public: diff --git a/include/sqlpp11/postgresql/result_field.h b/include/sqlpp11/postgresql/result_field.h index 8311f286..565ade42 100644 --- a/include/sqlpp11/postgresql/result_field.h +++ b/include/sqlpp11/postgresql/result_field.h @@ -40,7 +40,8 @@ namespace sqlpp { namespace postgresql { - class connection; + // Forward declaration + class connection_base; } namespace detail @@ -93,8 +94,8 @@ namespace sqlpp } // namespace detail template - struct result_field_t> - : public result_field_base> + struct result_field_t> + : public result_field_base> { private: const uint8_t* _blob{nullptr}; // Non-owning diff --git a/include/sqlpp11/sqlite3/connection.h b/include/sqlpp11/sqlite3/connection.h index 164b676b..d5f1bb4a 100644 --- a/include/sqlpp11/sqlite3/connection.h +++ b/include/sqlpp11/sqlite3/connection.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2013 - 2016, Roland Bock + * Copyright (c) 2023, Vesselin Atanasov * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, @@ -72,18 +73,20 @@ namespace sqlpp struct connection_handle { - connection_config config; + std::shared_ptr config; std::unique_ptr<::sqlite3, void (*)(::sqlite3*)> sqlite; - connection_handle(connection_config conf) : config(conf), sqlite(nullptr, handle_cleanup) + connection_handle(const std::shared_ptr& conf) : + config(conf), + sqlite(nullptr, handle_cleanup) { #ifdef SQLPP_DYNAMIC_LOADING init_sqlite(""); #endif ::sqlite3* sqlite_ptr; - const auto rc = sqlite3_open_v2(conf.path_to_database.c_str(), &sqlite_ptr, conf.flags, - conf.vfs.empty() ? nullptr : conf.vfs.c_str()); + const auto rc = sqlite3_open_v2(conf->path_to_database.c_str(), &sqlite_ptr, conf->flags, + conf->vfs.empty() ? nullptr : conf->vfs.c_str()); if (rc != SQLITE_OK) { const std::string msg = sqlite3_errmsg(sqlite_ptr); @@ -94,13 +97,13 @@ namespace sqlpp sqlite.reset(sqlite_ptr); #ifdef SQLITE_HAS_CODEC - if (conf.password.size() > 0) + if (conf->password.size() > 0) { - int ret = sqlite3_key(sqlite.get(), conf.password.data(), conf.password.size()); + int ret = sqlite3_key(native_handle(), conf->password.data(), conf->password.size()); if (ret != SQLITE_OK) { - const std::string msg = sqlite3_errmsg(sqlite.get()); - sqlite3_close(sqlite.get()); + const std::string msg = sqlite3_errmsg(native_handle()); + sqlite3_close(native_handle()); throw sqlpp::exception("Sqlite3 error: Can't set password to database: " + msg); } } @@ -111,31 +114,40 @@ namespace sqlpp connection_handle(connection_handle&&) = default; connection_handle& operator=(const connection_handle&) = delete; connection_handle& operator=(connection_handle&&) = default; - ~connection_handle() = default; + + ::sqlite3* native_handle() const + { + return sqlite.get(); + } + + bool check_connection() const + { + return native_handle() != nullptr; + } }; - inline detail::prepared_statement_handle_t prepare_statement(detail::connection_handle& handle, + inline detail::prepared_statement_handle_t prepare_statement(std::unique_ptr& handle, const std::string& statement) { - if (handle.config.debug) + if (handle->config->debug) std::cerr << "Sqlite3 debug: Preparing: '" << statement << "'" << std::endl; - detail::prepared_statement_handle_t result(nullptr, handle.config.debug); + detail::prepared_statement_handle_t result(nullptr, handle->config->debug); - auto rc = sqlite3_prepare_v2(handle.sqlite.get(), statement.c_str(), static_cast(statement.size()), + auto rc = sqlite3_prepare_v2(handle->native_handle(), statement.c_str(), static_cast(statement.size()), &result.sqlite_statement, nullptr); if (rc != SQLITE_OK) { throw sqlpp::exception( - "Sqlite3 error: Could not prepare statement: " + std::string(sqlite3_errmsg(handle.sqlite.get())) + + "Sqlite3 error: Could not prepare statement: " + std::string(sqlite3_errmsg(handle->native_handle())) + " (statement was >>" + (rc == SQLITE_TOOBIG ? statement.substr(0, 128) + "..." : statement) + "<<\n"); } return result; } - inline void execute_statement(detail::connection_handle& handle, detail::prepared_statement_handle_t& prepared) + inline void execute_statement(std::unique_ptr& handle, detail::prepared_statement_handle_t& prepared) { auto rc = sqlite3_step(prepared.sqlite_statement); switch (rc) @@ -145,19 +157,20 @@ namespace sqlpp case SQLITE_DONE: return; default: - if (handle.config.debug) + if (handle->config->debug) std::cerr << "Sqlite3 debug: sqlite3_step return code: " << rc << std::endl; throw sqlpp::exception("Sqlite3 error: Could not execute statement: " + - std::string(sqlite3_errmsg(handle.sqlite.get()))); + std::string(sqlite3_errmsg(handle->native_handle()))); } } } // namespace detail - class connection; + // Forward declaration + class connection_base; struct serializer_t { - serializer_t(const connection& db) : _db(db), _count(1) + serializer_t(const connection_base& db) : _db(db), _count(1) { } @@ -184,14 +197,15 @@ namespace sqlpp ++_count; } - const connection& _db; + const connection_base& _db; sqlpp::detail::float_safe_ostringstream _os; size_t _count; }; - class SQLPP11_SQLITE3_EXPORT connection : public sqlpp::connection + // Base connection class + class SQLPP11_SQLITE3_EXPORT connection_base : public sqlpp::connection { - detail::connection_handle _handle; + private: enum class transaction_status_type { none, @@ -219,21 +233,21 @@ namespace sqlpp auto prepared = prepare_statement(_handle, statement); execute_statement(_handle, prepared); - return static_cast(sqlite3_last_insert_rowid(_handle.sqlite.get())); + return static_cast(sqlite3_last_insert_rowid(native_handle())); } size_t update_impl(const std::string& statement) { auto prepared = prepare_statement(_handle, statement); execute_statement(_handle, prepared); - return static_cast(sqlite3_changes(_handle.sqlite.get())); + return static_cast(sqlite3_changes(native_handle())); } size_t remove_impl(const std::string& statement) { auto prepared = prepare_statement(_handle, statement); execute_statement(_handle, prepared); - return static_cast(sqlite3_changes(_handle.sqlite.get())); + return static_cast(sqlite3_changes(native_handle())); } // prepared execution @@ -252,31 +266,37 @@ namespace sqlpp { execute_statement(_handle, *prepared_statement._handle.get()); - return static_cast(sqlite3_last_insert_rowid(_handle.sqlite.get())); + return static_cast(sqlite3_last_insert_rowid(native_handle())); } size_t run_prepared_update_impl(prepared_statement_t& prepared_statement) { execute_statement(_handle, *prepared_statement._handle.get()); - return static_cast(sqlite3_changes(_handle.sqlite.get())); + return static_cast(sqlite3_changes(native_handle())); } size_t run_prepared_remove_impl(prepared_statement_t& prepared_statement) { execute_statement(_handle, *prepared_statement._handle.get()); - return static_cast(sqlite3_changes(_handle.sqlite.get())); + return static_cast(sqlite3_changes(native_handle())); } size_t run_prepared_execute_impl(prepared_statement_t& prepared_statement) { execute_statement(_handle, *prepared_statement._handle.get()); - return static_cast(sqlite3_changes(_handle.sqlite.get())); + return static_cast(sqlite3_changes(native_handle())); } public: + using _connection_base_t = connection_base; + using _config_t = connection_config; + using _config_ptr_t = std::shared_ptr; + using _handle_t = detail::connection_handle; + using _handle_ptr_t = std::unique_ptr<_handle_t>; + using _prepared_statement_t = prepared_statement_t; using _context_t = serializer_t; using _serializer_context_t = _context_t; @@ -299,18 +319,6 @@ namespace sqlpp return ::sqlpp::serialize(t, context); } - connection(connection_config config) : _handle(std::move(config)) - { - } - - connection(connection&&) noexcept = default; - connection& operator=(connection&&) noexcept = default; - - ~connection() = default; - - connection(const connection&) = delete; - connection& operator=(const connection&) = delete; - //! select returns a result (which can be iterated row by row) template bind_result_t select(const Select& s) @@ -416,7 +424,7 @@ namespace sqlpp { auto prepared = prepare_statement(_handle, statement); execute_statement(_handle, prepared); - return static_cast(sqlite3_changes(_handle.sqlite.get())); + return static_cast(sqlite3_changes(native_handle())); } template < @@ -573,12 +581,12 @@ namespace sqlpp //! get the last inserted id uint64_t last_insert_id() noexcept { - return static_cast(sqlite3_last_insert_rowid(_handle.sqlite.get())); + return static_cast(sqlite3_last_insert_rowid(native_handle())); } - ::sqlite3* native_handle() + ::sqlite3* native_handle() const { - return _handle.sqlite.get(); + return _handle->native_handle(); } schema_t attach(const connection_config& config, const std::string name) @@ -589,12 +597,25 @@ namespace sqlpp return {name}; } + + protected: + _handle_ptr_t _handle; + + // Constructors + connection_base() = default; + connection_base(_handle_ptr_t&& handle) : _handle{std::move(handle)} + { + } }; + // Method definition moved outside of class because it needs connection_base inline std::string serializer_t::escape(std::string arg) { return _db.escape(arg); } + + using connection = sqlpp::normal_connection; + using pooled_connection = sqlpp::pooled_connection; } // namespace sqlite3 } // namespace sqlpp diff --git a/include/sqlpp11/sqlite3/connection_pool.h b/include/sqlpp11/sqlite3/connection_pool.h new file mode 100644 index 00000000..05a4187b --- /dev/null +++ b/include/sqlpp11/sqlite3/connection_pool.h @@ -0,0 +1,39 @@ +#pragma once + +/* +Copyright (c) 2017 - 2018, Roland Bock +Copyright (c) 2023, Vesselin Atanasov +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. +*/ + +#include +#include + +namespace sqlpp +{ + namespace sqlite3 + { + using connection_pool = sqlpp::connection_pool; + } // namespace sqlite3 +} // namespace sqlpp diff --git a/include/sqlpp11/sqlite3/prepared_statement.h b/include/sqlpp11/sqlite3/prepared_statement.h index db285bff..30158a96 100644 --- a/include/sqlpp11/sqlite3/prepared_statement.h +++ b/include/sqlpp11/sqlite3/prepared_statement.h @@ -49,6 +49,9 @@ namespace sqlpp { namespace sqlite3 { + // Forward declaration + class connection_base; + namespace detail { inline void check_bind_result(int result, const char* const type) @@ -70,11 +73,9 @@ namespace sqlpp } } // namespace detail - class connection; - class SQLPP11_SQLITE3_EXPORT prepared_statement_t { - friend ::sqlpp::sqlite3::connection; + friend class ::sqlpp::sqlite3::connection_base; std::shared_ptr _handle; public: diff --git a/include/sqlpp11/sqlite3/prepared_statement_handle.h b/include/sqlpp11/sqlite3/prepared_statement_handle.h index 36bb2600..6af1ab6f 100644 --- a/include/sqlpp11/sqlite3/prepared_statement_handle.h +++ b/include/sqlpp11/sqlite3/prepared_statement_handle.h @@ -42,8 +42,6 @@ namespace sqlpp { namespace sqlite3 { - class connection; - namespace detail { struct prepared_statement_handle_t diff --git a/include/sqlpp11/sqlite3/sqlite3.h b/include/sqlpp11/sqlite3/sqlite3.h index 2a033ad1..c53977a3 100644 --- a/include/sqlpp11/sqlite3/sqlite3.h +++ b/include/sqlpp11/sqlite3/sqlite3.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2013 - 2015, Roland Bock + * Copyright (c) 2023, Vesselin Atanasov * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, @@ -28,6 +29,7 @@ #define SQLPP_SQLITE3_H #include +#include #include #endif diff --git a/tests/include/ConnectionPoolTests.h b/tests/include/ConnectionPoolTests.h new file mode 100644 index 00000000..35b4330a --- /dev/null +++ b/tests/include/ConnectionPoolTests.h @@ -0,0 +1,260 @@ +#pragma once + +/* +Copyright (c) 2017 - 2018, Roland Bock +Copyright (c) 2023, Vesselin Atanasov +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. +*/ + +#include +#include +#include +#include + +#include "TabDepartment.h" + +namespace sqlpp +{ + namespace test + { + namespace + { + template + using native_type = std::decay_t().get().native_handle())>; + + template + using native_set = std::unordered_set>; + + template + using pool_conn_type = std::decay_t().get())>; + + template + native_set get_native_handles(Pool& pool) + { + native_set ns; + if (pool.available() == 0) { + return ns; + } + for (;;) { + auto handle = pool.get().native_handle(); + auto insert_res = ns.insert(handle); + if (insert_res.second == false) { + return ns; + } + } + } + + template + void test_conn_move(Pool& pool) + { + auto nh_all = get_native_handles(pool); + { + // Get one connection from the pool + auto conn_1 = pool.get(); + // If the native handles list was empty before getting a connection, then a new connection + // was created, so we need to update the set of all native handles + if (nh_all.empty()) { + nh_all.insert(conn_1.native_handle()); + } + auto nh_removed = nh_all; + if (nh_removed.erase(conn_1.native_handle()) != 1) { + throw std::logic_error{"Got an unknown native connection handle"}; + } + if (get_native_handles(pool) != nh_removed) { + throw std::logic_error{"Could not get correctly a connection from the pool"}; + } + { + // Move the pooled connection once + auto conn_2 = std::move(conn_1); + if (get_native_handles(pool) != nh_removed) { + throw std::logic_error{"Moving a connection changes the pool"}; + } + + // Move the pooled connection again + conn_1 = std::move(conn_2); + if (get_native_handles(pool) != nh_removed) { + throw std::logic_error{"Moving a connection changes the pool"}; + } + + // The empty connection conn_2 goes out of scope and gets destroyed + } + + // Check if destroying an empty connection changed the pool + if (get_native_handles(pool) != nh_removed) { + throw std::logic_error{"Destroying an empty connection changes the pool"}; + } + + // The valid connection conn_1 goes out of scope and gets destroyed + } + + // Check if destroying a valid connection from the pool returned the handle to the pool + if (get_native_handles(pool) != nh_all) { + throw std::logic_error{"Destroying a valid connection does not return its handle to the pool"}; + } + } + + template + void test_basic(Pool& pool, const std::string& create_table) + { + try + { + auto db = pool.get(); + db.execute("DROP TABLE IF EXISTS tab_department"); + db.execute(create_table); + model::TabDepartment tabDept = {}; + db(insert_into(tabDept).default_values()); + } + catch (const std::exception& e) + { + std::cerr << "Exception in " << __func__ << "\n"; + throw; + } + } + + template + void test_single_connection(Pool& pool) + { + try + { + auto* handle = [&pool]() { + auto db = pool.get(); + return db.native_handle(); + }(); + + for (auto i = 0; i < 100; ++i) + { + auto db = pool.get(); + if (handle != db.native_handle()) + { + std::cerr << "original connection: " << handle << std::endl; + std::cerr << "received connection: " << db.native_handle() << std::endl; + throw std::logic_error{"Pool acquired more than one connection"}; + } + } + } + catch (const std::exception& e) + { + std::cerr << "Exception in " << __func__ << "\n"; + throw; + } + } + + template + void test_multiple_connections(Pool& pool) + { + try + { + model::TabDepartment tabDept = {}; + auto connections = std::vector>{}; + auto pointers = std::set{}; + for (auto i = 0; i < 50; ++i) + { + connections.push_back(pool.get()); + if (pointers.count(connections.back().native_handle())) + { + throw std::logic_error{"Pool yielded connection twice (without getting it back in between)"}; + } + pointers.insert(connections.back().native_handle()); + connections.back()(insert_into(tabDept).default_values()); + } + } + catch (const std::exception& e) + { + std::cerr << "Exception in " << __func__ << "\n"; + throw; + } + } + + template + void test_multithreaded(Pool& pool) + { + std::random_device r; + std::default_random_engine random_engine(r()); + std::uniform_int_distribution uniform_dist(1, 20); + + std::clog << "Run a random number [1,20] of threads\n"; + std::clog << "Each with a random number [1,20] of {pool.get() & insert}\n"; + + try + { + model::TabDepartment tabDept = {}; + auto threads = std::vector{}; + const auto thread_count = uniform_dist(random_engine); + + for (auto i = 0; i < thread_count; ++i) + { + threads.push_back(std::thread([func = __func__, call_count = uniform_dist(random_engine), &pool, &tabDept]() { + try + { + for (auto k = 0; k < call_count; ++k) + { + auto connection = pool.get(); + connection(insert_into(tabDept).default_values()); + } + } + catch (const std::exception& e) + { + std::cerr << std::string(func) + ": In-thread exception: " + e.what() + "\n"; + std::abort(); + } + })); + } + for (auto&& t : threads) + { + t.join(); + } + } + catch (const std::exception& e) + { + std::cerr << "Exception in " << __func__ << "\n"; + throw; + } + } + + template + void test_destruction_order(typename Pool::_config_ptr_t config) + { + // Create a pool, get a connection from it and then destroy the pool before the connection + auto pool = std::make_unique(config, 5); + auto conn = pool->get(); + pool = nullptr; + } + } + + template + void test_connection_pool (typename Pool::_config_ptr_t config, const std::string& create_table, bool test_mt) + { + auto pool = Pool {config, 5}; + sqlpp::test::test_conn_move(pool); + sqlpp::test::test_basic(pool, create_table); + sqlpp::test::test_single_connection(pool); + sqlpp::test::test_multiple_connections(pool); + if (test_mt) + { + sqlpp::test::test_multithreaded(pool); + } + sqlpp::test::test_destruction_order(config); + } + } // namespace test +} // namespace sqlpp diff --git a/tests/include/TabDepartment.h b/tests/include/TabDepartment.h new file mode 100644 index 00000000..aed3dd4b --- /dev/null +++ b/tests/include/TabDepartment.h @@ -0,0 +1,79 @@ +#pragma once + +#include +#include +#include + +namespace model +{ + namespace TabDepartment_ + { + struct Id + { + struct _alias_t + { + static constexpr const char _literal[] = "id"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T id; + T& operator()() { return id; } + const T& operator()() const { return id; } + }; + }; + using _traits = sqlpp::make_traits; + }; + struct Name + { + struct _alias_t + { + static constexpr const char _literal[] = "name"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T name; + T& operator()() { return name; } + const T& operator()() const { return name; } + }; + }; + using _traits = sqlpp::make_traits; + }; + struct Division + { + struct _alias_t + { + static constexpr const char _literal[] = "division"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T division; + T& operator()() { return division; } + const T& operator()() const { return division; } + }; + }; + using _traits = sqlpp::make_traits; + }; + } // namespace TabDepartment_ + + struct TabDepartment: sqlpp::table_t + { + struct _alias_t + { + static constexpr const char _literal[] = "tab_department"; + using _name_t = sqlpp::make_char_sequence; + template + struct _member_t + { + T tabDepartment; + T& operator()() { return tabDepartment; } + const T& operator()() const { return tabDepartment; } + }; + }; + }; +} // namespace model diff --git a/tests/mysql/usage/CMakeLists.txt b/tests/mysql/usage/CMakeLists.txt index 93284c49..3735f433 100644 --- a/tests/mysql/usage/CMakeLists.txt +++ b/tests/mysql/usage/CMakeLists.txt @@ -40,6 +40,7 @@ set(test_files Truncated.cpp Update.cpp Remove.cpp + ConnectionPool.cpp ) create_test_sourcelist(test_sources test_main.cpp ${test_files}) @@ -62,4 +63,4 @@ foreach(test_file IN LISTS test_files) add_test(NAME sqlpp11.mysql.usage.${test} COMMAND sqlpp11_mysql_tests ${test} ) -endforeach() \ No newline at end of file +endforeach() diff --git a/tests/mysql/usage/ConnectionPool.cpp b/tests/mysql/usage/ConnectionPool.cpp new file mode 100644 index 00000000..3cd55c73 --- /dev/null +++ b/tests/mysql/usage/ConnectionPool.cpp @@ -0,0 +1,56 @@ +/* +Copyright (c) 2017 - 2018, Roland Bock +Copyright (c) 2023, Vesselin Atanasov +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. +*/ + +#include +#include + +#include "../../include/ConnectionPoolTests.h" +#include "make_test_connection.h" + +namespace sql = ::sqlpp::mysql; + +int ConnectionPool(int, char*[]) +{ + try + { + sqlpp::test::test_connection_pool( + sql::make_test_config(), + "CREATE TABLE tab_department (" + "id INTEGER PRIMARY KEY AUTO_INCREMENT, " + "name CHAR(100), " + "division VARCHAR(255) NOT NULL DEFAULT 'engineering'" + ")", + mysql_thread_safe() + ); + } + catch (const std::exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + return 1; + } + return 0; +} diff --git a/tests/mysql/usage/CustomQuery.cpp b/tests/mysql/usage/CustomQuery.cpp index 3c2a0f3f..a1aece13 100644 --- a/tests/mysql/usage/CustomQuery.cpp +++ b/tests/mysql/usage/CustomQuery.cpp @@ -24,6 +24,7 @@ */ #include +#include "make_test_connection.h" #include "TabSample.h" #include #include @@ -59,28 +60,13 @@ namespace const auto tab = TabSample{}; -namespace mysql = sqlpp::mysql; +namespace sql = sqlpp::mysql; int CustomQuery(int, char*[]) { - mysql::global_library_init(); - - auto config = std::make_shared(); - config->user = "root"; - config->database = "sqlpp_mysql"; - config->debug = true; + sql::global_library_init(); 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); + auto db = sql::make_test_connection(); db.execute(R"(DROP TABLE IF EXISTS tab_sample)"); db.execute(R"(CREATE TABLE tab_sample ( alpha bigint(20) AUTO_INCREMENT, diff --git a/tests/mysql/usage/DateTime.cpp b/tests/mysql/usage/DateTime.cpp index f0b50bae..7b5eb1fb 100644 --- a/tests/mysql/usage/DateTime.cpp +++ b/tests/mysql/usage/DateTime.cpp @@ -23,6 +23,7 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "make_test_connection.h" #include "TabSample.h" #include #include @@ -68,32 +69,13 @@ namespace } } -namespace mysql = sqlpp::mysql; +namespace sql = sqlpp::mysql; int DateTime(int, char*[]) { - auto config = std::make_shared(); - config->user = "root"; - config->database = "sqlpp_mysql"; - config->debug = true; + sql::global_library_init(); 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 - { - mysql::connection db(config); + auto db = sql::make_test_connection(); db.execute(R"(SET time_zone = '+00:00')"); // To force MySQL's CURRENT_TIMESTAMP into the right timezone db.execute(R"(DROP TABLE IF EXISTS tab_date_time)"); db.execute(R"(CREATE TABLE tab_date_time ( diff --git a/tests/mysql/usage/DynamicSelect.cpp b/tests/mysql/usage/DynamicSelect.cpp index 9493750d..4cad0cb1 100644 --- a/tests/mysql/usage/DynamicSelect.cpp +++ b/tests/mysql/usage/DynamicSelect.cpp @@ -23,6 +23,7 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "make_test_connection.h" #include "TabSample.h" #include #include @@ -38,27 +39,13 @@ const auto library_raii = sqlpp::mysql::scoped_library_initializer_t{}; -namespace mysql = sqlpp::mysql; +namespace sql = sqlpp::mysql; int DynamicSelect(int, char*[]) { - auto config = std::make_shared(); - config->user = "root"; - config->database = "sqlpp_mysql"; - config->debug = true; + sql::global_library_init(); 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); + auto db = sql::make_test_connection(); db.execute(R"(DROP TABLE IF EXISTS tab_sample)"); db.execute(R"(CREATE TABLE tab_sample ( alpha bigint(20) DEFAULT NULL, diff --git a/tests/mysql/usage/Json.cpp b/tests/mysql/usage/Json.cpp index e51eaafb..78d477ba 100644 --- a/tests/mysql/usage/Json.cpp +++ b/tests/mysql/usage/Json.cpp @@ -23,6 +23,8 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "make_test_connection.h" + #include #include @@ -52,28 +54,13 @@ namespace test SQLPP_ALIAS_PROVIDER(value) } -namespace mysql = sqlpp::mysql; +namespace sql = sqlpp::mysql; int Json(int, char*[]) { - mysql::global_library_init(); - - auto config = std::make_shared(); - config->user = "root"; - config->database = "sqlpp_mysql"; - config->debug = true; + sql::global_library_init(); 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); + auto db = sql::make_test_connection(); db.execute(R"(DROP TABLE IF EXISTS tab_json)"); db.execute(R"(CREATE TABLE tab_json ( data JSON NOT NULL diff --git a/tests/mysql/usage/MoveConstructor.cpp b/tests/mysql/usage/MoveConstructor.cpp index 1e650198..08a69765 100644 --- a/tests/mysql/usage/MoveConstructor.cpp +++ b/tests/mysql/usage/MoveConstructor.cpp @@ -23,6 +23,7 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "make_test_connection.h" #include "TabSample.h" #include #include @@ -37,29 +38,15 @@ #include #include -namespace mysql = sqlpp::mysql; +namespace sql = sqlpp::mysql; int MoveConstructor(int, char*[]) { - mysql::global_library_init(); - - auto config = std::make_shared(); - config->user = "root"; - config->database = "sqlpp_mysql"; - config->debug = true; + sql::global_library_init(); + auto config = sql::make_test_config(); 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)); + std::vector connections; + connections.emplace_back(sql::connection(config)); connections.at(0).execute(R"(DROP TABLE IF EXISTS tab_sample)"); connections.at(0).execute(R"(CREATE TABLE tab_sample ( diff --git a/tests/mysql/usage/Prepared.cpp b/tests/mysql/usage/Prepared.cpp index d277ec81..e7bffad8 100644 --- a/tests/mysql/usage/Prepared.cpp +++ b/tests/mysql/usage/Prepared.cpp @@ -23,6 +23,7 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "make_test_connection.h" #include "TabSample.h" #include #include @@ -69,23 +70,10 @@ void testPreparedStatementResult(sql::connection& db) int Prepared(int, char*[]) { - auto config = std::make_shared(); - config->user = "root"; - config->database = "sqlpp_mysql"; - config->debug = true; + sql::global_library_init(); 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); + auto db = sql::make_test_connection(); db.execute(R"(DROP TABLE IF EXISTS tab_sample)"); db.execute(R"(CREATE TABLE tab_sample ( alpha bigint(20) AUTO_INCREMENT, diff --git a/tests/mysql/usage/Remove.cpp b/tests/mysql/usage/Remove.cpp index 1fd67072..b7dd1615 100644 --- a/tests/mysql/usage/Remove.cpp +++ b/tests/mysql/usage/Remove.cpp @@ -23,6 +23,7 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "make_test_connection.h" #include "TabSample.h" #include #include @@ -36,23 +37,10 @@ namespace sql = sqlpp::mysql; int Remove(int, char*[]) { - auto config = std::make_shared(); - config->user = "root"; - config->database = "sqlpp_mysql"; - config->debug = true; + sql::global_library_init(); 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); + auto db = sql::make_test_connection(); db.execute(R"(DROP TABLE IF EXISTS tab_sample)"); db.execute(R"(CREATE TABLE tab_sample ( alpha bigint(20) AUTO_INCREMENT, diff --git a/tests/mysql/usage/Sample.cpp b/tests/mysql/usage/Sample.cpp index 39fe56ed..27935eea 100644 --- a/tests/mysql/usage/Sample.cpp +++ b/tests/mysql/usage/Sample.cpp @@ -23,6 +23,7 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "make_test_connection.h" #include "TabSample.h" #include #include @@ -31,29 +32,13 @@ #include #include -namespace mysql = sqlpp::mysql; +namespace sql = sqlpp::mysql; int Sample(int, char*[]) { - sqlpp::mysql::global_library_init(); - - auto config = std::make_shared(); - config->user = "root"; - config->database = "sqlpp_mysql"; - config->debug = true; - config->connect_timeout_seconds = 5; + sql::global_library_init(); 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); + auto db = sql::make_test_connection(); db.execute(R"(DROP TABLE IF EXISTS tab_sample)"); db.execute(R"(CREATE TABLE tab_sample ( alpha bigint(20) AUTO_INCREMENT, diff --git a/tests/mysql/usage/Select.cpp b/tests/mysql/usage/Select.cpp index 6472d4a8..3f4d12b2 100644 --- a/tests/mysql/usage/Select.cpp +++ b/tests/mysql/usage/Select.cpp @@ -23,6 +23,7 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "make_test_connection.h" #include "TabSample.h" #include #include @@ -83,23 +84,9 @@ void testSelectAll(sql::connection& db, int expectedRowCount) int Select(int, char*[]) { - 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); + auto db = sql::make_test_connection(); db.execute(R"(DROP TABLE IF EXISTS tab_sample)"); db.execute(R"(CREATE TABLE tab_sample ( alpha bigint(20) AUTO_INCREMENT, diff --git a/tests/mysql/usage/Truncated.cpp b/tests/mysql/usage/Truncated.cpp index 267b429d..2c0fa078 100644 --- a/tests/mysql/usage/Truncated.cpp +++ b/tests/mysql/usage/Truncated.cpp @@ -23,6 +23,7 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "make_test_connection.h" #include "TabSample.h" #include #include @@ -44,24 +45,10 @@ const auto tab = TabSample{}; int Truncated(int, char*[]) { - auto config = std::make_shared(); - config->user = "root"; - config->database = "sqlpp_mysql"; - config->debug = true; - config->charset = "utf8"; + sql::global_library_init(); 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); + auto db = sql::make_test_connection(); db.execute(R"(DROP TABLE IF EXISTS tab_sample)"); db.execute(R"(CREATE TABLE tab_sample ( alpha bigint(20) AUTO_INCREMENT, diff --git a/tests/mysql/usage/Union.cpp b/tests/mysql/usage/Union.cpp index 6411c84c..a406ac1a 100644 --- a/tests/mysql/usage/Union.cpp +++ b/tests/mysql/usage/Union.cpp @@ -23,6 +23,7 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "make_test_connection.h" #include "TabSample.h" #include #include @@ -36,23 +37,10 @@ const auto tab = TabSample{}; int Union(int, char*[]) { - auto config = std::make_shared(); - config->user = "root"; - config->database = "sqlpp_mysql"; - config->debug = true; + sql::global_library_init(); 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); + auto db = sql::make_test_connection(); db.execute(R"(DROP TABLE IF EXISTS tab_sample)"); db.execute(R"(CREATE TABLE tab_sample ( alpha bigint(20) AUTO_INCREMENT, diff --git a/tests/mysql/usage/Update.cpp b/tests/mysql/usage/Update.cpp index d7aff71a..ecf098e1 100644 --- a/tests/mysql/usage/Update.cpp +++ b/tests/mysql/usage/Update.cpp @@ -23,6 +23,7 @@ * OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "make_test_connection.h" #include "TabSample.h" #include #include @@ -36,23 +37,10 @@ namespace sql = sqlpp::mysql; int Update(int, char*[]) { - auto config = std::make_shared(); - config->user = "root"; - config->database = "sqlpp_mysql"; - config->debug = true; + sql::global_library_init(); 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); + auto db = sql::make_test_connection(); db.execute(R"(DROP TABLE IF EXISTS tab_sample)"); db.execute(R"(CREATE TABLE tab_sample ( alpha bigint(20) AUTO_INCREMENT, diff --git a/tests/mysql/usage/make_test_connection.h b/tests/mysql/usage/make_test_connection.h new file mode 100644 index 00000000..e716e45b --- /dev/null +++ b/tests/mysql/usage/make_test_connection.h @@ -0,0 +1,65 @@ +#pragma once + +/* +Copyright (c) 2023, Vesselin Atanasov + * 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 + +namespace sqlpp +{ + namespace mysql + { + // Get configuration for test connection + inline std::shared_ptr make_test_config() + { + auto config = std::make_shared(); + config->user = "root"; + config->database = "sqlpp_mysql"; + config->debug = true; + return config; + } + + // Starts a connection + inline ::sqlpp::mysql::connection make_test_connection() + { + namespace sql = sqlpp::mysql; + + auto config = make_test_config(); + sql::connection db; + try + { + db.connectUsing(config); + } + catch (const sqlpp::exception&) + { + std::cerr << "For testing, you'll need to create a database called '" << config->database + << "', accessible by user '" << config->user << "' without a password." << std::endl; + throw; + } + return db; + } + } // namespace mysql +} // namespace sqlpp diff --git a/tests/postgresql/usage/CMakeLists.txt b/tests/postgresql/usage/CMakeLists.txt index 2d4deaf3..d2b6826b 100644 --- a/tests/postgresql/usage/CMakeLists.txt +++ b/tests/postgresql/usage/CMakeLists.txt @@ -30,6 +30,7 @@ set(test_files Basic.cpp BasicConstConfig.cpp Blob.cpp + ConnectionPool.cpp Constructor.cpp Date.cpp DateTime.cpp diff --git a/tests/postgresql/usage/ConnectionPool.cpp b/tests/postgresql/usage/ConnectionPool.cpp new file mode 100644 index 00000000..15fcdee8 --- /dev/null +++ b/tests/postgresql/usage/ConnectionPool.cpp @@ -0,0 +1,56 @@ +/* +Copyright (c) 2017 - 2018, Roland Bock +Copyright (c) 2023, Vesselin Atanasov +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. +*/ + +#include +#include + +#include "../../include/ConnectionPoolTests.h" +#include "make_test_connection.h" + +namespace sql = ::sqlpp::postgresql; + +int ConnectionPool(int, char*[]) +{ + try + { + sqlpp::test::test_connection_pool( + sql::make_test_config(), + "CREATE TABLE tab_department (" + "id SERIAL PRIMARY KEY, " + "name CHAR(100), " + "division VARCHAR(255) NOT NULL DEFAULT 'engineering'" + ")", + PQisthreadsafe() + ); + } + catch (const std::exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + return 1; + } + return 0; +} diff --git a/tests/postgresql/usage/make_test_connection.h b/tests/postgresql/usage/make_test_connection.h index 335c3285..eb308316 100644 --- a/tests/postgresql/usage/make_test_connection.h +++ b/tests/postgresql/usage/make_test_connection.h @@ -33,12 +33,10 @@ namespace sqlpp { namespace postgresql { - // Starts a connection and sets the time zone to UTC - inline ::sqlpp::postgresql::connection make_test_connection() + // Get configuration for test connection + inline std::shared_ptr make_test_config() { - namespace sql = sqlpp::postgresql; - - auto config = std::make_shared(); + auto config = std::make_shared(); #ifdef WIN32 config->dbname = "test"; @@ -49,6 +47,15 @@ namespace sqlpp config->dbname = "sqlpp_postgresql"; config->debug = true; #endif + return config; + } + + // Starts a connection and sets the time zone to UTC + inline ::sqlpp::postgresql::connection make_test_connection() + { + namespace sql = sqlpp::postgresql; + + auto config = make_test_config(); sql::connection db; try diff --git a/tests/sqlite3/usage/CMakeLists.txt b/tests/sqlite3/usage/CMakeLists.txt index a4b42b9d..09ccca9d 100644 --- a/tests/sqlite3/usage/CMakeLists.txt +++ b/tests/sqlite3/usage/CMakeLists.txt @@ -38,6 +38,7 @@ set(test_files FloatingPoint.cpp Integral.cpp Blob.cpp + ConnectionPool.cpp ) create_test_sourcelist(test_sources test_main.cpp ${test_files}) @@ -76,4 +77,4 @@ if (SQLPP_DYNAMIC_LOADING) target_compile_options(Sqlpp11Sqlite3DynamicLoadingTest INTERFACE -Wall -Wextra -pedantic) endif () add_test(NAME Sqlpp11Sqlite3DynamicLoadingTest COMMAND Sqlpp11Sqlite3DynamicLoadingTest) -endif() \ No newline at end of file +endif() diff --git a/tests/sqlite3/usage/ConnectionPool.cpp b/tests/sqlite3/usage/ConnectionPool.cpp new file mode 100644 index 00000000..519d1e53 --- /dev/null +++ b/tests/sqlite3/usage/ConnectionPool.cpp @@ -0,0 +1,59 @@ +/* +Copyright (c) 2017 - 2018, Roland Bock +Copyright (c) 2023, Vesselin Atanasov +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. +*/ + +#include +#include + +#include "../../include/ConnectionPoolTests.h" + +namespace sql = ::sqlpp::sqlite3; + +int ConnectionPool(int, char*[]) +{ + try + { + auto config = std::make_shared(); + config->path_to_database = "file:testpool?mode=memory&cache=shared"; + config->flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_URI; + config->debug = true; + sqlpp::test::test_connection_pool( + config, + "CREATE TABLE tab_department (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "name CHAR(100), " + "division VARCHAR(255) NOT NULL DEFAULT 'engineering'" + ")", + sqlite3_threadsafe() + ); + } + catch (const std::exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + return 1; + } + return 0; +}