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

Connection pools (#499)

Add support for connection pooling

* Add support for connection pooling to the core code.
* Add support for PostgreSQL connection pooling with tests.
* Add support for SQLite3 connection pooling with tests.
* Add support for MySQL connection pooling with tests.
This commit is contained in:
MeanSquaredError 2023-07-17 07:14:23 +03:00 committed by GitHub
parent a50d719364
commit dff8c23b22
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 1551 additions and 443 deletions

View File

@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2013-2015, Roland Bock * Copyright (c) 2013-2015, Roland Bock
* Copyright (c) 2023, Vesselin Atanasov
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without modification, * Redistribution and use in source and binary forms, with or without modification,
@ -37,6 +38,44 @@ namespace sqlpp
{ {
namespace database 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<const connection_config>& 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 // The context is not a requirement, but if the database requires
// any deviations from the SQL standard, you should use your own // any deviations from the SQL standard, you should use your own
// context in order to specialize the behaviour, see also interpreter.h // context in order to specialize the behaviour, see also interpreter.h
@ -48,9 +87,25 @@ namespace sqlpp
std::string escape(std::string arg); 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: 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<const _config_t>;
// 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< using _traits = ::sqlpp::make_traits<
::sqlpp::no_value_t, ::sqlpp::no_value_t,
::sqlpp::tag::enforce_null_result_treatment // If that is what you really want, leave it out otherwise ::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 // serializer and interpreter are typically the same for string based connectors
// the types are required for dynamic statement components, see sqlpp11/interpretable.h // 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 //! "direct" select
template <typename Select> template <typename Select>
<< bind_result_t >> << 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) //! report a rollback failure (will be called by transactions in case of a rollback failure in the destructor)
void report_rollback_failure(const std::string message) noexcept; void report_rollback_failure(const std::string message) noexcept;
};
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<connection_base>;
// Pooled connections that are created by the thread pool
using pooled_connection = sqlpp::pooled_connection<connection_base>;
} // namespace database
} // namespace sqlpp
#include <sqlpp11/database/interpreter.h> #include <sqlpp11/database/interpreter.h>

View File

@ -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 <sqlpp11/connection_pool.h>
#include <sqlpp11/database/connection.h>
namespace sqlpp
{
namespace database
{
using connection_pool = sqlpp::connection_pool<pooled_connection>;
} // namespace database
} // namespace sqlpp

View File

@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2013-2015, Roland Bock * Copyright (c) 2013-2015, Roland Bock
* Copyright (c) 2023, Vesselin Atanasov
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without modification, * Redistribution and use in source and binary forms, with or without modification,
@ -27,11 +28,133 @@
#ifndef SQLPP11_CONNECTION_H #ifndef SQLPP11_CONNECTION_H
#define SQLPP11_CONNECTION_H #define SQLPP11_CONNECTION_H
#include <functional>
#include <memory>
namespace sqlpp namespace sqlpp
{ {
struct connection struct connection
{ {
}; };
// Normal (non-pooled) connection
template<typename ConnectionBase>
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<typename ConnectionBase>
normal_connection<ConnectionBase>::normal_connection(const _config_t& config) :
normal_connection{std::make_shared<_config_t>(config)}
{
}
template<typename ConnectionBase>
normal_connection<ConnectionBase>::normal_connection(const _config_ptr_t& config) :
ConnectionBase{std::make_unique<_handle_t>(config)}
{
}
template<typename ConnectionBase>
void normal_connection<ConnectionBase>::connectUsing(const _config_ptr_t& config) noexcept(false)
{
ConnectionBase::_handle = std::make_unique<_handle_t>(config);
}
// Forward declaration
template<typename ConnectionBase>
class connection_pool;
// Pooled connection
template<typename ConnectionBase>
class pooled_connection : public ConnectionBase
{
friend class connection_pool<ConnectionBase>::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<typename connection_pool<ConnectionBase>::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<typename ConnectionBase>
pooled_connection<ConnectionBase>::~pooled_connection()
{
conn_release();
}
template<typename ConnectionBase>
pooled_connection<ConnectionBase>& pooled_connection<ConnectionBase>::operator=(pooled_connection&& other)
{
if (this != &other) {
conn_release();
static_cast<ConnectionBase&>(*this) = std::move(static_cast<ConnectionBase&>(other));
_pool_core = std::move(other._pool_core);
}
return *this;
}
template<typename ConnectionBase>
pooled_connection<ConnectionBase>::pooled_connection(_handle_ptr_t&& handle, _pool_core_ptr_t pool_core) :
ConnectionBase{std::move(handle)},
_pool_core{pool_core}
{
}
template<typename ConnectionBase>
pooled_connection<ConnectionBase>::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<typename ConnectionBase>
void pooled_connection<ConnectionBase>::conn_release()
{
if (_pool_core) {
_pool_core->put(ConnectionBase::_handle);
_pool_core = nullptr;
}
}
} // namespace sqlpp } // namespace sqlpp
#endif #endif

View File

@ -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 <mutex>
#include <sqlpp11/detail/circular_buffer.h>
namespace sqlpp
{
template<typename ConnectionBase>
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<ConnectionBase>;
class pool_core : public std::enable_shared_from_this<pool_core>
{
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<pool_core> _core;
};
template<typename ConnectionBase>
connection_pool<ConnectionBase>::pool_core::pool_core(const _config_ptr_t& connection_config, std::size_t capacity) :
_connection_config{connection_config},
_handles{capacity}
{
}
template<typename ConnectionBase>
typename connection_pool<ConnectionBase>::_pooled_connection_t connection_pool<ConnectionBase>::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<typename ConnectionBase>
void connection_pool<ConnectionBase>::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<typename ConnectionBase>
std::size_t connection_pool<ConnectionBase>::pool_core::available()
{
std::unique_lock lock{_mutex};
return _handles.size();
}
template<typename ConnectionBase>
connection_pool<ConnectionBase>::connection_pool(const _config_ptr_t& connection_config, std::size_t capacity) :
_core{std::make_shared<pool_core>(connection_config, capacity)}
{
}
template<typename ConnectionBase>
void connection_pool<ConnectionBase>::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<pool_core>(connection_config, capacity);
}
template<typename ConnectionBase>
typename connection_pool<ConnectionBase>::_pooled_connection_t connection_pool<ConnectionBase>::get()
{
return _core->get();
}
template<typename ConnectionBase>
std::size_t connection_pool<ConnectionBase>::available()
{
return _core->available();
}
} // namespace sqlpp

View File

@ -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 <algorithm>
#include <stdexcept>
#include <vector>
namespace sqlpp
{
namespace detail
{
// This class is modelled after boost::circular_buffer
template <typename T>
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<T> _data;
std::size_t _capacity;
std::size_t _size;
std::size_t _head;
std::size_t _tail;
void increment(std::size_t& pos);
};
template <typename T>
circular_buffer<T>::circular_buffer(std::size_t capacity) :
_data(capacity),
_capacity{capacity},
_size{0},
_head{0},
_tail{0}
{
}
template <typename T>
std::size_t circular_buffer<T>::capacity() const
{
return _capacity;
}
template <typename T>
void circular_buffer<T>::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 <typename T>
std::size_t circular_buffer<T>::size() const
{
return _size;
}
template <typename T>
bool circular_buffer<T>::empty() const
{
return _size == 0;
}
template <typename T>
bool circular_buffer<T>::full() const
{
return _size == _capacity;
}
template <typename T>
T& circular_buffer<T>::front()
{
if (empty()) {
throw std::runtime_error{"circular_buffer::front() called on empty buffer"};
}
return _data[_tail];
}
template <typename T>
void circular_buffer<T>::pop_front()
{
if (empty()) {
throw std::runtime_error{"circular_buffer::pop_front() called on empty buffer"};
}
_data[_tail] = {};
increment(_tail);
--_size;
}
template <typename T>
void circular_buffer<T>::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 <typename T>
void circular_buffer<T>::increment(std::size_t& pos)
{
pos = (pos + 1) % _capacity;
}
} // namespace detail
} // namespace sqlpp

View File

@ -51,7 +51,7 @@ namespace sqlpp
template <typename Db, template <typename Db,
typename Expr, typename Expr,
typename std::enable_if<not std::is_convertible<Expr, std::string>::value, int>::type = 0> typename std::enable_if<not std::is_convertible<Expr, std::string>::value, int>::type = 0>
auto eval(Db& db, Expr expr) -> typename eval_t<Db, Expr>::type auto eval(Db& db, Expr expr) -> typename eval_t<typename Db::_connection_base_t, Expr>::type
{ {
return db(select(expr.as(alias::a))).front().a; return db(select(expr.as(alias::a))).front().a;
} }

View File

@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2013 - 2017, Roland Bock * Copyright (c) 2013 - 2017, Roland Bock
* Copyright (c) 2023, Vesselin Atanasov
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without modification, * Redistribution and use in source and binary forms, with or without modification,
@ -104,11 +105,12 @@ namespace sqlpp
struct connection_handle_t struct connection_handle_t
{ {
std::shared_ptr<connection_config> config; std::shared_ptr<const connection_config> config;
std::unique_ptr<MYSQL, void (*)(MYSQL*)> mysql; std::unique_ptr<MYSQL, void (*)(MYSQL*)> mysql;
connection_handle_t(const std::shared_ptr<connection_config>& conf) connection_handle_t(const std::shared_ptr<const connection_config>& conf) :
: config(conf), mysql(mysql_init(nullptr), handle_cleanup) config(conf),
mysql(mysql_init(nullptr), handle_cleanup)
{ {
if (not mysql) if (not mysql)
{ {
@ -118,43 +120,48 @@ namespace sqlpp
if (config->auto_reconnect) if (config->auto_reconnect)
{ {
my_bool my_true = true; 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"); 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(const connection_handle_t&) = delete;
connection_handle_t(connection_handle_t&&) = default; connection_handle_t(connection_handle_t&&) = default;
connection_handle_t& operator=(const connection_handle_t&) = delete; connection_handle_t& operator=(const connection_handle_t&) = delete;
connection_handle_t& operator=(connection_handle_t&&) = default; 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() 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<connection_handle_t>& handle, const std::string& statement)
{ {
thread_init(); thread_init();
if (handle.config->debug) if (handle->config->debug)
std::cerr << "MySQL debug: Executing: '" << statement << "'" << std::endl; 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( 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"); " (statement was >>" + statement + "<<\n");
} }
} }
@ -179,18 +186,18 @@ namespace sqlpp
} }
} }
inline std::shared_ptr<detail::prepared_statement_handle_t> prepare_statement(detail::connection_handle_t& handle, inline std::shared_ptr<detail::prepared_statement_handle_t> prepare_statement(std::unique_ptr<connection_handle_t>& handle,
const std::string& statement, const std::string& statement,
size_t no_of_parameters, size_t no_of_parameters,
size_t no_of_columns) size_t no_of_columns)
{ {
thread_init(); thread_init();
if (handle.config->debug) if (handle->config->debug)
std::cerr << "MySQL debug: Preparing: '" << statement << "'" << std::endl; std::cerr << "MySQL debug: Preparing: '" << statement << "'" << std::endl;
auto prepared_statement = std::make_shared<detail::prepared_statement_handle_t>( auto prepared_statement = std::make_shared<detail::prepared_statement_handle_t>(
mysql_stmt_init(handle.mysql.get()), no_of_parameters, no_of_columns, handle.config->debug); mysql_stmt_init(handle->native_handle()), no_of_parameters, no_of_columns, handle->config->debug);
if (not prepared_statement) if (not prepared_statement)
{ {
throw sqlpp::exception("MySQL error: Could not allocate prepared statement\n"); 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())) if (mysql_stmt_prepare(prepared_statement->mysql_stmt, statement.data(), statement.size()))
{ {
throw sqlpp::exception( 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"); " (statement was >>" + statement + "<<\n");
} }
@ -226,13 +233,15 @@ namespace sqlpp
static const auto global_init_and_end = scoped_library_initializer_t(argc, argv, groups); static const auto global_init_and_end = scoped_library_initializer_t(argc, argv, groups);
} }
class connection; // Forward declaration
class connection_base;
struct serializer_t 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 <typename T> template <typename T>
std::ostream& operator<<(T t) std::ostream& operator<<(T t)
@ -247,7 +256,7 @@ namespace sqlpp
return _os.str(); return _os.str();
} }
const connection& _db; const connection_base& _db;
sqlpp::detail::float_safe_ostringstream _os; sqlpp::detail::float_safe_ostringstream _os;
}; };
@ -255,9 +264,9 @@ namespace sqlpp
std::integral_constant<char, '`'> get_quote_right(const serializer_t&); std::integral_constant<char, '`'> 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; bool _transaction_active = false;
// direct execution // direct execution
@ -265,11 +274,11 @@ namespace sqlpp
{ {
execute_statement(_handle, statement); execute_statement(_handle, statement);
std::unique_ptr<detail::result_handle> result_handle( std::unique_ptr<detail::result_handle> 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) if (!*result_handle)
{ {
throw sqlpp::exception("MySQL error: Could not store result set: " + 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)}; return {std::move(result_handle)};
@ -279,19 +288,19 @@ namespace sqlpp
{ {
execute_statement(_handle, statement); 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) size_t update_impl(const std::string& statement)
{ {
execute_statement(_handle, 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) size_t remove_impl(const std::string& statement)
{ {
execute_statement(_handle, statement); execute_statement(_handle, statement);
return mysql_affected_rows(_handle.mysql.get()); return mysql_affected_rows(_handle->native_handle());
} }
// prepared execution // prepared execution
@ -325,6 +334,12 @@ namespace sqlpp
} }
public: public:
using _connection_base_t = connection_base;
using _config_t = connection_config;
using _config_ptr_t = std::shared_ptr<const _config_t>;
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 _prepared_statement_t = ::sqlpp::mysql::prepared_statement_t;
using _context_t = serializer_t; using _context_t = serializer_t;
using _serializer_context_t = _context_t; using _serializer_context_t = _context_t;
@ -347,30 +362,19 @@ namespace sqlpp
return serialize(t, context); return serialize(t, context);
} }
connection(const std::shared_ptr<connection_config>& config) : _handle{config} bool is_valid() const
{ {
} return _handle->check_connection();
~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();
} }
void reconnect() void reconnect()
{ {
return _handle.reconnect(); return _handle->reconnect();
} }
const std::shared_ptr<connection_config>& get_config() const std::shared_ptr<const connection_config>& get_config()
{ {
return _handle.config; return _handle->config;
} }
bool is_transaction_active() bool is_transaction_active()
@ -483,7 +487,7 @@ namespace sqlpp
std::string escape(const std::string& s) const std::string escape(const std::string& s) const
{ {
std::unique_ptr<char[]> dest(new char[s.size() * 2 + 1]); std::unique_ptr<char[]> dest(new char[s.size() * 2 + 1]);
mysql_real_escape_string(_handle.mysql.get(), dest.get(), s.c_str(), s.size()); mysql_real_escape_string(_handle->native_handle(), dest.get(), s.c_str(), s.size());
return dest.get(); return dest.get();
} }
@ -569,16 +573,35 @@ namespace sqlpp
std::cerr << "MySQL message:" << message << std::endl; std::cerr << "MySQL message:" << message << std::endl;
} }
MYSQL* native_handle()
{
return _handle->native_handle();
}
// Kept for compatibility with old code
MYSQL* get_handle() 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) inline std::string serializer_t::escape(std::string arg)
{ {
return _db.escape(arg); return _db.escape(arg);
} }
using connection = sqlpp::normal_connection<connection_base>;
using pooled_connection = sqlpp::pooled_connection<connection_base>;
} // namespace mysql } // namespace mysql
} // namespace sqlpp } // namespace sqlpp

View File

@ -33,10 +33,8 @@ namespace sqlpp
{ {
namespace mysql namespace mysql
{ {
class connection;
struct connection_config struct connection_config
{ {
typedef ::sqlpp::mysql::connection connection;
std::string host = "localhost"; std::string host = "localhost";
std::string user; std::string user;
std::string password; std::string password;

View File

@ -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 <sqlpp11/connection_pool.h>
#include <sqlpp11/mysql/connection.h>
namespace sqlpp
{
namespace mysql
{
using connection_pool = sqlpp::connection_pool<connection_base>;
} // namespace mysql
} // namespace sqlpp

View File

@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2013 - 2015, Roland Bock * Copyright (c) 2013 - 2015, Roland Bock
* Copyright (c) 2023, Vesselin Atanasov
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without modification, * Redistribution and use in source and binary forms, with or without modification,
@ -28,6 +29,7 @@
#define SQLPP_MYSQL_H #define SQLPP_MYSQL_H
#include <sqlpp11/mysql/connection.h> #include <sqlpp11/mysql/connection.h>
#include <sqlpp11/mysql/connection_pool.h>
#include <sqlpp11/mysql/char_result.h> #include <sqlpp11/mysql/char_result.h>
#endif #endif

View File

@ -41,11 +41,11 @@ namespace sqlpp
{ {
namespace mysql namespace mysql
{ {
class connection; class connection_base;
class prepared_statement_t class prepared_statement_t
{ {
friend ::sqlpp::mysql::connection; friend ::sqlpp::mysql::connection_base;
std::shared_ptr<detail::prepared_statement_handle_t> _handle; std::shared_ptr<detail::prepared_statement_handle_t> _handle;
public: public:

View File

@ -1,5 +1,6 @@
/** /**
* Copyright © 2014-2015, Matthijs Möhlmann * Copyright © 2014-2015, Matthijs Möhlmann
* Copyright (c) 2023, Vesselin Atanasov
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -63,39 +64,39 @@ namespace sqlpp
namespace detail namespace detail
{ {
// Forward declaration // Forward declaration
inline std::unique_ptr<detail::prepared_statement_handle_t> prepare_statement(detail::connection_handle& handle, inline std::unique_ptr<detail::prepared_statement_handle_t> prepare_statement(std::unique_ptr<connection_handle>& handle,
const std::string& stmt, const std::string& stmt,
const size_t& paramCount) const size_t& paramCount)
{ {
if (handle.config->debug) if (handle->config->debug)
{ {
std::cerr << "PostgreSQL debug: preparing: " << stmt << std::endl; std::cerr << "PostgreSQL debug: preparing: " << stmt << std::endl;
} }
return std::unique_ptr<detail::prepared_statement_handle_t>(new detail::prepared_statement_handle_t return std::unique_ptr<detail::prepared_statement_handle_t>(new detail::prepared_statement_handle_t
(handle, stmt, paramCount)); (*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<connection_handle>& handle, std::shared_ptr<detail::prepared_statement_handle_t>& 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 // Forward declaration
class connection; class connection_base;
// Context // Context
struct context_t 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 <typename T> template <typename T>
std::ostream& operator<<(T t) std::ostream& operator<<(T t)
@ -108,7 +109,7 @@ namespace sqlpp
return _os << (t ? "TRUE" : "FALSE"); return _os << (t ? "TRUE" : "FALSE");
} }
std::string escape(const std::string& arg); std::string escape(const std::string& arg) const;
std::string str() const std::string str() const
{ {
@ -125,23 +126,23 @@ namespace sqlpp
++_count; ++_count;
} }
const connection& _db; const connection_base& _db;
sqlpp::detail::float_safe_ostringstream _os; sqlpp::detail::float_safe_ostringstream _os;
size_t _count{1}; size_t _count{1};
}; };
// Connection // Base connection class
class connection : public sqlpp::connection class connection_base : public sqlpp::connection
{ {
private: private:
std::unique_ptr<detail::connection_handle> _handle;
bool _transaction_active{false}; bool _transaction_active{false};
void validate_connection_handle() const void validate_connection_handle() const
{ {
if (!_handle) if (!_handle) {
throw std::logic_error("connection handle used, but not initialized"); throw std::logic_error("connection handle used, but not initialized");
} }
}
// direct execution // direct execution
bind_result_t select_impl(const std::string& stmt); bind_result_t select_impl(const std::string& stmt);
@ -158,6 +159,12 @@ namespace sqlpp
size_t run_prepared_remove_impl(prepared_statement_t& prep); size_t run_prepared_remove_impl(prepared_statement_t& prep);
public: public:
using _connection_base_t = connection_base;
using _config_t = connection_config;
using _config_ptr_t = std::shared_ptr<const _config_t>;
using _handle_t = detail::connection_handle;
using _handle_ptr_t = std::unique_ptr<_handle_t>;
using _prepared_statement_t = prepared_statement_t; using _prepared_statement_t = prepared_statement_t;
using _context_t = context_t; using _context_t = context_t;
using _serializer_context_t = _context_t; using _serializer_context_t = _context_t;
@ -180,18 +187,6 @@ namespace sqlpp
return ::sqlpp::serialize(t, context); return ::sqlpp::serialize(t, context);
} }
// ctor / dtor
connection();
connection(const std::shared_ptr<const connection_config>& 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<const connection_config>& config) noexcept(false);
// Select stmt (returns a result) // Select stmt (returns a result)
template <typename Select> template <typename Select>
bind_result_t select(const Select& s) bind_result_t select(const Select& s)
@ -384,45 +379,19 @@ namespace sqlpp
//! get the last inserted id for a certain table //! get the last inserted id for a certain table
uint64_t last_insert_id(const std::string& table, const std::string& fieldname); 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 std::shared_ptr<detail::statement_handle_t> connection_base::execute(const std::string& stmt)
{
}
inline connection::connection(const std::shared_ptr<const connection_config>& 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<const connection_config>& config) noexcept(false)
{
this->_handle.reset(new detail::connection_handle(config));
}
inline std::shared_ptr<detail::statement_handle_t> connection::execute(const std::string& stmt)
{ {
validate_connection_handle(); validate_connection_handle();
if (_handle->config->debug) if (_handle->config->debug)
@ -431,75 +400,76 @@ namespace sqlpp
} }
auto result = std::make_shared<detail::statement_handle_t>(*_handle); auto result = std::make_shared<detail::statement_handle_t>(*_handle);
result->result = PQexec(_handle->native(), stmt.c_str()); result->result = PQexec(native_handle(), stmt.c_str());
result->valid = true; result->valid = true;
return result; return result;
} }
// direct execution // 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); 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<size_t>(execute(stmt)->result.affected_rows()); return static_cast<size_t>(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<size_t>(execute(stmt)->result.affected_rows()); return static_cast<size_t>(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<size_t>(execute(stmt)->result.affected_rows()); return static_cast<size_t>(execute(stmt)->result.affected_rows());
} }
// prepared execution // 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(); 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(); validate_connection_handle();
execute_prepared_statement(*_handle, *prep._handle.get()); execute_prepared_statement(_handle, prep._handle);
return {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(); validate_connection_handle();
execute_prepared_statement(*_handle, *prep._handle.get()); execute_prepared_statement(_handle, prep._handle);
return static_cast<size_t>(prep._handle->result.affected_rows()); return static_cast<size_t>(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(); validate_connection_handle();
execute_prepared_statement(*_handle, *prep._handle.get()); execute_prepared_statement(_handle, prep._handle);
return static_cast<size_t>(prep._handle->result.affected_rows()); return static_cast<size_t>(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(); validate_connection_handle();
execute_prepared_statement(*_handle, *prep._handle.get()); execute_prepared_statement(_handle, prep._handle);
return static_cast<size_t>(prep._handle->result.affected_rows()); return static_cast<size_t>(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(); validate_connection_handle();
execute_prepared_statement(*_handle, *prep._handle.get()); execute_prepared_statement(_handle, prep._handle);
return static_cast<size_t>(prep._handle->result.affected_rows()); return static_cast<size_t>(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"; std::string level_str = "read uncommmitted";
switch (level) switch (level)
@ -524,7 +494,8 @@ namespace sqlpp
execute(cmd); 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 res = execute("SHOW default_transaction_isolation;");
auto status = res->result.status(); auto status = res->result.status();
@ -554,7 +525,7 @@ namespace sqlpp
} }
// TODO: Fix escaping. // 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(); validate_connection_handle();
// Escape strings // Escape strings
@ -562,13 +533,13 @@ namespace sqlpp
result.resize((s.size() * 2) + 1); result.resize((s.size() * 2) + 1);
int err; 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); result.resize(length);
return result; return result;
} }
//! start transaction //! start transaction
inline void connection::start_transaction(sqlpp::isolation_level level) inline void connection_base::start_transaction(isolation_level level)
{ {
if (_transaction_active) if (_transaction_active)
{ {
@ -606,28 +577,28 @@ namespace sqlpp
} }
//! create savepoint //! create savepoint
inline void connection::savepoint(const std::string& name) inline void connection_base::savepoint(const std::string& name)
{ {
/// NOTE prevent from sql injection? /// NOTE prevent from sql injection?
execute("SAVEPOINT " + name); execute("SAVEPOINT " + name);
} }
//! ROLLBACK TO SAVEPOINT //! 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? /// NOTE prevent from sql injection?
execute("ROLLBACK TO SAVEPOINT " + name); execute("ROLLBACK TO SAVEPOINT " + name);
} }
//! release_savepoint //! 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? /// NOTE prevent from sql injection?
execute("RELEASE SAVEPOINT " + name); execute("RELEASE SAVEPOINT " + name);
} }
//! commit transaction (or throw transaction if transaction has finished already) //! commit transaction (or throw transaction if transaction has finished already)
inline void connection::commit_transaction() inline void connection_base::commit_transaction()
{ {
if (!_transaction_active) if (!_transaction_active)
{ {
@ -639,7 +610,7 @@ namespace sqlpp
} }
//! rollback transaction //! rollback transaction
inline void connection::rollback_transaction(bool report) inline void connection_base::rollback_transaction(bool report)
{ {
if (!_transaction_active) if (!_transaction_active)
{ {
@ -655,15 +626,15 @@ namespace sqlpp
} }
//! report rollback failure //! 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; 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')"; 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) if (PQresultStatus(res) != PGRES_TUPLES_OK)
{ {
std::string err{PQresultErrorMessage(res)}; std::string err{PQresultErrorMessage(res)};
@ -677,17 +648,20 @@ namespace sqlpp
return std::stoul(in); 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); return _db.escape(arg);
} }
}
} using connection = sqlpp::normal_connection<connection_base>;
using pooled_connection = sqlpp::pooled_connection<connection_base>;
} // namespace postgresql
} // namespace sqlpp
#include <sqlpp11/postgresql/serializer.h> #include <sqlpp11/postgresql/serializer.h>

View File

@ -35,12 +35,8 @@ namespace sqlpp
{ {
namespace postgresql namespace postgresql
{ {
class connection;
struct DLL_PUBLIC connection_config struct DLL_PUBLIC connection_config
{ {
// Needed for the connection pool
typedef ::sqlpp::postgresql::connection connection;
enum class sslmode_t enum class sslmode_t
{ {
disable, disable,

View File

@ -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 <sqlpp11/connection_pool.h>
#include <sqlpp11/postgresql/connection.h>
namespace sqlpp
{
namespace postgresql
{
using connection_pool = sqlpp::connection_pool<connection_base>;
} // namespace postgresql
} // namespace sqlpp

View File

@ -67,16 +67,13 @@ namespace sqlpp
connection_handle(const std::shared_ptr<const connection_config>& config); connection_handle(const std::shared_ptr<const connection_config>& config);
connection_handle(const connection_handle&) = delete; connection_handle(const connection_handle&) = delete;
connection_handle(connection_handle&&) = default; connection_handle(connection_handle&&) = default;
~connection_handle();
connection_handle& operator=(const connection_handle&) = delete; connection_handle& operator=(const connection_handle&) = delete;
connection_handle& operator=(connection_handle&&) = default; connection_handle& operator=(connection_handle&&) = default;
~connection_handle();
PGconn* native() const
{
return postgres.get();
}
void deallocate_prepared_statement(const std::string& name); 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<const connection_config>& conf) inline connection_handle::connection_handle(const std::shared_ptr<const connection_config>& conf)
@ -206,9 +203,9 @@ namespace sqlpp
if (!postgres) if (!postgres)
throw std::bad_alloc(); 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)); throw broken_connection(std::move(msg));
} }
} }
@ -225,10 +222,21 @@ namespace sqlpp
inline void connection_handle::deallocate_prepared_statement(const std::string& name) inline void connection_handle::deallocate_prepared_statement(const std::string& name)
{ {
std::string cmd = "DEALLOCATE \"" + name + "\""; std::string cmd = "DEALLOCATE \"" + name + "\"";
PGresult* result = PQexec(postgres.get(), cmd.c_str()); PGresult* result = PQexec(native_handle(), cmd.c_str());
PQclear(result); PQclear(result);
prepared_statement_names.erase(name); 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);
}
} }
} }
} }

View File

@ -164,7 +164,7 @@ namespace sqlpp
valid = false; valid = false;
count = 0; count = 0;
totalCount = 0; totalCount = 0;
result = PQexecPrepared(connection.native(), _name.data(), static_cast<int>(size), values.data(), nullptr, nullptr, 0); result = PQexecPrepared(connection.native_handle(), _name.data(), static_cast<int>(size), values.data(), nullptr, nullptr, 0);
/// @todo validate result? is it really valid /// @todo validate result? is it really valid
valid = true; valid = true;
} }
@ -188,7 +188,7 @@ namespace sqlpp
inline void prepared_statement_handle_t::prepare(std::string stmt) inline void prepared_statement_handle_t::prepare(std::string stmt)
{ {
// Create the prepared statement // 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; valid = true;
} }
} }

View File

@ -1,5 +1,6 @@
/** /**
* Copyright © 2014-2015, Matthijs Möhlmann * Copyright © 2014-2015, Matthijs Möhlmann
* Copyright (c) 2023, Vesselin Atanasov
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -29,6 +30,7 @@
#define SQLPP_POSTGRESQL_H #define SQLPP_POSTGRESQL_H
#include <sqlpp11/postgresql/connection.h> #include <sqlpp11/postgresql/connection.h>
#include <sqlpp11/postgresql/connection_pool.h>
#include <sqlpp11/postgresql/exception.h> #include <sqlpp11/postgresql/exception.h>
#include <sqlpp11/postgresql/insert.h> #include <sqlpp11/postgresql/insert.h>
#include <sqlpp11/postgresql/update.h> #include <sqlpp11/postgresql/update.h>

View File

@ -44,7 +44,7 @@ namespace sqlpp
#endif #endif
// Forward declaration // Forward declaration
class connection; class connection_base;
// Detail namespace // Detail namespace
namespace detail namespace detail
@ -54,9 +54,9 @@ namespace sqlpp
class prepared_statement_t class prepared_statement_t
{ {
friend sqlpp::postgresql::connection;
private: private:
friend class sqlpp::postgresql::connection_base;
std::shared_ptr<detail::prepared_statement_handle_t> _handle; std::shared_ptr<detail::prepared_statement_handle_t> _handle;
public: public:

View File

@ -40,7 +40,8 @@ namespace sqlpp
{ {
namespace postgresql namespace postgresql
{ {
class connection; // Forward declaration
class connection_base;
} }
namespace detail namespace detail
@ -93,8 +94,8 @@ namespace sqlpp
} // namespace detail } // namespace detail
template <typename NameType, bool CanBeNull> template <typename NameType, bool CanBeNull>
struct result_field_t<postgresql::connection, field_spec_t<NameType, blob, CanBeNull>> struct result_field_t<postgresql::connection_base, field_spec_t<NameType, blob, CanBeNull>>
: public result_field_base<postgresql::connection, field_spec_t<NameType, blob, CanBeNull>> : public result_field_base<postgresql::connection_base, field_spec_t<NameType, blob, CanBeNull>>
{ {
private: private:
const uint8_t* _blob{nullptr}; // Non-owning const uint8_t* _blob{nullptr}; // Non-owning

View File

@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2013 - 2016, Roland Bock * Copyright (c) 2013 - 2016, Roland Bock
* Copyright (c) 2023, Vesselin Atanasov
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without modification, * Redistribution and use in source and binary forms, with or without modification,
@ -72,18 +73,20 @@ namespace sqlpp
struct connection_handle struct connection_handle
{ {
connection_config config; std::shared_ptr<const connection_config> config;
std::unique_ptr<::sqlite3, void (*)(::sqlite3*)> sqlite; std::unique_ptr<::sqlite3, void (*)(::sqlite3*)> sqlite;
connection_handle(connection_config conf) : config(conf), sqlite(nullptr, handle_cleanup) connection_handle(const std::shared_ptr<const connection_config>& conf) :
config(conf),
sqlite(nullptr, handle_cleanup)
{ {
#ifdef SQLPP_DYNAMIC_LOADING #ifdef SQLPP_DYNAMIC_LOADING
init_sqlite(""); init_sqlite("");
#endif #endif
::sqlite3* sqlite_ptr; ::sqlite3* sqlite_ptr;
const auto rc = sqlite3_open_v2(conf.path_to_database.c_str(), &sqlite_ptr, conf.flags, const auto rc = sqlite3_open_v2(conf->path_to_database.c_str(), &sqlite_ptr, conf->flags,
conf.vfs.empty() ? nullptr : conf.vfs.c_str()); conf->vfs.empty() ? nullptr : conf->vfs.c_str());
if (rc != SQLITE_OK) if (rc != SQLITE_OK)
{ {
const std::string msg = sqlite3_errmsg(sqlite_ptr); const std::string msg = sqlite3_errmsg(sqlite_ptr);
@ -94,13 +97,13 @@ namespace sqlpp
sqlite.reset(sqlite_ptr); sqlite.reset(sqlite_ptr);
#ifdef SQLITE_HAS_CODEC #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) if (ret != SQLITE_OK)
{ {
const std::string msg = sqlite3_errmsg(sqlite.get()); const std::string msg = sqlite3_errmsg(native_handle());
sqlite3_close(sqlite.get()); sqlite3_close(native_handle());
throw sqlpp::exception("Sqlite3 error: Can't set password to database: " + msg); 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(connection_handle&&) = default;
connection_handle& operator=(const connection_handle&) = delete; connection_handle& operator=(const connection_handle&) = delete;
connection_handle& operator=(connection_handle&&) = default; 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<connection_handle>& handle,
const std::string& statement) const std::string& statement)
{ {
if (handle.config.debug) if (handle->config->debug)
std::cerr << "Sqlite3 debug: Preparing: '" << statement << "'" << std::endl; 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<int>(statement.size()), auto rc = sqlite3_prepare_v2(handle->native_handle(), statement.c_str(), static_cast<int>(statement.size()),
&result.sqlite_statement, nullptr); &result.sqlite_statement, nullptr);
if (rc != SQLITE_OK) if (rc != SQLITE_OK)
{ {
throw sqlpp::exception( 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"); " (statement was >>" + (rc == SQLITE_TOOBIG ? statement.substr(0, 128) + "..." : statement) + "<<\n");
} }
return result; return result;
} }
inline void execute_statement(detail::connection_handle& handle, detail::prepared_statement_handle_t& prepared) inline void execute_statement(std::unique_ptr<connection_handle>& handle, detail::prepared_statement_handle_t& prepared)
{ {
auto rc = sqlite3_step(prepared.sqlite_statement); auto rc = sqlite3_step(prepared.sqlite_statement);
switch (rc) switch (rc)
@ -145,19 +157,20 @@ namespace sqlpp
case SQLITE_DONE: case SQLITE_DONE:
return; return;
default: default:
if (handle.config.debug) if (handle->config->debug)
std::cerr << "Sqlite3 debug: sqlite3_step return code: " << rc << std::endl; std::cerr << "Sqlite3 debug: sqlite3_step return code: " << rc << std::endl;
throw sqlpp::exception("Sqlite3 error: Could not execute statement: " + 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 } // namespace detail
class connection; // Forward declaration
class connection_base;
struct serializer_t 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; ++_count;
} }
const connection& _db; const connection_base& _db;
sqlpp::detail::float_safe_ostringstream _os; sqlpp::detail::float_safe_ostringstream _os;
size_t _count; 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 enum class transaction_status_type
{ {
none, none,
@ -219,21 +233,21 @@ namespace sqlpp
auto prepared = prepare_statement(_handle, statement); auto prepared = prepare_statement(_handle, statement);
execute_statement(_handle, prepared); execute_statement(_handle, prepared);
return static_cast<size_t>(sqlite3_last_insert_rowid(_handle.sqlite.get())); return static_cast<size_t>(sqlite3_last_insert_rowid(native_handle()));
} }
size_t update_impl(const std::string& statement) size_t update_impl(const std::string& statement)
{ {
auto prepared = prepare_statement(_handle, statement); auto prepared = prepare_statement(_handle, statement);
execute_statement(_handle, prepared); execute_statement(_handle, prepared);
return static_cast<size_t>(sqlite3_changes(_handle.sqlite.get())); return static_cast<size_t>(sqlite3_changes(native_handle()));
} }
size_t remove_impl(const std::string& statement) size_t remove_impl(const std::string& statement)
{ {
auto prepared = prepare_statement(_handle, statement); auto prepared = prepare_statement(_handle, statement);
execute_statement(_handle, prepared); execute_statement(_handle, prepared);
return static_cast<size_t>(sqlite3_changes(_handle.sqlite.get())); return static_cast<size_t>(sqlite3_changes(native_handle()));
} }
// prepared execution // prepared execution
@ -252,31 +266,37 @@ namespace sqlpp
{ {
execute_statement(_handle, *prepared_statement._handle.get()); execute_statement(_handle, *prepared_statement._handle.get());
return static_cast<size_t>(sqlite3_last_insert_rowid(_handle.sqlite.get())); return static_cast<size_t>(sqlite3_last_insert_rowid(native_handle()));
} }
size_t run_prepared_update_impl(prepared_statement_t& prepared_statement) size_t run_prepared_update_impl(prepared_statement_t& prepared_statement)
{ {
execute_statement(_handle, *prepared_statement._handle.get()); execute_statement(_handle, *prepared_statement._handle.get());
return static_cast<size_t>(sqlite3_changes(_handle.sqlite.get())); return static_cast<size_t>(sqlite3_changes(native_handle()));
} }
size_t run_prepared_remove_impl(prepared_statement_t& prepared_statement) size_t run_prepared_remove_impl(prepared_statement_t& prepared_statement)
{ {
execute_statement(_handle, *prepared_statement._handle.get()); execute_statement(_handle, *prepared_statement._handle.get());
return static_cast<size_t>(sqlite3_changes(_handle.sqlite.get())); return static_cast<size_t>(sqlite3_changes(native_handle()));
} }
size_t run_prepared_execute_impl(prepared_statement_t& prepared_statement) size_t run_prepared_execute_impl(prepared_statement_t& prepared_statement)
{ {
execute_statement(_handle, *prepared_statement._handle.get()); execute_statement(_handle, *prepared_statement._handle.get());
return static_cast<size_t>(sqlite3_changes(_handle.sqlite.get())); return static_cast<size_t>(sqlite3_changes(native_handle()));
} }
public: public:
using _connection_base_t = connection_base;
using _config_t = connection_config;
using _config_ptr_t = std::shared_ptr<const connection_config>;
using _handle_t = detail::connection_handle;
using _handle_ptr_t = std::unique_ptr<_handle_t>;
using _prepared_statement_t = prepared_statement_t; using _prepared_statement_t = prepared_statement_t;
using _context_t = serializer_t; using _context_t = serializer_t;
using _serializer_context_t = _context_t; using _serializer_context_t = _context_t;
@ -299,18 +319,6 @@ namespace sqlpp
return ::sqlpp::serialize(t, context); 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) //! select returns a result (which can be iterated row by row)
template <typename Select> template <typename Select>
bind_result_t select(const Select& s) bind_result_t select(const Select& s)
@ -416,7 +424,7 @@ namespace sqlpp
{ {
auto prepared = prepare_statement(_handle, statement); auto prepared = prepare_statement(_handle, statement);
execute_statement(_handle, prepared); execute_statement(_handle, prepared);
return static_cast<size_t>(sqlite3_changes(_handle.sqlite.get())); return static_cast<size_t>(sqlite3_changes(native_handle()));
} }
template < template <
@ -573,12 +581,12 @@ namespace sqlpp
//! get the last inserted id //! get the last inserted id
uint64_t last_insert_id() noexcept uint64_t last_insert_id() noexcept
{ {
return static_cast<size_t>(sqlite3_last_insert_rowid(_handle.sqlite.get())); return static_cast<size_t>(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) schema_t attach(const connection_config& config, const std::string name)
@ -589,12 +597,25 @@ namespace sqlpp
return {name}; 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) inline std::string serializer_t::escape(std::string arg)
{ {
return _db.escape(arg); return _db.escape(arg);
} }
using connection = sqlpp::normal_connection<connection_base>;
using pooled_connection = sqlpp::pooled_connection<connection_base>;
} // namespace sqlite3 } // namespace sqlite3
} // namespace sqlpp } // namespace sqlpp

View File

@ -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 <sqlpp11/connection_pool.h>
#include <sqlpp11/sqlite3/connection.h>
namespace sqlpp
{
namespace sqlite3
{
using connection_pool = sqlpp::connection_pool<connection_base>;
} // namespace sqlite3
} // namespace sqlpp

View File

@ -49,6 +49,9 @@ namespace sqlpp
{ {
namespace sqlite3 namespace sqlite3
{ {
// Forward declaration
class connection_base;
namespace detail namespace detail
{ {
inline void check_bind_result(int result, const char* const type) inline void check_bind_result(int result, const char* const type)
@ -70,11 +73,9 @@ namespace sqlpp
} }
} // namespace detail } // namespace detail
class connection;
class SQLPP11_SQLITE3_EXPORT prepared_statement_t class SQLPP11_SQLITE3_EXPORT prepared_statement_t
{ {
friend ::sqlpp::sqlite3::connection; friend class ::sqlpp::sqlite3::connection_base;
std::shared_ptr<detail::prepared_statement_handle_t> _handle; std::shared_ptr<detail::prepared_statement_handle_t> _handle;
public: public:

View File

@ -42,8 +42,6 @@ namespace sqlpp
{ {
namespace sqlite3 namespace sqlite3
{ {
class connection;
namespace detail namespace detail
{ {
struct prepared_statement_handle_t struct prepared_statement_handle_t

View File

@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2013 - 2015, Roland Bock * Copyright (c) 2013 - 2015, Roland Bock
* Copyright (c) 2023, Vesselin Atanasov
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without modification, * Redistribution and use in source and binary forms, with or without modification,
@ -28,6 +29,7 @@
#define SQLPP_SQLITE3_H #define SQLPP_SQLITE3_H
#include <sqlpp11/sqlite3/connection.h> #include <sqlpp11/sqlite3/connection.h>
#include <sqlpp11/sqlite3/connection_pool.h>
#include <sqlpp11/sqlite3/insert_or.h> #include <sqlpp11/sqlite3/insert_or.h>
#endif #endif

View File

@ -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 <random>
#include <set>
#include <thread>
#include <unordered_set>
#include "TabDepartment.h"
namespace sqlpp
{
namespace test
{
namespace
{
template<typename Pool>
using native_type = std::decay_t<decltype(std::declval<Pool>().get().native_handle())>;
template<typename Pool>
using native_set = std::unordered_set<native_type<Pool>>;
template<typename Pool>
using pool_conn_type = std::decay_t<decltype(std::declval<Pool>().get())>;
template<typename Pool>
native_set<Pool> get_native_handles(Pool& pool)
{
native_set<Pool> 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 <typename Pool>
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 <typename Pool>
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 <typename Pool>
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 <typename Pool>
void test_multiple_connections(Pool& pool)
{
try
{
model::TabDepartment tabDept = {};
auto connections = std::vector<std::decay_t<decltype(pool.get())>>{};
auto pointers = std::set<void*>{};
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 <typename Pool>
void test_multithreaded(Pool& pool)
{
std::random_device r;
std::default_random_engine random_engine(r());
std::uniform_int_distribution<int> 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<std::thread>{};
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 <typename Pool>
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<Pool>(config, 5);
auto conn = pool->get();
pool = nullptr;
}
}
template <typename Pool>
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<Pool>(config);
}
} // namespace test
} // namespace sqlpp

View File

@ -0,0 +1,79 @@
#pragma once
#include <sqlpp11/table.h>
#include <sqlpp11/data_types.h>
#include <sqlpp11/char_sequence.h>
namespace model
{
namespace TabDepartment_
{
struct Id
{
struct _alias_t
{
static constexpr const char _literal[] = "id";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template<typename T>
struct _member_t
{
T id;
T& operator()() { return id; }
const T& operator()() const { return id; }
};
};
using _traits = sqlpp::make_traits<sqlpp::integer, sqlpp::tag::must_not_insert, sqlpp::tag::must_not_update, sqlpp::tag::can_be_null>;
};
struct Name
{
struct _alias_t
{
static constexpr const char _literal[] = "name";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template<typename T>
struct _member_t
{
T name;
T& operator()() { return name; }
const T& operator()() const { return name; }
};
};
using _traits = sqlpp::make_traits<sqlpp::text, sqlpp::tag::can_be_null>;
};
struct Division
{
struct _alias_t
{
static constexpr const char _literal[] = "division";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template<typename T>
struct _member_t
{
T division;
T& operator()() { return division; }
const T& operator()() const { return division; }
};
};
using _traits = sqlpp::make_traits<sqlpp::text>;
};
} // namespace TabDepartment_
struct TabDepartment: sqlpp::table_t<TabDepartment,
TabDepartment_::Id,
TabDepartment_::Name,
TabDepartment_::Division>
{
struct _alias_t
{
static constexpr const char _literal[] = "tab_department";
using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>;
template<typename T>
struct _member_t
{
T tabDepartment;
T& operator()() { return tabDepartment; }
const T& operator()() const { return tabDepartment; }
};
};
};
} // namespace model

View File

@ -40,6 +40,7 @@ set(test_files
Truncated.cpp Truncated.cpp
Update.cpp Update.cpp
Remove.cpp Remove.cpp
ConnectionPool.cpp
) )
create_test_sourcelist(test_sources test_main.cpp ${test_files}) create_test_sourcelist(test_sources test_main.cpp ${test_files})

View File

@ -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 <sqlpp11/mysql/mysql.h>
#include <sqlpp11/sqlpp11.h>
#include "../../include/ConnectionPoolTests.h"
#include "make_test_connection.h"
namespace sql = ::sqlpp::mysql;
int ConnectionPool(int, char*[])
{
try
{
sqlpp::test::test_connection_pool<sql::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;
}

View File

@ -24,6 +24,7 @@
*/ */
#include <iostream> #include <iostream>
#include "make_test_connection.h"
#include "TabSample.h" #include "TabSample.h"
#include <sqlpp11/sqlpp11.h> #include <sqlpp11/sqlpp11.h>
#include <sqlpp11/custom_query.h> #include <sqlpp11/custom_query.h>
@ -59,28 +60,13 @@ namespace
const auto tab = TabSample{}; const auto tab = TabSample{};
namespace mysql = sqlpp::mysql; namespace sql = sqlpp::mysql;
int CustomQuery(int, char*[]) int CustomQuery(int, char*[])
{ {
mysql::global_library_init(); sql::global_library_init();
auto config = std::make_shared<mysql::connection_config>();
config->user = "root";
config->database = "sqlpp_mysql";
config->debug = true;
try try
{ {
mysql::connection db(config); auto db = sql::make_test_connection();
}
catch (const sqlpp::exception& e)
{
std::cerr << "For testing, you'll need to create a database sqlpp_mysql for user root (no password)" << std::endl;
std::cerr << e.what() << std::endl;
return 1;
}
try
{
mysql::connection db(config);
db.execute(R"(DROP TABLE IF EXISTS tab_sample)"); db.execute(R"(DROP TABLE IF EXISTS tab_sample)");
db.execute(R"(CREATE TABLE tab_sample ( db.execute(R"(CREATE TABLE tab_sample (
alpha bigint(20) AUTO_INCREMENT, alpha bigint(20) AUTO_INCREMENT,

View File

@ -23,6 +23,7 @@
* OF THE POSSIBILITY OF SUCH DAMAGE. * OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "make_test_connection.h"
#include "TabSample.h" #include "TabSample.h"
#include <sqlpp11/mysql/mysql.h> #include <sqlpp11/mysql/mysql.h>
#include <sqlpp11/sqlpp11.h> #include <sqlpp11/sqlpp11.h>
@ -68,32 +69,13 @@ namespace
} }
} }
namespace mysql = sqlpp::mysql; namespace sql = sqlpp::mysql;
int DateTime(int, char*[]) int DateTime(int, char*[])
{ {
auto config = std::make_shared<mysql::connection_config>(); sql::global_library_init();
config->user = "root";
config->database = "sqlpp_mysql";
config->debug = true;
try try
{ {
mysql::connection db(config); auto db = sql::make_test_connection();
}
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);
db.execute(R"(SET time_zone = '+00:00')"); // To force MySQL's CURRENT_TIMESTAMP into the right timezone 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"(DROP TABLE IF EXISTS tab_date_time)");
db.execute(R"(CREATE TABLE tab_date_time ( db.execute(R"(CREATE TABLE tab_date_time (

View File

@ -23,6 +23,7 @@
* OF THE POSSIBILITY OF SUCH DAMAGE. * OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "make_test_connection.h"
#include "TabSample.h" #include "TabSample.h"
#include <sqlpp11/alias_provider.h> #include <sqlpp11/alias_provider.h>
#include <sqlpp11/functions.h> #include <sqlpp11/functions.h>
@ -38,27 +39,13 @@
const auto library_raii = sqlpp::mysql::scoped_library_initializer_t{}; const auto library_raii = sqlpp::mysql::scoped_library_initializer_t{};
namespace mysql = sqlpp::mysql; namespace sql = sqlpp::mysql;
int DynamicSelect(int, char*[]) int DynamicSelect(int, char*[])
{ {
auto config = std::make_shared<mysql::connection_config>(); sql::global_library_init();
config->user = "root";
config->database = "sqlpp_mysql";
config->debug = true;
try try
{ {
mysql::connection db(config); auto db = sql::make_test_connection();
}
catch (const sqlpp::exception& e)
{
std::cerr << "For testing, you'll need to create a database sqlpp_mysql for user root (no password)" << std::endl;
std::cerr << e.what() << std::endl;
return 1;
}
try
{
mysql::connection db(config);
db.execute(R"(DROP TABLE IF EXISTS tab_sample)"); db.execute(R"(DROP TABLE IF EXISTS tab_sample)");
db.execute(R"(CREATE TABLE tab_sample ( db.execute(R"(CREATE TABLE tab_sample (
alpha bigint(20) DEFAULT NULL, alpha bigint(20) DEFAULT NULL,

View File

@ -23,6 +23,8 @@
* OF THE POSSIBILITY OF SUCH DAMAGE. * OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "make_test_connection.h"
#include <mysql.h> #include <mysql.h>
#include <iostream> #include <iostream>
@ -52,28 +54,13 @@ namespace test
SQLPP_ALIAS_PROVIDER(value) SQLPP_ALIAS_PROVIDER(value)
} }
namespace mysql = sqlpp::mysql; namespace sql = sqlpp::mysql;
int Json(int, char*[]) int Json(int, char*[])
{ {
mysql::global_library_init(); sql::global_library_init();
auto config = std::make_shared<mysql::connection_config>();
config->user = "root";
config->database = "sqlpp_mysql";
config->debug = true;
try try
{ {
mysql::connection db(config); auto db = sql::make_test_connection();
}
catch (const sqlpp::exception& e)
{
std::cerr << "For testing, you'll need to create a database sqlpp_mysql for user root (no password)" << std::endl;
std::cerr << e.what() << std::endl;
return 1;
}
try
{
mysql::connection db(config);
db.execute(R"(DROP TABLE IF EXISTS tab_json)"); db.execute(R"(DROP TABLE IF EXISTS tab_json)");
db.execute(R"(CREATE TABLE tab_json ( db.execute(R"(CREATE TABLE tab_json (
data JSON NOT NULL data JSON NOT NULL

View File

@ -23,6 +23,7 @@
* OF THE POSSIBILITY OF SUCH DAMAGE. * OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "make_test_connection.h"
#include "TabSample.h" #include "TabSample.h"
#include <sqlpp11/alias_provider.h> #include <sqlpp11/alias_provider.h>
#include <sqlpp11/functions.h> #include <sqlpp11/functions.h>
@ -37,29 +38,15 @@
#include <iostream> #include <iostream>
#include <vector> #include <vector>
namespace mysql = sqlpp::mysql; namespace sql = sqlpp::mysql;
int MoveConstructor(int, char*[]) int MoveConstructor(int, char*[])
{ {
mysql::global_library_init(); sql::global_library_init();
auto config = sql::make_test_config();
auto config = std::make_shared<mysql::connection_config>();
config->user = "root";
config->database = "sqlpp_mysql";
config->debug = true;
try try
{ {
mysql::connection db(config); std::vector<sql::connection> connections;
} connections.emplace_back(sql::connection(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<sqlpp::mysql::connection> connections;
connections.emplace_back(sqlpp::mysql::connection(config));
connections.at(0).execute(R"(DROP TABLE IF EXISTS tab_sample)"); connections.at(0).execute(R"(DROP TABLE IF EXISTS tab_sample)");
connections.at(0).execute(R"(CREATE TABLE tab_sample ( connections.at(0).execute(R"(CREATE TABLE tab_sample (

View File

@ -23,6 +23,7 @@
* OF THE POSSIBILITY OF SUCH DAMAGE. * OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "make_test_connection.h"
#include "TabSample.h" #include "TabSample.h"
#include <cassert> #include <cassert>
#include <sqlpp11/alias_provider.h> #include <sqlpp11/alias_provider.h>
@ -69,23 +70,10 @@ void testPreparedStatementResult(sql::connection& db)
int Prepared(int, char*[]) int Prepared(int, char*[])
{ {
auto config = std::make_shared<sql::connection_config>(); sql::global_library_init();
config->user = "root";
config->database = "sqlpp_mysql";
config->debug = true;
try try
{ {
sql::connection db(config); auto db = sql::make_test_connection();
}
catch (const sqlpp::exception& e)
{
std::cerr << "For testing, you'll need to create a database sqlpp_mysql for user root (no password)" << std::endl;
std::cerr << e.what() << std::endl;
return 1;
}
try
{
sql::connection db(config);
db.execute(R"(DROP TABLE IF EXISTS tab_sample)"); db.execute(R"(DROP TABLE IF EXISTS tab_sample)");
db.execute(R"(CREATE TABLE tab_sample ( db.execute(R"(CREATE TABLE tab_sample (
alpha bigint(20) AUTO_INCREMENT, alpha bigint(20) AUTO_INCREMENT,

View File

@ -23,6 +23,7 @@
* OF THE POSSIBILITY OF SUCH DAMAGE. * OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "make_test_connection.h"
#include "TabSample.h" #include "TabSample.h"
#include <sqlpp11/mysql/mysql.h> #include <sqlpp11/mysql/mysql.h>
#include <sqlpp11/sqlpp11.h> #include <sqlpp11/sqlpp11.h>
@ -36,23 +37,10 @@ namespace sql = sqlpp::mysql;
int Remove(int, char*[]) int Remove(int, char*[])
{ {
auto config = std::make_shared<sql::connection_config>(); sql::global_library_init();
config->user = "root";
config->database = "sqlpp_mysql";
config->debug = true;
try try
{ {
sql::connection db(config); auto db = sql::make_test_connection();
}
catch (const sqlpp::exception& e)
{
std::cerr << "For testing, you'll need to create a database sqlpp_mysql for user root (no password)" << std::endl;
std::cerr << e.what() << std::endl;
return 1;
}
try
{
sql::connection db(config);
db.execute(R"(DROP TABLE IF EXISTS tab_sample)"); db.execute(R"(DROP TABLE IF EXISTS tab_sample)");
db.execute(R"(CREATE TABLE tab_sample ( db.execute(R"(CREATE TABLE tab_sample (
alpha bigint(20) AUTO_INCREMENT, alpha bigint(20) AUTO_INCREMENT,

View File

@ -23,6 +23,7 @@
* OF THE POSSIBILITY OF SUCH DAMAGE. * OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "make_test_connection.h"
#include "TabSample.h" #include "TabSample.h"
#include <sqlpp11/mysql/mysql.h> #include <sqlpp11/mysql/mysql.h>
#include <sqlpp11/sqlpp11.h> #include <sqlpp11/sqlpp11.h>
@ -31,29 +32,13 @@
#include <iostream> #include <iostream>
#include <vector> #include <vector>
namespace mysql = sqlpp::mysql; namespace sql = sqlpp::mysql;
int Sample(int, char*[]) int Sample(int, char*[])
{ {
sqlpp::mysql::global_library_init(); sql::global_library_init();
auto config = std::make_shared<mysql::connection_config>();
config->user = "root";
config->database = "sqlpp_mysql";
config->debug = true;
config->connect_timeout_seconds = 5;
try try
{ {
mysql::connection db(config); auto db = sql::make_test_connection();
}
catch (const sqlpp::exception& e)
{
std::cerr << "For testing, you'll need to create a database sqlpp_mysql for user root (no password)" << std::endl;
std::cerr << e.what() << std::endl;
return 1;
}
try
{
mysql::connection db(config);
db.execute(R"(DROP TABLE IF EXISTS tab_sample)"); db.execute(R"(DROP TABLE IF EXISTS tab_sample)");
db.execute(R"(CREATE TABLE tab_sample ( db.execute(R"(CREATE TABLE tab_sample (
alpha bigint(20) AUTO_INCREMENT, alpha bigint(20) AUTO_INCREMENT,

View File

@ -23,6 +23,7 @@
* OF THE POSSIBILITY OF SUCH DAMAGE. * OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "make_test_connection.h"
#include "TabSample.h" #include "TabSample.h"
#include <cassert> #include <cassert>
#include <sqlpp11/alias_provider.h> #include <sqlpp11/alias_provider.h>
@ -83,23 +84,9 @@ void testSelectAll(sql::connection& db, int expectedRowCount)
int Select(int, char*[]) int Select(int, char*[])
{ {
auto config = std::make_shared<sql::connection_config>();
config->user = "root";
config->database = "sqlpp_mysql";
config->debug = true;
try try
{ {
sql::connection db(config); auto db = sql::make_test_connection();
}
catch (const sqlpp::exception& e)
{
std::cerr << "For testing, you'll need to create a database sqlpp_mysql for user root (no password)" << std::endl;
std::cerr << e.what() << std::endl;
return 1;
}
try
{
sql::connection db(config);
db.execute(R"(DROP TABLE IF EXISTS tab_sample)"); db.execute(R"(DROP TABLE IF EXISTS tab_sample)");
db.execute(R"(CREATE TABLE tab_sample ( db.execute(R"(CREATE TABLE tab_sample (
alpha bigint(20) AUTO_INCREMENT, alpha bigint(20) AUTO_INCREMENT,

View File

@ -23,6 +23,7 @@
* OF THE POSSIBILITY OF SUCH DAMAGE. * OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "make_test_connection.h"
#include "TabSample.h" #include "TabSample.h"
#include <cassert> #include <cassert>
#include <sqlpp11/alias_provider.h> #include <sqlpp11/alias_provider.h>
@ -44,24 +45,10 @@ const auto tab = TabSample{};
int Truncated(int, char*[]) int Truncated(int, char*[])
{ {
auto config = std::make_shared<sql::connection_config>(); sql::global_library_init();
config->user = "root";
config->database = "sqlpp_mysql";
config->debug = true;
config->charset = "utf8";
try try
{ {
sql::connection db(config); auto db = sql::make_test_connection();
}
catch (const sqlpp::exception& e)
{
std::cerr << "For testing, you'll need to create a database sqlpp_mysql for user root (no password)" << std::endl;
std::cerr << e.what() << std::endl;
return 1;
}
try
{
sql::connection db(config);
db.execute(R"(DROP TABLE IF EXISTS tab_sample)"); db.execute(R"(DROP TABLE IF EXISTS tab_sample)");
db.execute(R"(CREATE TABLE tab_sample ( db.execute(R"(CREATE TABLE tab_sample (
alpha bigint(20) AUTO_INCREMENT, alpha bigint(20) AUTO_INCREMENT,

View File

@ -23,6 +23,7 @@
* OF THE POSSIBILITY OF SUCH DAMAGE. * OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "make_test_connection.h"
#include "TabSample.h" #include "TabSample.h"
#include <sqlpp11/mysql/connection.h> #include <sqlpp11/mysql/connection.h>
#include <sqlpp11/sqlpp11.h> #include <sqlpp11/sqlpp11.h>
@ -36,23 +37,10 @@ const auto tab = TabSample{};
int Union(int, char*[]) int Union(int, char*[])
{ {
auto config = std::make_shared<sql::connection_config>(); sql::global_library_init();
config->user = "root";
config->database = "sqlpp_mysql";
config->debug = true;
try try
{ {
sql::connection db(config); auto db = sql::make_test_connection();
}
catch (const sqlpp::exception& e)
{
std::cerr << "For testing, you'll need to create a database sqlpp_mysql for user root (no password)" << std::endl;
std::cerr << e.what() << std::endl;
return 1;
}
try
{
sql::connection db(config);
db.execute(R"(DROP TABLE IF EXISTS tab_sample)"); db.execute(R"(DROP TABLE IF EXISTS tab_sample)");
db.execute(R"(CREATE TABLE tab_sample ( db.execute(R"(CREATE TABLE tab_sample (
alpha bigint(20) AUTO_INCREMENT, alpha bigint(20) AUTO_INCREMENT,

View File

@ -23,6 +23,7 @@
* OF THE POSSIBILITY OF SUCH DAMAGE. * OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "make_test_connection.h"
#include "TabSample.h" #include "TabSample.h"
#include <sqlpp11/mysql/mysql.h> #include <sqlpp11/mysql/mysql.h>
#include <sqlpp11/sqlpp11.h> #include <sqlpp11/sqlpp11.h>
@ -36,23 +37,10 @@ namespace sql = sqlpp::mysql;
int Update(int, char*[]) int Update(int, char*[])
{ {
auto config = std::make_shared<sql::connection_config>(); sql::global_library_init();
config->user = "root";
config->database = "sqlpp_mysql";
config->debug = true;
try try
{ {
sql::connection db(config); auto db = sql::make_test_connection();
}
catch (const sqlpp::exception& e)
{
std::cerr << "For testing, you'll need to create a database sqlpp_mysql for user root (no password)" << std::endl;
std::cerr << e.what() << std::endl;
return 1;
}
try
{
sql::connection db(config);
db.execute(R"(DROP TABLE IF EXISTS tab_sample)"); db.execute(R"(DROP TABLE IF EXISTS tab_sample)");
db.execute(R"(CREATE TABLE tab_sample ( db.execute(R"(CREATE TABLE tab_sample (
alpha bigint(20) AUTO_INCREMENT, alpha bigint(20) AUTO_INCREMENT,

View File

@ -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 <sqlpp11/mysql/mysql.h>
namespace sqlpp
{
namespace mysql
{
// Get configuration for test connection
inline std::shared_ptr<sqlpp::mysql::connection_config> make_test_config()
{
auto config = std::make_shared<sqlpp::mysql::connection_config>();
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

View File

@ -30,6 +30,7 @@ set(test_files
Basic.cpp Basic.cpp
BasicConstConfig.cpp BasicConstConfig.cpp
Blob.cpp Blob.cpp
ConnectionPool.cpp
Constructor.cpp Constructor.cpp
Date.cpp Date.cpp
DateTime.cpp DateTime.cpp

View File

@ -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 <sqlpp11/postgresql/postgresql.h>
#include <sqlpp11/sqlpp11.h>
#include "../../include/ConnectionPoolTests.h"
#include "make_test_connection.h"
namespace sql = ::sqlpp::postgresql;
int ConnectionPool(int, char*[])
{
try
{
sqlpp::test::test_connection_pool<sql::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;
}

View File

@ -33,12 +33,10 @@ namespace sqlpp
{ {
namespace postgresql namespace postgresql
{ {
// Starts a connection and sets the time zone to UTC // Get configuration for test connection
inline ::sqlpp::postgresql::connection make_test_connection() inline std::shared_ptr<sqlpp::postgresql::connection_config> make_test_config()
{ {
namespace sql = sqlpp::postgresql; auto config = std::make_shared<sqlpp::postgresql::connection_config>();
auto config = std::make_shared<sql::connection_config>();
#ifdef WIN32 #ifdef WIN32
config->dbname = "test"; config->dbname = "test";
@ -49,6 +47,15 @@ namespace sqlpp
config->dbname = "sqlpp_postgresql"; config->dbname = "sqlpp_postgresql";
config->debug = true; config->debug = true;
#endif #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; sql::connection db;
try try

View File

@ -38,6 +38,7 @@ set(test_files
FloatingPoint.cpp FloatingPoint.cpp
Integral.cpp Integral.cpp
Blob.cpp Blob.cpp
ConnectionPool.cpp
) )
create_test_sourcelist(test_sources test_main.cpp ${test_files}) create_test_sourcelist(test_sources test_main.cpp ${test_files})

View File

@ -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 <sqlpp11/sqlite3/sqlite3.h>
#include <sqlpp11/sqlpp11.h>
#include "../../include/ConnectionPoolTests.h"
namespace sql = ::sqlpp::sqlite3;
int ConnectionPool(int, char*[])
{
try
{
auto config = std::make_shared<sql::connection_config>();
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<sql::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;
}