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

Merge tag '0.49' into develop

Add get default transaction isolation level function (thanks volka)
This commit is contained in:
rbock 2017-06-04 16:00:51 +02:00
commit 8113a5f1fc
13 changed files with 612 additions and 2 deletions

1
_config.yml Normal file
View File

@ -0,0 +1 @@
theme: jekyll-theme-minimal

View File

@ -29,6 +29,7 @@
#include <string>
#include <sqlpp11/connection.h>
#include <sqlpp11/transaction.h>
#include <sqlpp11/database/char_result.h> // You may use char result or bind result or both
#include <sqlpp11/database/bind_result.h> // to represent results of select and prepared select
@ -129,9 +130,20 @@ namespace sqlpp
return t._prepare(*this);
}
//! set the transaction isolation level for the current connection
/// time of effect is connector-specific, for most is will only affect new transactions
void set_default_isolation_level(sqlpp::isolation_level);
//! read the default transaction isolation level for the current connection
sqlpp::isolation_level get_default_isolation_level();
//! start transaction
void start_transaction();
//! start transaction with defined isolation level (optional only for connectors that support it)
void start_transaction(isolation_level isolation /* = isolation_level::undefined */);
//! commit transaction (or throw transaction if the transaction has been finished already)
void commit_transaction();

View File

@ -0,0 +1,192 @@
/*
* Copyright (c) 2013 - 2017, Roland Bock, Frank Park
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SQLPP_CONNECTION_POOL_H
#define SQLPP_CONNECTION_POOL_H
#include <mutex>
#include <stack>
#include <unordered_map>
#include <memory>
#include <iostream>
#include <chrono>
#include <type_traits>
#include <sqlpp11/exception.h>
#include <sqlpp11/pool_connection.h>
namespace sqlpp
{
namespace reconnect_policy
{
struct auto_reconnect {
template<typename Connection>
void operator()(Connection* connection)
{
if(!connection->is_valid())
connection->reconnect()
}
template<typename Connection>
void clean(Connection* connection) {}
};
using namespace std::chrono_literals;
class periodic_reconnect
{
private:
std::chrono::seconds revalidate_after;
std::unordered_map<void*,std::chrono::time_point<std::chrono::system_clock> > last_checked;
public:
periodic_reconnect(const std::chrono::seconds r = 28800s) //default wait_timeout in MySQL
: revalidate_after(r), last_checked() {}
template<typename Connection>
void operator()(Connection* con)
{
auto last = last_checked.find(con);
auto now = std::chrono::system_clock::now();
if(last == last_checked.end())
{
if (!con->is_valid())
{
con->reconnect();
}
last_checked.emplace_hint(last, con, now);
}
else if(now - last->second > revalidate_after)
{
if (!con->is_valid())
{
con->reconnect();
}
last = now;
}
}
template<typename Connection>
void clean(Connection* con) {
auto itr = last_checked.find(con);
if(itr != last_checked.end())
{
last_checked.erase(itr);
}
}
};
struct never_reconnect {
template<typename Connection>
void operator()(Connection*) {}
template<typename Connection>
void clean(Connection*) {}
};
}
template <typename Connection_config,
typename Reconnect_policy = reconnect_policy::auto_reconnect,
typename Connection = typename std::enable_if<std::is_class<Connection_config::connection>::value, Connection_config::connection>::type>
class connection_pool
{
friend pool_connection<Connection_config, Reconnect_policy, Connection>;
private:
std::mutex connection_pool_mutex;
const std::shared_ptr<Connection_config> config;
size_t maximum_pool_size = 0;
std::stack<std::unique_ptr<Connection>> free_connections;
Reconnect_policy reconnect_policy;
void free_connection(std::unique_ptr<Connection>& connection)
{
std::lock_guard<std::mutex> lock(connection_pool_mutex);
if (free_connections.size() >= maximum_pool_size)
{
// Exceeds default size, do nothign and let connection self destroy.
}
else
{
if (connection.get())
{
if (connection->is_valid())
{
free_connections.push(std::move(connection));
}
else
{
throw sqlpp::exception("Trying to free a connection with incompatible config.");
}
}
else
{
throw sqlpp::exception("Trying to free an empty connection.");
}
}
}
public:
connection_pool(const std::shared_ptr<Connection_config>& config, size_t pool_size)
: config(config), maximum_pool_size(pool_size), reconnect_policy(Reconnect_policy()) {}
~connection_pool() = default;
connection_pool(const connection_pool&) = delete;
connection_pool(connection_pool&& other)
: config(std::move(other.config)), maximum_pool_size(std::move(other.maximum_pool_size)),
reconnect_policy(std::move(other.reconnect_policy)) {}
connection_pool& operator=(const connection_pool&) = delete;
connection_pool& operator=(connection_pool&&) = delete;
pool_connection<Connection_config, Reconnect_policy, Connection> get_connection()
{
std::lock_guard<std::mutex> lock(connection_pool_mutex);
if (!free_connections.empty())
{
auto connection = std::move(free_connections.top());
free_connections.pop();
return pool_connection<Connection_config, Reconnect_policy, Connection>(connection, this);
}
try
{
return pool_connection<Connection_config, Reconnect_policy, Connection>(std::move(std::make_unique<Connection>(config)), this);
}
catch (const sqlpp::exception& e)
{
std::cerr << "Failed to spawn a new connection." << std::endl;
std::cerr << e.what() << std::endl;
throw;
}
}
};
template<typename Connection_config,
typename Reconnect_policy = reconnect_policy::auto_reconnect,
typename Connection = typename std::enable_if<std::is_class<Connection_config::connection>::value,Connection_config::connection>::type>
connection_pool<Connection_config, Reconnect_policy, Connection> make_connection_pool(
const std::shared_ptr<Connection_config>& config,
size_t max_pool_size)
{
return connection_pool<Connection_config, Reconnect_policy, Connection>(config, max_pool_size);
}
}
#endif

View File

@ -0,0 +1,179 @@
/*
* Copyright (c) 2017, Serge Robyns
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SQLPP_FOR_UPDATE_H
#define SQLPP_FOR_UPDATE_H
#include <sqlpp11/detail/type_set.h>
#include <sqlpp11/policy_update.h>
#include <sqlpp11/type_traits.h>
namespace sqlpp
{
// FOR_UPDATE DATA
struct for_update_data_t
{
};
// FOR_UPDATE
struct for_update_t
{
using _traits = make_traits<no_value_t, tag::is_for_update>;
using _nodes = detail::type_vector<>;
// Data
using _data_t = for_update_data_t;
// Member implementation with data and methods
template <typename Policies>
struct _impl_t
{
// workaround for msvc bug https://connect.microsoft.com/VisualStudio/Feedback/Details/2091069
_impl_t() = default;
_impl_t(const _data_t& data) : _data(data)
{
}
_data_t _data;
};
// Base template to be inherited by the statement
template <typename Policies>
struct _base_t
{
using _data_t = for_update_data_t;
// workaround for msvc bug https://connect.microsoft.com/VisualStudio/Feedback/Details/2091069
template <typename... Args>
_base_t(Args&&... args) : for_update{std::forward<Args>(args)...}
{
}
_impl_t<Policies> for_update;
_impl_t<Policies>& operator()()
{
return for_update;
}
const _impl_t<Policies>& operator()() const
{
return for_update;
}
template <typename T>
static auto _get_member(T t) -> decltype(t.for_update)
{
return t.for_update;
}
using _consistency_check = consistent_t;
};
};
struct no_for_update_t
{
using _traits = make_traits<no_value_t, tag::is_noop>;
using _nodes = detail::type_vector<>;
// Data
using _data_t = no_data_t;
// Member implementation with data and methods
template <typename Policies>
struct _impl_t
{
// workaround for msvc bug https://connect.microsoft.com/VisualStudio/Feedback/Details/2091069
_impl_t() = default;
_impl_t(const _data_t& data) : _data(data)
{
}
_data_t _data;
};
// Base template to be inherited by the statement
template <typename Policies>
struct _base_t
{
using _data_t = no_data_t;
// workaround for msvc bug https://connect.microsoft.com/VisualStudio/Feedback/Details/2091069
template <typename... Args>
_base_t(Args&&... args) : no_for_update{std::forward<Args>(args)...}
{
}
_impl_t<Policies> no_for_update;
_impl_t<Policies>& operator()()
{
return no_for_update;
}
const _impl_t<Policies>& operator()() const
{
return no_for_update;
}
template <typename T>
static auto _get_member(T t) -> decltype(t.no_for_update)
{
return t.no_for_update;
}
using _database_t = typename Policies::_database_t;
template <typename Check, typename T>
using _new_statement_t = new_statement_t<Check, Policies, no_for_update_t, T>;
using _consistency_check = consistent_t;
auto for_update() const -> _new_statement_t<consistent_t, for_update_t>
{
return {static_cast<const derived_statement_t<Policies>&>(*this), for_update_data_t{}};
}
};
};
// Interpreters
template <typename Context>
struct serializer_t<Context, for_update_data_t>
{
using _serialize_check = serialize_check_of<Context>;
using T = for_update_data_t;
static Context& _(const T& t, Context& context)
{
context << " FOR UPDATE ";
return context;
}
};
template <typename T>
auto for_update(T&& t) -> decltype(statement_t<void, no_for_update_t>().for_update(std::forward<T>(t)))
{
return statement_t<void, no_for_update_t>().for_update(std::forward<T>(t));
}
}
#endif

View File

@ -0,0 +1,76 @@
/*
* Copyright (c) 2013 - 2017, Roland Bock, Frank Park
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SQLPP_POOL_CONNECTION_H
#define SQLPP_POOL_CONNECTION_H
#include <memory>
namespace sqlpp
{
template <typename Connection_config, typename Reconnect_policy, typename Connection,
typename Connection_pool = connection_pool<Connection_config, Reconnect_policy, Connection>>
struct pool_connection
{
private:
std::unique_ptr<Connection> _impl;
Connection_pool* origin;
public:
pool_connection(std::unique_ptr<Connection>& connection, Connection_pool* origin)
: _impl(std::move(connection)), origin(origin) {}
~pool_connection()
{
origin->free_connection(_impl);
}
template<typename... Args>
auto operator()(Args&&... args) -> decltype(_impl->args(std::forward<Args>(args)...))
{
return _impl->args(std::forward<Args>(args)...);
}
template <typename T>
auto operator()(const T& t) -> decltype(_impl->run(t))
{
return _impl->run(t);
}
Connection* operator->()
{
return &_impl;
}
pool_connection(const pool_connection&) = delete;
pool_connection(pool_connection&& other)
: _impl(std::move(other._impl)), origin(other.origin) {}
pool_connection& operator=(const pool_connection&) = delete;
pool_connection& operator=(pool_connection&&) = delete;
};
}
#endif

View File

@ -40,6 +40,7 @@
#include <sqlpp11/having.h>
#include <sqlpp11/order_by.h>
#include <sqlpp11/limit.h>
#include <sqlpp11/for_update.h>
#include <sqlpp11/offset.h>
#include <sqlpp11/union.h>
#include <sqlpp11/expression.h>
@ -82,7 +83,8 @@ namespace sqlpp
no_order_by_t,
no_limit_t,
no_offset_t,
no_union_t>;
no_union_t,
no_for_update_t>;
inline blank_select_t<void> select() // FIXME: These should be constexpr
{

View File

@ -28,12 +28,21 @@
#define SQLPP_TRANSACTION_H
#include <stdexcept>
#include <ciso646>
namespace sqlpp
{
static constexpr bool quiet_auto_rollback = false;
static constexpr bool report_auto_rollback = true;
enum class isolation_level {
undefined, // use the current database default
serializable, // highest level, stronguest guarantee
repeatable_read, // DBMS holds read and write locks
read_committed, // DMBS holds read locks, non-repeatable reads can occur
read_uncommitted // lowest isolation level, dirty reads may occur
};
template <typename Db>
class transaction_t
{
@ -48,6 +57,12 @@ namespace sqlpp
_db.start_transaction();
}
transaction_t(Db& db, bool report_unfinished_transaction, isolation_level isolation)
: _db(db), _report_unfinished_transaction(report_unfinished_transaction)
{
_db.start_transaction(isolation);
}
transaction_t(const transaction_t&) = delete;
transaction_t(transaction_t&&) = default;
transaction_t& operator=(const transaction_t&) = delete;
@ -88,7 +103,13 @@ namespace sqlpp
template <typename Db>
transaction_t<Db> start_transaction(Db& db, bool report_unfinished_transaction = report_auto_rollback)
{
return {db, report_unfinished_transaction};
return {db, report_unfinished_transaction};
}
template <typename Db>
transaction_t<Db> start_transaction(Db& db, isolation_level isolation, bool report_unfinished_transaction = report_auto_rollback)
{
return {db, report_unfinished_transaction, isolation};
}
}

View File

@ -200,6 +200,7 @@ namespace sqlpp
SQLPP_VALUE_TRAIT_GENERATOR(is_having)
SQLPP_VALUE_TRAIT_GENERATOR(is_order_by)
SQLPP_VALUE_TRAIT_GENERATOR(is_limit)
SQLPP_VALUE_TRAIT_GENERATOR(is_for_update)
SQLPP_VALUE_TRAIT_GENERATOR(is_offset)
SQLPP_VALUE_TRAIT_GENERATOR(is_using_)
SQLPP_VALUE_TRAIT_GENERATOR(is_column_list)

View File

@ -204,6 +204,7 @@ types = {
'integer': 'integer',
'int': 'integer',
'serial': 'integer', # PostgreSQL
'mediumint' : 'integer',
'bigint': 'bigint',
'bigserial': 'bigint', # PostgreSQL
'char': 'char_',

View File

@ -30,6 +30,7 @@ set(test_serializer_names
Insert
TableAlias
Where
ForUpdate
)
create_test_sourcelist(test_serializer_sources test_serializer_main.cpp ${test_serializer_names})

View File

@ -0,0 +1,60 @@
/*
* Copyright (c) 2017, Serge Robyns
* 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 "compare.h"
#include "Sample.h"
#include <sqlpp11/sqlpp11.h>
#include <iostream>
namespace
{
auto getTrue() -> std::string
{
MockDb::_serializer_context_t printer = {};
return serialize(sqlpp::value(true), printer).str();
}
auto getFalse() -> std::string
{
MockDb::_serializer_context_t printer = {};
return serialize(sqlpp::value(false), printer).str();
}
}
int ForUpdate(int, char* [])
{
const auto foo = test::TabFoo{};
const auto bar = test::TabBar{};
// Unconditionally
compare(__LINE__, select(foo.omega).from(foo).unconditionally().for_update(), "SELECT tab_foo.omega FROM tab_foo FOR UPDATE ");
// Never
compare(__LINE__, where(sqlpp::value(false)), " WHERE " + getFalse());
return 0;
}

View File

@ -28,12 +28,19 @@
#include <iostream>
#include <sqlpp11/connection.h>
#include <sqlpp11/transaction.h>
#include <sqlpp11/data_types/no_value.h>
#include <sqlpp11/schema.h>
#include <sqlpp11/serialize.h>
#include <sqlpp11/serializer_context.h>
#include <sstream>
// an object to store internal Mock flags and values to validate in tests
struct InternalMockData {
sqlpp::isolation_level _last_isolation_level;
sqlpp::isolation_level _default_isolation_level;
};
template <bool enforceNullResultTreatment>
struct MockDbT : public sqlpp::connection
{
@ -244,6 +251,38 @@ struct MockDbT : public sqlpp::connection
{
return {name};
}
void start_transaction()
{
_mock_data._last_isolation_level = _mock_data._default_isolation_level;
}
void start_transaction(sqlpp::isolation_level level)
{
_mock_data._last_isolation_level = level;
}
void set_default_isolation_level(sqlpp::isolation_level level)
{
_mock_data._default_isolation_level = level;
}
sqlpp::isolation_level get_default_isolation_level()
{
return _mock_data._default_isolation_level;
}
void rollback_transaction(bool)
{}
void commit_transaction()
{}
void report_rollback_failure(std::string)
{}
// temporary data store to verify the expected results were produced
InternalMockData _mock_data;
};
using MockDb = MockDbT<false>;

View File

@ -107,6 +107,14 @@ int Select(int, char* [])
std::cout << a << ", " << b << ", " << g << std::endl;
}
for (const auto& row : db(select(all_of(t).as(t), t.gamma).from(t).where(t.alpha > 7).for_update()))
{
int64_t a = row.tabBar.alpha;
const std::string b = row.tabBar.beta;
const bool g = row.gamma;
std::cout << a << ", " << b << ", " << g << std::endl;
}
for (const auto& row :
db(select(all_of(t), all_of(f)).from(t.join(f).on(t.alpha > f.omega and not t.gamma)).unconditionally()))
{
@ -199,5 +207,22 @@ int Select(int, char* [])
for_each_field(row, to_cerr{});
}
{
auto transaction = start_transaction(db, sqlpp::isolation_level::read_committed);
if (db._mock_data._last_isolation_level != sqlpp::isolation_level::read_committed)
{
std::cout << "Error: transaction isolation level does not match expected level" << std::endl;
}
}
db.set_default_isolation_level(sqlpp::isolation_level::read_uncommitted);
{
auto transaction = start_transaction(db);
if (db._mock_data._last_isolation_level != sqlpp::isolation_level::read_uncommitted)
{
std::cout << "Error: transaction isolation level does not match default level" << std::endl;
}
}
return 0;
}