Add SAVEPOINT support to enable nested gnc_dbi_transaction calls.

This commit is contained in:
John Ralls 2017-02-16 11:09:56 -08:00
parent 9f0b086546
commit 2a2369b297
4 changed files with 82 additions and 51 deletions

View File

@ -31,6 +31,7 @@ extern "C"
#include <string>
#include <regex>
#include <sstream>
#include "gnc-dbisqlconnection.hpp"
@ -86,7 +87,7 @@ GncDbiSqlConnection::GncDbiSqlConnection (DbType type, QofBackend* qbe,
make_dbi_provider<DbType::DBI_MYSQL>() :
make_dbi_provider<DbType::DBI_PGSQL>()},
m_conn_ok{true}, m_last_error{ERR_BACKEND_NO_ERR}, m_error_repeat{0},
m_retry{false}
m_retry{false}, m_sql_savepoint{0}
{
if (!lock_database(ignore_lock))
throw std::runtime_error("Failed to lock database!");
@ -185,7 +186,7 @@ GncDbiSqlConnection::unlock_database ()
"SELECT * FROM %s WHERE Hostname = '%s' "
"AND PID = '%d'", lock_table.c_str(),
hostname,
(int)GETPID ());
(int)GETPID ());
if (result && dbi_result_get_numrows (result))
{
if (result)
@ -303,75 +304,104 @@ GncDbiSqlConnection::begin_transaction () noexcept
{
PERR ("gnc_dbi_verify_conn() failed\n");
qof_backend_set_error (m_qbe, ERR_BACKEND_SERVER_ERR);
return FALSE;
return false;
}
do
{
init_error ();
result = dbi_conn_queryf (m_conn, "BEGIN");
if (m_sql_savepoint == 0)
result = dbi_conn_queryf (m_conn, "BEGIN");
else
{
std::ostringstream savepoint("savepoint_");
savepoint << m_sql_savepoint;
result = dbi_conn_queryf(m_conn, "SAVEPOINT %s",
savepoint.str().c_str());
}
}
while (m_retry);
auto success = (result != nullptr);
auto status = dbi_result_free (result);
if (status < 0)
{
PERR ("Error in dbi_result_free() result\n");
qof_backend_set_error (m_qbe, ERR_BACKEND_SERVER_ERR);
}
if (!success)
if (!result)
{
PERR ("BEGIN transaction failed()\n");
qof_backend_set_error (m_qbe, ERR_BACKEND_SERVER_ERR);
return false;
}
return success;
}
bool
GncDbiSqlConnection::rollback_transaction () const noexcept
{
DEBUG ("ROLLBACK\n");
const char* command = "ROLLBACK";
auto result = dbi_conn_query (m_conn, command);
auto success = (result != nullptr);
auto status = dbi_result_free (result);
if (status < 0)
if (dbi_result_free (result) < 0)
{
PERR ("Error in dbi_result_free() result\n");
qof_backend_set_error (m_qbe, ERR_BACKEND_SERVER_ERR);
return false;
}
if (!success)
++m_sql_savepoint;
return true;
}
bool
GncDbiSqlConnection::rollback_transaction () noexcept
{
DEBUG ("ROLLBACK\n");
if (m_sql_savepoint == 0) return false;
dbi_result result;
if (m_sql_savepoint == 1)
result = dbi_conn_query (m_conn, "ROLLBACK");
else
{
std::ostringstream savepoint("savepoint_");
savepoint << m_sql_savepoint;
result = dbi_conn_queryf(m_conn, "ROLLBACK TO SAVEPOINT %s",
savepoint.str().c_str());
}
if (!result)
{
PERR ("Error in conn_rollback_transaction()\n");
qof_backend_set_error (m_qbe, ERR_BACKEND_SERVER_ERR);
return false;
}
return success;
}
bool
GncDbiSqlConnection::commit_transaction () const noexcept
{
DEBUG ("COMMIT\n");
auto result = dbi_conn_queryf (m_conn, "COMMIT");
auto success = (result != nullptr);
auto status = dbi_result_free (result);
if (status < 0)
if (dbi_result_free (result) < 0)
{
PERR ("Error in dbi_result_free() result\n");
qof_backend_set_error (m_qbe, ERR_BACKEND_SERVER_ERR);
return false;
}
if (!success)
--m_sql_savepoint;
return true;
}
bool
GncDbiSqlConnection::commit_transaction () noexcept
{
DEBUG ("COMMIT\n");
if (m_sql_savepoint == 0) return false;
dbi_result result;
if (m_sql_savepoint == 1)
result = dbi_conn_queryf (m_conn, "COMMIT");
else
{
std::ostringstream savepoint("savepoint_");
savepoint << m_sql_savepoint;
result = dbi_conn_queryf(m_conn, "RELEASE SAVEPOINT %s",
savepoint.str().c_str());
}
if (!result)
{
PERR ("Error in conn_commit_transaction()\n");
qof_backend_set_error (m_qbe, ERR_BACKEND_SERVER_ERR);
return false;
}
return success;
if (dbi_result_free (result) < 0)
{
PERR ("Error in dbi_result_free() result\n");
qof_backend_set_error (m_qbe, ERR_BACKEND_SERVER_ERR);
return false;
}
--m_sql_savepoint;
return true;
}

View File

@ -52,8 +52,8 @@ public:
const noexcept override;
bool does_table_exist (const std::string&) const noexcept override;
bool begin_transaction () noexcept override;
bool rollback_transaction () const noexcept override;
bool commit_transaction () const noexcept override;
bool rollback_transaction () noexcept override;
bool commit_transaction () noexcept override;
bool create_table (const std::string&, const ColVec&) const noexcept override;
bool create_index (const std::string&, const std::string&, const EntryVec&)
const noexcept override;
@ -65,7 +65,7 @@ public:
QofBackend* qbe () const noexcept { return m_qbe; }
dbi_conn conn() const noexcept { return m_conn; }
inline void set_error(int error, unsigned int repeat,
bool retry) noexcept override
bool retry) noexcept override
{
m_last_error = error;
m_error_repeat = repeat;
@ -103,10 +103,11 @@ private:
*/
unsigned int m_error_repeat;
/** Signals the calling function that it should retry (the error handler
* detected transient error and managed to resolve it, but it can't run the
* original query)
* detected a transient error and managed to resolve it, but it can't run
* the original query)
*/
gboolean m_retry;
bool m_retry;
unsigned int m_sql_savepoint;
bool lock_database(bool ignore_lock);
void unlock_database();

View File

@ -75,9 +75,9 @@ public:
/** Returns TRUE if successful, false if error */
virtual bool begin_transaction () noexcept = 0;
/** Returns TRUE if successful, FALSE if error */
virtual bool rollback_transaction () const noexcept = 0;
virtual bool rollback_transaction () noexcept = 0;
/** Returns TRUE if successful, FALSE if error */
virtual bool commit_transaction () const noexcept = 0;
virtual bool commit_transaction () noexcept = 0;
/** Returns TRUE if successful, FALSE if error */
virtual bool create_table (const std::string&, const ColVec&)
const noexcept = 0;

View File

@ -107,8 +107,8 @@ public:
bool does_table_exist (const std::string&) const noexcept override {
return true; }
bool begin_transaction () noexcept override { return true;}
bool rollback_transaction () const noexcept override { return true; }
bool commit_transaction () const noexcept override { return true; }
bool rollback_transaction () noexcept override { return true; }
bool commit_transaction () noexcept override { return true; }
bool create_table (const std::string&, const ColVec&)
const noexcept override { return false; }
bool create_index (const std::string&, const std::string&,