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

Add is_transaction_active() to all connectors (#550)

* Document the connector API method is_transaction_active()
* Move mysql::connection_base::is_transaction_active() to the other transaction-handling methods.
* Add more tests for mysql::connection::is_transaction_active()
* Add postgresql::connection_base::is_transaction_active()
* Add tests for postgresql::connection_base::is_transaction_active()
* Change the type of the SQLite3 transaction status from transaction_status_type to a boolean flag.
* Add sqlite3::connection_base::is_transaction_active()
* Add tests for sqlite3::connection_base::is_transaction_active()
* When closing a transaction do it in the following order: report (if any), execute SQL command, set transaction active flag to false.
This commit is contained in:
MeanSquaredError 2024-01-05 09:59:42 +02:00 committed by GitHub
parent 3474a4fa5d
commit 9b49afa306
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 42 additions and 30 deletions

View File

@ -180,7 +180,6 @@ namespace sqlpp
return t._prepare(*this); return t._prepare(*this);
} }
//! set the transaction isolation level for the current connection //! set the transaction isolation level for the current connection
/// time of effect is connector-specific, for most is will only affect new transactions /// time of effect is connector-specific, for most is will only affect new transactions
void set_default_isolation_level(sqlpp::isolation_level); void set_default_isolation_level(sqlpp::isolation_level);
@ -204,6 +203,9 @@ namespace sqlpp
//! report a rollback failure (will be called by transactions in case of a rollback failure in the destructor) //! report a rollback failure (will be called by transactions in case of a rollback failure in the destructor)
void report_rollback_failure(const std::string message) noexcept; void report_rollback_failure(const std::string message) noexcept;
//! check if transaction is active
bool is_transaction_active();
protected: protected:
// Low-level connection handle // Low-level connection handle
_handle_ptr_t _handle; _handle_ptr_t _handle;

View File

@ -291,11 +291,6 @@ namespace sqlpp
return _handle->config; return _handle->config;
} }
bool is_transaction_active()
{
return _transaction_active;
}
template <typename Select> template <typename Select>
char_result_t select(const Select& s) char_result_t select(const Select& s)
{ {
@ -462,8 +457,8 @@ namespace sqlpp
{ {
throw sqlpp::exception{"MySQL: Cannot commit a finished or failed transaction"}; throw sqlpp::exception{"MySQL: Cannot commit a finished or failed transaction"};
} }
_transaction_active = false;
execute_statement(_handle, "COMMIT"); execute_statement(_handle, "COMMIT");
_transaction_active = false;
} }
//! rollback transaction (or throw if the transaction has been finished already) //! rollback transaction (or throw if the transaction has been finished already)
@ -477,8 +472,8 @@ namespace sqlpp
{ {
std::cerr << "MySQL warning: Rolling back unfinished transaction" << std::endl; std::cerr << "MySQL warning: Rolling back unfinished transaction" << std::endl;
} }
_transaction_active = false;
execute_statement(_handle, "ROLLBACK"); execute_statement(_handle, "ROLLBACK");
_transaction_active = false;
} }
//! report a rollback failure (will be called by transactions in case of a rollback failure in the destructor) //! report a rollback failure (will be called by transactions in case of a rollback failure in the destructor)
@ -487,6 +482,12 @@ namespace sqlpp
std::cerr << "MySQL message:" << message << std::endl; std::cerr << "MySQL message:" << message << std::endl;
} }
//! check if transaction is active
bool is_transaction_active()
{
return _transaction_active;
}
MYSQL* native_handle() MYSQL* native_handle()
{ {
return _handle->native_handle(); return _handle->native_handle();

View File

@ -542,9 +542,8 @@ namespace sqlpp
{ {
throw sqlpp::exception{"PostgreSQL error: transaction failed or finished."}; throw sqlpp::exception{"PostgreSQL error: transaction failed or finished."};
} }
_transaction_active = false;
execute("COMMIT"); execute("COMMIT");
_transaction_active = false;
} }
//! rollback transaction //! rollback transaction
@ -554,12 +553,11 @@ namespace sqlpp
{ {
throw sqlpp::exception{"PostgreSQL error: transaction failed or finished."}; throw sqlpp::exception{"PostgreSQL error: transaction failed or finished."};
} }
execute("ROLLBACK");
if (report) if (report)
{ {
std::cerr << "PostgreSQL warning: rolling back unfinished transaction" << std::endl; std::cerr << "PostgreSQL warning: rolling back unfinished transaction" << std::endl;
} }
execute("ROLLBACK");
_transaction_active = false; _transaction_active = false;
} }
@ -569,6 +567,12 @@ namespace sqlpp
std::cerr << "PostgreSQL error: " << message << std::endl; std::cerr << "PostgreSQL error: " << message << std::endl;
} }
//! check if transaction is active
bool is_transaction_active()
{
return _transaction_active;
}
//! get the last inserted id for a certain table //! get the last inserted id for a certain table
uint64_t last_insert_id(const std::string& table, const std::string& fieldname) uint64_t last_insert_id(const std::string& table, const std::string& fieldname)
{ {

View File

@ -146,14 +146,7 @@ namespace sqlpp
class SQLPP11_SQLITE3_EXPORT connection_base : public sqlpp::connection class SQLPP11_SQLITE3_EXPORT connection_base : public sqlpp::connection
{ {
private: private:
enum class transaction_status_type bool _transaction_active{false};
{
none,
maybe,
active
};
transaction_status_type _transaction_status{transaction_status_type::none};
// direct execution // direct execution
bind_result_t select_impl(const std::string& statement) bind_result_t select_impl(const std::string& statement)
@ -470,35 +463,33 @@ namespace sqlpp
//! start transaction //! start transaction
void start_transaction() void start_transaction()
{ {
if (_transaction_status == transaction_status_type::active) if (_transaction_active)
{ {
throw sqlpp::exception{"Sqlite3 error: Cannot have more than one open transaction per connection"}; throw sqlpp::exception{"Sqlite3 error: Cannot have more than one open transaction per connection"};
} }
_transaction_status = transaction_status_type::maybe;
auto prepared = prepare_statement(_handle, "BEGIN"); auto prepared = prepare_statement(_handle, "BEGIN");
execute_statement(_handle, prepared); execute_statement(_handle, prepared);
_transaction_status = transaction_status_type::active; _transaction_active = true;
} }
//! commit transaction (or throw if the transaction has been finished already) //! commit transaction (or throw if the transaction has been finished already)
void commit_transaction() void commit_transaction()
{ {
if (_transaction_status == transaction_status_type::none) if (!_transaction_active)
{ {
throw sqlpp::exception{"Sqlite3 error: Cannot commit a finished or failed transaction"}; throw sqlpp::exception{"Sqlite3 error: Cannot commit a finished or failed transaction"};
} }
_transaction_status = transaction_status_type::maybe;
auto prepared = prepare_statement(_handle, "COMMIT"); auto prepared = prepare_statement(_handle, "COMMIT");
execute_statement(_handle, prepared); execute_statement(_handle, prepared);
_transaction_status = transaction_status_type::none; _transaction_active = false;
} }
//! rollback transaction with or without reporting the rollback (or throw if the transaction has been finished //! rollback transaction with or without reporting the rollback (or throw if the transaction has been finished
// already) // already)
void rollback_transaction(bool report) void rollback_transaction(bool report)
{ {
if (_transaction_status == transaction_status_type::none) if (!_transaction_active)
{ {
throw sqlpp::exception{"Sqlite3 error: Cannot rollback a finished or failed transaction"}; throw sqlpp::exception{"Sqlite3 error: Cannot rollback a finished or failed transaction"};
} }
@ -506,10 +497,9 @@ namespace sqlpp
{ {
std::cerr << "Sqlite3 warning: Rolling back unfinished transaction" << std::endl; std::cerr << "Sqlite3 warning: Rolling back unfinished transaction" << std::endl;
} }
_transaction_status = transaction_status_type::maybe;
auto prepared = prepare_statement(_handle, "ROLLBACK"); auto prepared = prepare_statement(_handle, "ROLLBACK");
execute_statement(_handle, prepared); execute_statement(_handle, prepared);
_transaction_status = transaction_status_type::none; _transaction_active = false;
} }
//! report a rollback failure (will be called by transactions in case of a rollback failure in the destructor) //! report a rollback failure (will be called by transactions in case of a rollback failure in the destructor)
@ -518,6 +508,12 @@ namespace sqlpp
std::cerr << "Sqlite3 message:" << message << std::endl; std::cerr << "Sqlite3 message:" << message << std::endl;
} }
//! check if transaction is active
bool is_transaction_active()
{
return _transaction_active;
}
//! get the last inserted id //! get the last inserted id
uint64_t last_insert_id() noexcept uint64_t last_insert_id() noexcept
{ {

View File

@ -55,6 +55,7 @@ int MoveConstructor(int, char*[])
gamma bool DEFAULT NULL gamma bool DEFAULT NULL
))"); ))");
assert(connections.at(0).is_transaction_active() == false);
connections.at(0).start_transaction(); connections.at(0).start_transaction();
auto db = std::move(connections.at(0)); auto db = std::move(connections.at(0));
assert(db.is_transaction_active()); assert(db.is_transaction_active());
@ -74,6 +75,7 @@ int MoveConstructor(int, char*[])
std::cerr << "row.alpha: " << row.alpha << ", row.beta: " << row.at("beta") << std::endl; std::cerr << "row.alpha: " << row.alpha << ", row.beta: " << row.at("beta") << std::endl;
}; };
db.commit_transaction(); db.commit_transaction();
assert(db.is_transaction_active() == false);
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {

View File

@ -94,6 +94,7 @@ int Transaction(int, char*[])
{ {
{ {
require_equal(__LINE__, db.is_transaction_active(), false);
auto current_level = db(custom_query(sqlpp::verbatim("show transaction_isolation;")) auto current_level = db(custom_query(sqlpp::verbatim("show transaction_isolation;"))
.with_result_type_of(select(sqlpp::value("").as(level)))) .with_result_type_of(select(sqlpp::value("").as(level))))
.front() .front()
@ -102,14 +103,16 @@ int Transaction(int, char*[])
std::cerr << "isolation level outside transaction: " << current_level << "\n"; std::cerr << "isolation level outside transaction: " << current_level << "\n";
auto tx = start_transaction(db, sqlpp::isolation_level::serializable); auto tx = start_transaction(db, sqlpp::isolation_level::serializable);
require_equal(__LINE__, db.is_transaction_active(), true);
current_level = db(custom_query(sqlpp::verbatim("show transaction_isolation;")) current_level = db(custom_query(sqlpp::verbatim("show transaction_isolation;"))
.with_result_type_of(select(sqlpp::value("").as(level)))) .with_result_type_of(select(sqlpp::value("").as(level))))
.front() .front()
.level; .level;
require_equal(__LINE__, current_level, "serializable"); require_equal(__LINE__, current_level, "serializable");
std::cerr << "isolation level in transaction(serializable) : " << current_level << "\n"; std::cerr << "isolation level in transaction(serializable) : " << current_level << "\n";
tx.commit(); tx.commit();
require_equal(__LINE__, db.is_transaction_active(), false);
} }
require_equal(__LINE__, db.get_default_isolation_level(), sqlpp::isolation_level::read_committed); require_equal(__LINE__, db.get_default_isolation_level(), sqlpp::isolation_level::read_committed);

View File

@ -53,6 +53,8 @@ int Transaction(int, char*[])
std::cerr << "--------------------------------------" << std::endl; std::cerr << "--------------------------------------" << std::endl;
assert(db.is_transaction_active() == false);
auto current_level = db.get_default_isolation_level(); auto current_level = db.get_default_isolation_level();
std::cout << "Expecting default isolation level = 1, is " << static_cast<int>(current_level) << std::endl; std::cout << "Expecting default isolation level = 1, is " << static_cast<int>(current_level) << std::endl;
assert(current_level == sqlpp::isolation_level::serializable); assert(current_level == sqlpp::isolation_level::serializable);
@ -66,6 +68,7 @@ int Transaction(int, char*[])
std::cerr << "Expecting read_uncommitted = 0, is: " << pragmaValue << std::endl; std::cerr << "Expecting read_uncommitted = 0, is: " << pragmaValue << std::endl;
db.set_default_isolation_level(sqlpp::isolation_level::read_uncommitted); db.set_default_isolation_level(sqlpp::isolation_level::read_uncommitted);
auto tx = start_transaction(db); auto tx = start_transaction(db);
assert(db.is_transaction_active());
pragmaValue = db(custom_query(sqlpp::verbatim("PRAGMA read_uncommitted")) pragmaValue = db(custom_query(sqlpp::verbatim("PRAGMA read_uncommitted"))
.with_result_type_of(select(sqlpp::value(1).as(pragma)))) .with_result_type_of(select(sqlpp::value(1).as(pragma))))
.front() .front()
@ -78,6 +81,7 @@ int Transaction(int, char*[])
assert(current_level == sqlpp::isolation_level::read_uncommitted); assert(current_level == sqlpp::isolation_level::read_uncommitted);
tx.commit(); tx.commit();
assert(db.is_transaction_active() == false);
std::cerr << "--------------------------------------" << std::endl; std::cerr << "--------------------------------------" << std::endl;
return 0; return 0;