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) 2023, Vesselin Atanasov
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
@ -37,6 +38,44 @@ namespace sqlpp
{
namespace database
{
// Connection configuration that is used to create new database connections
struct connection_config
{
// Put the configuration properties here, e.g.
//
// std::string host;
// unsigned port;
// std::string username;
// std::string password;
// std::string db_name;
};
// The connection handle is a low-level representation of a database connection.
// Pre-connected handles are stored in the connection pool and are used to create
// full connection objects on request.
class connection_handle
{
public:
// Connection handles can be created from connection configurations
connection_handle(const std::shared_ptr<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
// any deviations from the SQL standard, you should use your own
// context in order to specialize the behaviour, see also interpreter.h
@ -48,9 +87,25 @@ namespace sqlpp
std::string escape(std::string arg);
};
class connection : public sqlpp::connection // this inheritance helps with ADL for dynamic_select, for instance
// The base database-specific connection class. Non-pooled and pooled connection classes derive from it
class connection_base : public sqlpp::connection // this inheritance helps with ADL for dynamic_select, for instance
{
public:
// Base configuration
using _connection_base_t = connection_base;
// Type of configuration instances
using _config_t = connection_config;
// Shared pointer wrapping a configuration instance
using _config_ptr_t = std::shared_ptr<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<
::sqlpp::no_value_t,
::sqlpp::tag::enforce_null_result_treatment // If that is what you really want, leave it out otherwise
@ -63,13 +118,6 @@ namespace sqlpp
// serializer and interpreter are typically the same for string based connectors
// the types are required for dynamic statement components, see sqlpp11/interpretable.h
connection(...);
~connection();
connection(const connection&) = delete;
connection(connection&&) = delete;
connection& operator=(const connection&) = delete;
connection& operator=(connection&&) = delete;
//! "direct" select
template <typename Select>
<< bind_result_t >>
@ -153,9 +201,26 @@ namespace sqlpp
//! report a rollback failure (will be called by transactions in case of a rollback failure in the destructor)
void report_rollback_failure(const std::string message) noexcept;
protected:
// Low-level connection handle
_handle_ptr_t _handle;
// The constructors are private because the base class instances are never created directly,
// The constructors are called from the constructors of the derived classes
connection_base() = default;
connection_base(_handle_ptr_t&& handle) : _handle{std::move(handle)}
{
}
};
}
}
// Normal non-pooled connections.
using connection = sqlpp::normal_connection<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>

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) 2023, Vesselin Atanasov
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
@ -27,11 +28,133 @@
#ifndef SQLPP11_CONNECTION_H
#define SQLPP11_CONNECTION_H
#include <functional>
#include <memory>
namespace sqlpp
{
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
#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,
typename Expr,
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;
}

View File

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

View File

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

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) 2023, Vesselin Atanasov
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
@ -28,6 +29,7 @@
#define SQLPP_MYSQL_H
#include <sqlpp11/mysql/connection.h>
#include <sqlpp11/mysql/connection_pool.h>
#include <sqlpp11/mysql/char_result.h>
#endif

View File

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

View File

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

View File

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

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

View File

@ -164,7 +164,7 @@ namespace sqlpp
valid = false;
count = 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
valid = true;
}
@ -188,7 +188,7 @@ namespace sqlpp
inline void prepared_statement_handle_t::prepare(std::string stmt)
{
// Create the prepared statement
result = PQprepare(connection.native(), _name.c_str(), stmt.c_str(), 0, nullptr);
result = PQprepare(connection.native_handle(), _name.c_str(), stmt.c_str(), 0, nullptr);
valid = true;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,6 @@
/*
* Copyright (c) 2013 - 2015, Roland Bock
* Copyright (c) 2023, Vesselin Atanasov
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
@ -28,6 +29,7 @@
#define SQLPP_SQLITE3_H
#include <sqlpp11/sqlite3/connection.h>
#include <sqlpp11/sqlite3/connection_pool.h>
#include <sqlpp11/sqlite3/insert_or.h>
#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
Update.cpp
Remove.cpp
ConnectionPool.cpp
)
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 "make_test_connection.h"
#include "TabSample.h"
#include <sqlpp11/sqlpp11.h>
#include <sqlpp11/custom_query.h>
@ -59,28 +60,13 @@ namespace
const auto tab = TabSample{};
namespace mysql = sqlpp::mysql;
namespace sql = sqlpp::mysql;
int CustomQuery(int, char*[])
{
mysql::global_library_init();
auto config = std::make_shared<mysql::connection_config>();
config->user = "root";
config->database = "sqlpp_mysql";
config->debug = true;
sql::global_library_init();
try
{
mysql::connection db(config);
}
catch (const sqlpp::exception& e)
{
std::cerr << "For testing, you'll need to create a database sqlpp_mysql for user root (no password)" << std::endl;
std::cerr << e.what() << std::endl;
return 1;
}
try
{
mysql::connection db(config);
auto db = sql::make_test_connection();
db.execute(R"(DROP TABLE IF EXISTS tab_sample)");
db.execute(R"(CREATE TABLE tab_sample (
alpha bigint(20) AUTO_INCREMENT,

View File

@ -23,6 +23,7 @@
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "make_test_connection.h"
#include "TabSample.h"
#include <sqlpp11/mysql/mysql.h>
#include <sqlpp11/sqlpp11.h>
@ -68,32 +69,13 @@ namespace
}
}
namespace mysql = sqlpp::mysql;
namespace sql = sqlpp::mysql;
int DateTime(int, char*[])
{
auto config = std::make_shared<mysql::connection_config>();
config->user = "root";
config->database = "sqlpp_mysql";
config->debug = true;
sql::global_library_init();
try
{
mysql::connection db(config);
}
catch (const std::exception& e)
{
std::cerr << "For testing, you'll need to create a database sqlpp_mysql for user root (no password)" << std::endl;
std::cerr << e.what() << std::endl;
return 1;
}
catch (...)
{
std::cerr << "Unknown exception during connect" << std::endl;
return 1;
}
try
{
mysql::connection db(config);
auto db = sql::make_test_connection();
db.execute(R"(SET time_zone = '+00:00')"); // To force MySQL's CURRENT_TIMESTAMP into the right timezone
db.execute(R"(DROP TABLE IF EXISTS tab_date_time)");
db.execute(R"(CREATE TABLE tab_date_time (

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

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;
}