Convert GncSqlBackend and GncDbiBackend into a class hierarchy.

This is a rather complex change, because it also begins to separate the
responsibilities of the backends and GncSqlConnection.
This commit is contained in:
John Ralls 2016-07-21 16:21:59 -07:00
parent d106346316
commit 6f67e2dd1a
29 changed files with 839 additions and 772 deletions

View File

@ -68,14 +68,6 @@ extern "C"
#include "splint-defs.h"
#endif
#ifdef G_OS_WIN32
#include <winsock2.h>
#define GETPID() GetCurrentProcessId()
#else
#include <limits.h>
#include <unistd.h>
#define GETPID() getpid()
#endif
/* For direct access to dbi data structs, sadly needed for datetime */
#include <dbi/dbi-dev.h>
@ -104,7 +96,6 @@ static dbi_inst dbi_instance = nullptr;
#define HAVE_LIBDBI_TO_LONGLONG 0
#endif
#define GNC_HOST_NAME_MAX 255
#define TRANSACTION_NAME "trans"
static QofLogModule log_module = G_LOG_DOMAIN;
@ -118,11 +109,10 @@ static gchar lock_table[] = "gnclock";
#define PGSQL_DEFAULT_PORT 5432
#define SQLITE3_TIMESPEC_STR_FORMAT "%04d%02d%02d%02d%02d%02d"
#define MYSQL_TIMESPEC_STR_FORMAT "%04d%02d%02d%02d%02d%02d"
#define PGSQL_TIMESPEC_STR_FORMAT "%04d%02d%02d %02d%02d%02d"
#define MYSQL_TIMESPEC_STR_FORMAT "%04d%02d%02d%02d%02d%02d"
#define PGSQL_TIMESPEC_STR_FORMAT "%04d%02d%02d %02d%02d%02d"
static gboolean gnc_dbi_lock_database (QofBackend *qbe, gboolean ignore_lock);
static void gnc_dbi_unlock (QofBackend *qbe);
static gboolean gnc_dbi_lock_database (QofBackend*, dbi_conn, gboolean);
static gboolean save_may_clobber_data (QofBackend* qbe);
static GncDbiTestResult conn_test_dbi_library (dbi_conn conn);
@ -175,23 +165,21 @@ create_tables(const OBEEntry& entry, GncDbiBackend* be)
std::tie(type, obe) = entry;
g_return_if_fail(obe->is_version (GNC_SQL_BACKEND_VERSION));
obe->create_tables (&be->sql_be);
obe->create_tables (be);
}
static void
void
sqlite3_error_fn (dbi_conn conn, void* user_data)
{
const gchar* msg;
GncDbiBackend *be = static_cast<decltype(be)>(user_data);
/* FIXME: GncSqlConnection doesn't have the error calls so we have to dynamic_cast from the connection stored in GncSqlBackend. Yuck. */
GncDbiSqlConnection *dbi_conn =
dynamic_cast<decltype(dbi_conn)>(be->sql_be.conn);
int errnum = dbi_conn_error (conn, &msg);
PERR ("DBI error: %s\n", msg);
dbi_conn->set_error (ERR_BACKEND_MISC, 0, false);
if (be->connected())
be->set_error (ERR_BACKEND_MISC, 0, false);
}
static void
void
gnc_dbi_sqlite3_session_begin (QofBackend* qbe, QofSession* session,
const gchar* book_id, gboolean ignore_lock,
gboolean create, gboolean force)
@ -232,22 +220,18 @@ gnc_dbi_sqlite3_session_begin (QofBackend* qbe, QofSession* session,
goto exit;
}
if (be->conn != nullptr)
{
dbi_conn_close (be->conn);
}
be->connect(nullptr);
dbi_conn conn;
#if HAVE_LIBDBI_R
if (dbi_instance)
be->conn = dbi_conn_new_r ("sqlite3", dbi_instance);
conn = dbi_conn_new_r ("sqlite3", dbi_instance);
else
PERR ("Attempt to connect with an uninitialized dbi_instance");
#else
be->conn = dbi_conn_new ("sqlite3");
conn = dbi_conn_new ("sqlite3");
#endif
if (be->conn == nullptr)
if (conn == nullptr)
{
PERR ("Unable to create sqlite3 dbi connection\n");
qof_backend_set_error (qbe, ERR_BACKEND_BAD_URL);
@ -256,30 +240,30 @@ gnc_dbi_sqlite3_session_begin (QofBackend* qbe, QofSession* session,
dirname = g_path_get_dirname (filepath);
basename = g_path_get_basename (filepath);
dbi_conn_error_handler (be->conn, sqlite3_error_fn, be);
dbi_conn_error_handler (conn, sqlite3_error_fn, be);
/* dbi-sqlite3 documentation says that sqlite3 doesn't take a "host" option */
result = dbi_conn_set_option (be->conn, "host", "localhost");
result = dbi_conn_set_option (conn, "host", "localhost");
if (result < 0)
{
PERR ("Error setting 'host' option\n");
qof_backend_set_error (qbe, ERR_BACKEND_SERVER_ERR);
goto exit;
}
result = dbi_conn_set_option (be->conn, "dbname", basename);
result = dbi_conn_set_option (conn, "dbname", basename);
if (result < 0)
{
PERR ("Error setting 'dbname' option\n");
qof_backend_set_error (qbe, ERR_BACKEND_SERVER_ERR);
goto exit;
}
result = dbi_conn_set_option (be->conn, "sqlite3_dbdir", dirname);
result = dbi_conn_set_option (conn, "sqlite3_dbdir", dirname);
if (result < 0)
{
PERR ("Error setting 'sqlite3_dbdir' option\n");
qof_backend_set_error (qbe, ERR_BACKEND_SERVER_ERR);
goto exit;
}
result = dbi_conn_connect (be->conn);
result = dbi_conn_connect (conn);
if (result < 0)
{
@ -288,7 +272,7 @@ gnc_dbi_sqlite3_session_begin (QofBackend* qbe, QofSession* session,
goto exit;
}
dbi_test_result = conn_test_dbi_library (be->conn);
dbi_test_result = conn_test_dbi_library (conn);
switch (dbi_test_result)
{
case GNC_DBI_PASS:
@ -311,27 +295,24 @@ gnc_dbi_sqlite3_session_begin (QofBackend* qbe, QofSession* session,
if (create && !file_exists) /* File didn't exist before, but it */
{
/* does now, and we don't want to */
dbi_conn_close (be->conn); /* leave it lying around. */
be->conn = nullptr;
dbi_conn_close (conn); /* leave it lying around. */
conn = nullptr;
g_unlink (filepath);
}
msg = "Bad DBI Library";
goto exit;
}
if (!gnc_dbi_lock_database (qbe, ignore_lock))
if (!gnc_dbi_lock_database (qbe, conn, ignore_lock))
{
qof_backend_set_error (qbe, ERR_BACKEND_LOCKED);
msg = "Locked";
goto exit;
}
if (be->sql_be.conn != nullptr)
{
delete (be->sql_be.conn);
}
be->sql_be.conn = new GncDbiSqlConnection (new GncDbiProviderImpl<DbType::DBI_SQLITE>,
qbe, be->conn);
be->sql_be.timespec_format = SQLITE3_TIMESPEC_STR_FORMAT;
be->connect(nullptr);
be->connect(
new GncDbiSqlConnection (new GncDbiProviderImpl<DbType::DBI_SQLITE>,
qbe, conn, lock_table));
/* We should now have a proper session set up.
* Let's start logging */
@ -378,8 +359,6 @@ static void
mysql_error_fn (dbi_conn conn, void* user_data)
{
GncDbiBackend* be = (GncDbiBackend*)user_data;
GncDbiSqlConnection* dbi_conn =
dynamic_cast<decltype(dbi_conn)>(be->sql_be.conn);
const char* msg;
auto err_num = dbi_conn_error (conn, &msg);
@ -394,7 +373,7 @@ mysql_error_fn (dbi_conn conn, void* user_data)
if (err_num == 1049) // Database doesn't exist
{
PINFO ("DBI error: %s\n", msg);
be->exists = FALSE;
be->set_exists(false);
return;
}
@ -402,7 +381,7 @@ mysql_error_fn (dbi_conn conn, void* user_data)
* has been initialized. So let's assert it exits here, otherwise
* simply return.
*/
if (!dbi_conn)
if (!be->connected())
{
PINFO ("DBI error: %s\n", msg);
PINFO ("Note: GbcDbiSqlConnection not yet initialized. Skipping further error processing.");
@ -413,20 +392,18 @@ mysql_error_fn (dbi_conn conn, void* user_data)
if (err_num == 2006) // Server has gone away
{
PINFO ("DBI error: %s - Reconnecting...\n", msg);
if (dbi_conn)
dbi_conn->set_error (ERR_BACKEND_CONN_LOST, 1, true);
dbi_conn->retry_connection(msg);
be->set_error (ERR_BACKEND_CONN_LOST, 1, true);
be->retry_connection(msg);
}
else if (err_num == 2003) // Unable to connect
{
dbi_conn->set_error (ERR_BACKEND_CANT_CONNECT, 1, true);
dbi_conn->retry_connection (msg);
be->set_error (ERR_BACKEND_CANT_CONNECT, 1, true);
be->retry_connection (msg);
}
else // Any other error
{
PERR ("DBI error: %s\n", msg);
if (dbi_conn)
dbi_conn->set_error (ERR_BACKEND_MISC, 0, FALSE);
be->set_error (ERR_BACKEND_MISC, 0, FALSE);
}
}
@ -496,17 +473,17 @@ set_standard_connection_options (QofBackend* qbe, dbi_conn conn,
return TRUE;
}
/* FIXME: Move to GncDbiSqlConnection. */
static gboolean
gnc_dbi_lock_database (QofBackend* qbe, gboolean ignore_lock)
gnc_dbi_lock_database (QofBackend* qbe, dbi_conn conn, gboolean ignore_lock)
{
GncDbiBackend* qe = (GncDbiBackend*)qbe;
dbi_conn dcon = qe->conn;
dbi_result result;
const gchar* dbname = dbi_conn_get_option (dcon, "dbname");
const gchar* dbname = dbi_conn_get_option (conn, "dbname");
/* Create the table if it doesn't exist */
result = dbi_conn_get_table_list (dcon, dbname, lock_table);
result = dbi_conn_get_table_list (conn, dbname, lock_table);
if (! (result && dbi_result_get_numrows (result)))
{
if (result)
@ -514,13 +491,13 @@ gnc_dbi_lock_database (QofBackend* qbe, gboolean ignore_lock)
dbi_result_free (result);
result = nullptr;
}
result = dbi_conn_queryf (dcon,
result = dbi_conn_queryf (conn,
"CREATE TABLE %s ( Hostname varchar(%d), PID int )", lock_table,
GNC_HOST_NAME_MAX);
if (dbi_conn_error (dcon, nullptr))
if (dbi_conn_error (conn, nullptr))
{
const gchar* errstr;
dbi_conn_error (dcon, &errstr);
dbi_conn_error (conn, &errstr);
PERR ("Error %s creating lock table", errstr);
qof_backend_set_error (qbe, ERR_BACKEND_SERVER_ERR);
if (result)
@ -543,7 +520,7 @@ gnc_dbi_lock_database (QofBackend* qbe, gboolean ignore_lock)
}
/* Protect everything with a single transaction to prevent races */
if ((result = dbi_conn_query (dcon, "BEGIN")))
if ((result = dbi_conn_query (conn, "BEGIN")))
{
/* Check for an existing entry; delete it if ignore_lock is true, otherwise fail */
gchar hostname[ GNC_HOST_NAME_MAX + 1 ];
@ -552,7 +529,7 @@ gnc_dbi_lock_database (QofBackend* qbe, gboolean ignore_lock)
dbi_result_free (result);
result = nullptr;
}
result = dbi_conn_queryf (dcon, "SELECT * FROM %s", lock_table);
result = dbi_conn_queryf (conn, "SELECT * FROM %s", lock_table);
if (result && dbi_result_get_numrows (result))
{
dbi_result_free (result);
@ -561,15 +538,15 @@ gnc_dbi_lock_database (QofBackend* qbe, gboolean ignore_lock)
{
qof_backend_set_error (qbe, ERR_BACKEND_LOCKED);
/* FIXME: After enhancing the qof_backend_error mechanism, report in the dialog what is the hostname of the machine holding the lock. */
dbi_conn_query (dcon, "ROLLBACK");
dbi_conn_query (conn, "ROLLBACK");
return FALSE;
}
result = dbi_conn_queryf (dcon, "DELETE FROM %s", lock_table);
result = dbi_conn_queryf (conn, "DELETE FROM %s", lock_table);
if (!result)
{
qof_backend_set_error (qbe, ERR_BACKEND_SERVER_ERR);
qof_backend_set_message (qbe, "Failed to delete lock record");
result = dbi_conn_query (dcon, "ROLLBACK");
result = dbi_conn_query (conn, "ROLLBACK");
if (result)
{
dbi_result_free (result);
@ -586,14 +563,14 @@ gnc_dbi_lock_database (QofBackend* qbe, gboolean ignore_lock)
/* Add an entry and commit the transaction */
memset (hostname, 0, sizeof (hostname));
gethostname (hostname, GNC_HOST_NAME_MAX);
result = dbi_conn_queryf (dcon,
result = dbi_conn_queryf (conn,
"INSERT INTO %s VALUES ('%s', '%d')",
lock_table, hostname, (int)GETPID ());
if (!result)
{
qof_backend_set_error (qbe, ERR_BACKEND_SERVER_ERR);
qof_backend_set_message (qbe, "Failed to create lock record");
result = dbi_conn_query (dcon, "ROLLBACK");
result = dbi_conn_query (conn, "ROLLBACK");
if (result)
{
dbi_result_free (result);
@ -606,7 +583,7 @@ gnc_dbi_lock_database (QofBackend* qbe, gboolean ignore_lock)
dbi_result_free (result);
result = nullptr;
}
result = dbi_conn_query (dcon, "COMMIT");
result = dbi_conn_query (conn, "COMMIT");
if (result)
{
dbi_result_free (result);
@ -624,96 +601,6 @@ gnc_dbi_lock_database (QofBackend* qbe, gboolean ignore_lock)
}
return FALSE;
}
static void
gnc_dbi_unlock (QofBackend* qbe)
{
GncDbiBackend* qe = (GncDbiBackend*)qbe;
dbi_conn dcon = qe->conn;
dbi_result result;
const gchar* dbname = nullptr;
g_return_if_fail (dcon != nullptr);
g_return_if_fail (dbi_conn_error (dcon, nullptr) == 0);
dbname = dbi_conn_get_option (dcon, "dbname");
/* Check if the lock table exists */
g_return_if_fail (dbname != nullptr);
result = dbi_conn_get_table_list (dcon, dbname, lock_table);
if (! (result && dbi_result_get_numrows (result)))
{
if (result)
{
dbi_result_free (result);
result = nullptr;
}
PWARN ("No lock table in database, so not unlocking it.");
return;
}
dbi_result_free (result);
result = dbi_conn_query (dcon, "BEGIN");
if (result)
{
/* Delete the entry if it's our hostname and PID */
gchar hostname[ GNC_HOST_NAME_MAX + 1 ];
dbi_result_free (result);
result = nullptr;
memset (hostname, 0, sizeof (hostname));
gethostname (hostname, GNC_HOST_NAME_MAX);
result = dbi_conn_queryf (dcon,
"SELECT * FROM %s WHERE Hostname = '%s' AND PID = '%d'", lock_table, hostname,
(int)GETPID ());
if (result && dbi_result_get_numrows (result))
{
if (result)
{
dbi_result_free (result);
result = nullptr;
}
result = dbi_conn_queryf (dcon, "DELETE FROM %s", lock_table);
if (!result)
{
PERR ("Failed to delete the lock entry");
qof_backend_set_error (qbe, ERR_BACKEND_SERVER_ERR);
result = dbi_conn_query (dcon, "ROLLBACK");
if (result)
{
dbi_result_free (result);
result = nullptr;
}
return;
}
else
{
dbi_result_free (result);
result = nullptr;
}
result = dbi_conn_query (dcon, "COMMIT");
if (result)
{
dbi_result_free (result);
result = nullptr;
}
return;
}
result = dbi_conn_query (dcon, "ROLLBACK");
if (result)
{
dbi_result_free (result);
result = nullptr;
}
PWARN ("There was no lock entry in the Lock table");
return;
}
if (result)
{
dbi_result_free (result);
result = nullptr;
}
PWARN ("Unable to get a lock on LOCK, so failed to clear the lock entry.");
qof_backend_set_error (qbe, ERR_BACKEND_SERVER_ERR);
}
#define SQL_OPTION_TO_REMOVE "NO_ZERO_DATE"
@ -815,36 +702,34 @@ gnc_dbi_mysql_session_begin (QofBackend* qbe, QofSession* session,
// Try to connect to the db. If it doesn't exist and the create
// flag is TRUE, we'll need to connect to the 'mysql' db and execute the
// CREATE DATABASE ddl statement there.
if (be->conn != nullptr)
{
dbi_conn_close (be->conn);
}
be->connect(nullptr);
dbi_conn conn;
#if HAVE_LIBDBI_R
if (dbi_instance)
be->conn = dbi_conn_new_r ("mysql", dbi_instance);
conn = dbi_conn_new_r ("mysql", dbi_instance);
else
PERR ("Attempt to connect with an uninitialized dbi_instance");
#else
be->conn = dbi_conn_new ("mysql");
conn = dbi_conn_new ("mysql");
#endif
if (be->conn == nullptr)
if (conn == nullptr)
{
PERR ("Unable to create mysql dbi connection\n");
qof_backend_set_error (qbe, ERR_BACKEND_BAD_URL);
goto exit;
}
dbi_conn_error_handler (be->conn, mysql_error_fn, be);
if (!set_standard_connection_options (qbe, be->conn, host, portnum, dbname,
dbi_conn_error_handler (conn, mysql_error_fn, be);
if (!set_standard_connection_options (qbe, conn, host, portnum, dbname,
username, password))
{
goto exit;
}
be->exists = TRUE;
result = dbi_conn_connect (be->conn);
be->set_exists(true);
result = dbi_conn_connect (conn);
if (result == 0)
{
adjust_sql_options (be->conn);
dbi_test_result = conn_test_dbi_library (be->conn);
adjust_sql_options (conn);
dbi_test_result = conn_test_dbi_library (conn);
switch (dbi_test_result)
{
case GNC_DBI_PASS:
@ -873,12 +758,12 @@ gnc_dbi_mysql_session_begin (QofBackend* qbe, QofSession* session,
goto exit;
}
success = gnc_dbi_lock_database (qbe, ignore_lock);
success = gnc_dbi_lock_database (qbe, conn, ignore_lock);
}
else
{
if (be->exists)
if (be->exists())
{
PERR ("Unable to connect to database '%s'\n", dbname);
qof_backend_set_error (qbe, ERR_BACKEND_SERVER_ERR);
@ -889,22 +774,23 @@ gnc_dbi_mysql_session_begin (QofBackend* qbe, QofSession* session,
if (create)
{
dbi_result dresult;
result = dbi_conn_set_option (be->conn, "dbname", "mysql");
result = dbi_conn_set_option (conn, "dbname", "mysql");
if (result < 0)
{
PERR ("Error setting 'dbname' option\n");
qof_backend_set_error (qbe, ERR_BACKEND_SERVER_ERR);
goto exit;
}
result = dbi_conn_connect (be->conn);
result = dbi_conn_connect (conn);
if (result < 0)
{
PERR ("Unable to connect to 'mysql' database\n");
qof_backend_set_error (qbe, ERR_BACKEND_SERVER_ERR);
goto exit;
}
adjust_sql_options (be->conn);
dresult = dbi_conn_queryf (be->conn, "CREATE DATABASE %s CHARACTER SET utf8",
adjust_sql_options (conn);
dresult = dbi_conn_queryf (conn,
"CREATE DATABASE %s CHARACTER SET utf8",
dbname);
if (dresult == nullptr)
{
@ -912,39 +798,40 @@ gnc_dbi_mysql_session_begin (QofBackend* qbe, QofSession* session,
qof_backend_set_error (qbe, ERR_BACKEND_SERVER_ERR);
goto exit;
}
dbi_conn_close (be->conn);
dbi_conn_close (conn);
conn = nullptr;
// Try again to connect to the db
#if HAVE_LIBDBI_R
if (dbi_instance)
be->conn = dbi_conn_new_r ("mysql", dbi_instance);
conn = dbi_conn_new_r ("mysql", dbi_instance);
else
PERR ("Attempt to connect with an uninitialized dbi_instance");
#else
be->conn = dbi_conn_new ("mysql");
conn = dbi_conn_new ("mysql");
#endif
if (be->conn == nullptr)
if (conn == nullptr)
{
PERR ("Unable to create mysql dbi connection\n");
qof_backend_set_error (qbe, ERR_BACKEND_BAD_URL);
goto exit;
}
dbi_conn_error_handler (be->conn, mysql_error_fn, be);
if (!set_standard_connection_options (qbe, be->conn, host, 0, dbname, username,
password))
dbi_conn_error_handler (conn, mysql_error_fn, be);
if (!set_standard_connection_options (qbe, conn, host, 0, dbname,
username, password))
{
goto exit;
}
result = dbi_conn_connect (be->conn);
result = dbi_conn_connect (conn);
if (result < 0)
{
PERR ("Unable to create database '%s'\n", dbname);
qof_backend_set_error (qbe, ERR_BACKEND_SERVER_ERR);
goto exit;
}
adjust_sql_options (be->conn);
dbi_test_result = conn_test_dbi_library (be->conn);
adjust_sql_options (conn);
dbi_test_result = conn_test_dbi_library (conn);
switch (dbi_test_result)
{
case GNC_DBI_PASS:
@ -964,10 +851,10 @@ gnc_dbi_mysql_session_begin (QofBackend* qbe, QofSession* session,
}
if (dbi_test_result != GNC_DBI_PASS)
{
dbi_conn_queryf (be->conn, "DROP DATABASE %s", dbname);
dbi_conn_queryf (conn, "DROP DATABASE %s", dbname);
goto exit;
}
success = gnc_dbi_lock_database (qbe, ignore_lock);
success = gnc_dbi_lock_database (qbe, conn, ignore_lock);
}
else
{
@ -979,15 +866,11 @@ gnc_dbi_mysql_session_begin (QofBackend* qbe, QofSession* session,
if (success)
{
dbi_result dresult;
if (be->sql_be.conn != nullptr)
{
delete (be->sql_be.conn);
}
be->sql_be.conn = new GncDbiSqlConnection (new GncDbiProviderImpl<DbType::DBI_MYSQL>,
qbe, be->conn);
be->connect(nullptr);
be->connect(
new GncDbiSqlConnection (new GncDbiProviderImpl<DbType::DBI_MYSQL>,
qbe, conn, lock_table));
}
be->sql_be.timespec_format = MYSQL_TIMESPEC_STR_FORMAT;
/* We should now have a proper session set up.
* Let's start logging */
@ -1066,8 +949,6 @@ static void
pgsql_error_fn (dbi_conn conn, void* user_data)
{
GncDbiBackend* be = (GncDbiBackend*)user_data;
GncDbiSqlConnection* dbi_conn =
dynamic_cast<decltype(dbi_conn)>(be->sql_be.conn);
const gchar* msg;
(void)dbi_conn_error (conn, &msg);
@ -1075,32 +956,32 @@ pgsql_error_fn (dbi_conn conn, void* user_data)
g_str_has_suffix (msg, "does not exist\n"))
{
PINFO ("DBI error: %s\n", msg);
be->exists = FALSE;
dbi_conn->set_error (ERR_BACKEND_NO_SUCH_DB, 0, FALSE);
be->set_exists(false);
be->set_error (ERR_BACKEND_NO_SUCH_DB, 0, FALSE);
}
else if (g_strrstr (msg,
"server closed the connection unexpectedly")) // Connection lost
{
if (dbi_conn == nullptr)
if (!be->connected())
{
PWARN ("DBI Error: Connection lost, connection pointer invalid");
return;
}
PINFO ("DBI error: %s - Reconnecting...\n", msg);
dbi_conn->set_error (ERR_BACKEND_CONN_LOST, 1, true);
dbi_conn->retry_connection(msg);
be->set_error (ERR_BACKEND_CONN_LOST, 1, true);
be->retry_connection(msg);
}
else if (dbi_conn &&
else if (be->connected() &&
(g_str_has_prefix (msg, "connection pointer is NULL") ||
g_str_has_prefix (msg, "could not connect to server"))) // No connection
{
dbi_conn->set_error(ERR_BACKEND_CANT_CONNECT, 1, true);
dbi_conn->retry_connection (msg);
be->set_error(ERR_BACKEND_CANT_CONNECT, 1, true);
be->retry_connection (msg);
}
else
{
PERR ("DBI error: %s\n", msg);
dbi_conn->set_error (ERR_BACKEND_MISC, 0, false);
be->set_error (ERR_BACKEND_MISC, 0, false);
}
}
@ -1144,37 +1025,34 @@ gnc_dbi_postgres_session_begin (QofBackend* qbe, QofSession* session,
// Try to connect to the db. If it doesn't exist and the create
// flag is TRUE, we'll need to connect to the 'postgres' db and execute the
// CREATE DATABASE ddl statement there.
if (be->conn != nullptr)
{
dbi_conn_close (be->conn);
}
be->connect(nullptr);
dbi_conn conn;
#if HAVE_LIBDBI_R
if (dbi_instance)
be->conn = dbi_conn_new_r ("pgsql", dbi_instance);
conn = dbi_conn_new_r ("pgsql", dbi_instance);
else
PERR ("Attempt to connect with an uninitialized dbi_instance");
#else
be->conn = dbi_conn_new ("pgsql");
conn = dbi_conn_new ("pgsql");
#endif
if (be->conn == nullptr)
if (conn == nullptr)
{
PERR ("Unable to create pgsql dbi connection\n");
qof_backend_set_error (qbe, ERR_BACKEND_BAD_URL);
goto exit;
}
dbi_conn_error_handler (be->conn, pgsql_error_fn, be);
if (!set_standard_connection_options (qbe, be->conn, host, portnum, dbnamelc,
dbi_conn_error_handler (conn, pgsql_error_fn, be);
if (!set_standard_connection_options (qbe, conn, host, portnum, dbnamelc,
username, password))
{
goto exit;
}
be->exists = TRUE;
result = dbi_conn_connect (be->conn);
be->set_exists(true);
result = dbi_conn_connect (conn);
if (result == 0)
{
dbi_test_result = conn_test_dbi_library (be->conn);
dbi_test_result = conn_test_dbi_library (conn);
switch (dbi_test_result)
{
case GNC_DBI_PASS:
@ -1203,12 +1081,12 @@ gnc_dbi_postgres_session_begin (QofBackend* qbe, QofSession* session,
goto exit;
}
success = gnc_dbi_lock_database (qbe, ignore_lock);
success = gnc_dbi_lock_database (qbe, conn, ignore_lock);
}
else
{
if (be->exists)
if (be->exists())
{
PERR ("Unable to connect to database '%s'\n", dbname);
qof_backend_set_error (qbe, ERR_BACKEND_SERVER_ERR);
@ -1219,21 +1097,21 @@ gnc_dbi_postgres_session_begin (QofBackend* qbe, QofSession* session,
if (create)
{
dbi_result dresult;
result = dbi_conn_set_option (be->conn, "dbname", "postgres");
result = dbi_conn_set_option (conn, "dbname", "postgres");
if (result < 0)
{
PERR ("Error setting 'dbname' option\n");
qof_backend_set_error (qbe, ERR_BACKEND_SERVER_ERR);
goto exit;
}
result = dbi_conn_connect (be->conn);
result = dbi_conn_connect (conn);
if (result < 0)
{
PERR ("Unable to connect to 'postgres' database\n");
qof_backend_set_error (qbe, ERR_BACKEND_SERVER_ERR);
goto exit;
}
dresult = dbi_conn_queryf (be->conn,
dresult = dbi_conn_queryf (conn,
"CREATE DATABASE %s WITH TEMPLATE template0 ENCODING 'UTF8'", dbnamelc);
if (dresult == nullptr)
{
@ -1241,40 +1119,41 @@ gnc_dbi_postgres_session_begin (QofBackend* qbe, QofSession* session,
qof_backend_set_error (qbe, ERR_BACKEND_SERVER_ERR);
goto exit;
}
dbi_conn_queryf (be->conn,
dbi_conn_queryf (conn,
"ALTER DATABASE %s SET standard_conforming_strings TO on", dbnamelc);
dbi_conn_close (be->conn);
dbi_conn_close (conn);
// Try again to connect to the db
#if HAVE_LIBDBI_R
if (dbi_instance)
be->conn = dbi_conn_new_r ("pgsql", dbi_instance);
conn = dbi_conn_new_r ("pgsql", dbi_instance);
else
PERR ("Attempt to connect with an uninitialized dbi_instance");
#else
be->conn = dbi_conn_new ("pgsql");
conn = dbi_conn_new ("pgsql");
#endif
if (be->conn == nullptr)
if (conn == nullptr)
{
PERR ("Unable to create pgsql dbi connection\n");
qof_backend_set_error (qbe, ERR_BACKEND_BAD_URL);
goto exit;
}
dbi_conn_error_handler (be->conn, pgsql_error_fn, be);
if (!set_standard_connection_options (qbe, be->conn, host, PGSQL_DEFAULT_PORT,
dbi_conn_error_handler (conn, pgsql_error_fn, be);
if (!set_standard_connection_options (qbe, conn, host,
PGSQL_DEFAULT_PORT,
dbnamelc, username, password))
{
goto exit;
}
result = dbi_conn_connect (be->conn);
result = dbi_conn_connect (conn);
if (result < 0)
{
PERR ("Unable to create database '%s'\n", dbname);
qof_backend_set_error (qbe, ERR_BACKEND_SERVER_ERR);
goto exit;
}
dbi_test_result = conn_test_dbi_library (be->conn);
dbi_test_result = conn_test_dbi_library (conn);
switch (dbi_test_result)
{
case GNC_DBI_PASS:
@ -1294,11 +1173,11 @@ gnc_dbi_postgres_session_begin (QofBackend* qbe, QofSession* session,
}
if (GNC_DBI_PASS != dbi_test_result)
{
dbi_conn_select_db (be->conn, "template1");
dbi_conn_queryf (be->conn, "DROP DATABASE %s", dbnamelc);
dbi_conn_select_db (conn, "template1");
dbi_conn_queryf (conn, "DROP DATABASE %s", dbnamelc);
goto exit;
}
success = gnc_dbi_lock_database (qbe, ignore_lock);
success = gnc_dbi_lock_database (qbe, conn, ignore_lock);
}
else
{
@ -1308,14 +1187,11 @@ gnc_dbi_postgres_session_begin (QofBackend* qbe, QofSession* session,
}
if (success)
{
if (be->sql_be.conn != nullptr)
{
delete (be->sql_be.conn);
}
be->sql_be.conn = new GncDbiSqlConnection (new GncDbiProviderImpl<DbType::DBI_PGSQL>,
qbe, be->conn);
be->connect(nullptr);
be->connect(
new GncDbiSqlConnection (new GncDbiProviderImpl<DbType::DBI_PGSQL>,
qbe, conn, lock_table));
}
be->sql_be.timespec_format = PGSQL_TIMESPEC_STR_FORMAT;
/* We should now have a proper session set up.
* Let's start logging */
@ -1370,17 +1246,8 @@ gnc_dbi_session_end (QofBackend* be_start)
ENTER (" ");
if (be->conn != nullptr)
{
gnc_dbi_unlock (be_start);
be->conn = nullptr;
}
if (be->sql_be.conn != nullptr)
{
delete (be->sql_be.conn);
be->sql_be.conn = nullptr;
}
gnc_sql_finalize_version_info (&be->sql_be);
be->finalize_version_info ();
be->connect(nullptr);
LEAVE (" ");
}
@ -1409,7 +1276,7 @@ gnc_dbi_destroy_backend (QofBackend* be)
* then the database will be loaded read-only. A resave will update
* both values to match this version of Gnucash.
*/
static void
void
gnc_dbi_load (QofBackend* qbe, QofBook* book, QofBackendLoadType loadType)
{
GncDbiBackend* be = (GncDbiBackend*)qbe;
@ -1421,10 +1288,10 @@ gnc_dbi_load (QofBackend* qbe, QofBook* book, QofBackendLoadType loadType)
if (loadType == LOAD_TYPE_INITIAL_LOAD)
{
g_assert (be->sql_be.book == nullptr);
// Set up table version information
gnc_sql_init_version_info (&be->sql_be);
be->init_version_info ();
g_assert (be->m_book == nullptr);
// Call all object backends to create any required tables
auto registry = gnc_sql_get_backend_registry();
@ -1432,9 +1299,9 @@ gnc_dbi_load (QofBackend* qbe, QofBook* book, QofBackendLoadType loadType)
create_tables(entry, be);
}
gnc_sql_load (&be->sql_be, book, loadType);
gnc_sql_load (be, book, loadType);
if (GNUCASH_RESAVE_VERSION > gnc_sql_get_table_version (&be->sql_be,
if (GNUCASH_RESAVE_VERSION > gnc_sql_get_table_version (be,
"Gnucash"))
{
/* The database was loaded with an older database schema or
@ -1442,7 +1309,7 @@ gnc_dbi_load (QofBackend* qbe, QofBook* book, QofBackendLoadType loadType)
* thing needs to be saved anew. */
qof_backend_set_error (qbe, ERR_SQL_DB_TOO_OLD);
}
else if (GNUCASH_RESAVE_VERSION < gnc_sql_get_table_version (&be->sql_be,
else if (GNUCASH_RESAVE_VERSION < gnc_sql_get_table_version (be,
"Gnucash-Resave"))
{
/* Worse, the database was created with a newer version. We
@ -1467,8 +1334,8 @@ save_may_clobber_data (QofBackend* qbe)
gboolean retval = FALSE;
/* Data may be clobbered iff the number of tables != 0 */
dbname = dbi_conn_get_option (be->conn, "dbname");
result = dbi_conn_get_table_list (be->conn, dbname, nullptr);
dbname = dbi_conn_get_option (be->conn(), "dbname");
result = dbi_conn_get_table_list (be->conn(), dbname, nullptr);
if (result)
{
retval = dbi_result_get_numrows (result) > 0;
@ -1583,16 +1450,15 @@ void
gnc_dbi_safe_sync_all (QofBackend* qbe, QofBook* book)
{
GncDbiBackend* be = (GncDbiBackend*)qbe;
GncDbiSqlConnection* conn = (GncDbiSqlConnection*) (((GncSqlBackend*)
be)->conn);
const gchar* dbname = nullptr;
auto conn = dynamic_cast<GncDbiSqlConnection*>(be->m_conn);
g_return_if_fail (conn != nullptr);
g_return_if_fail (be != nullptr);
g_return_if_fail (book != nullptr);
ENTER ("book=%p, primary=%p", book, be->sql_be.book);
dbname = dbi_conn_get_option (be->conn, "dbname");
auto table_list = conn->m_provider->get_table_list (conn->m_conn, dbname);
ENTER ("book=%p, primary=%p", book, be->m_book);
auto dbname = dbi_conn_get_option (conn->conn(), "dbname");
auto table_list = conn->m_provider->get_table_list (conn->conn(), dbname);
if (!conn_table_operation (conn, table_list, backup))
{
qof_backend_set_error (qbe, ERR_BACKEND_SERVER_ERR);
@ -1614,7 +1480,7 @@ gnc_dbi_safe_sync_all (QofBackend* qbe, QofBook* book)
}
}
gnc_sql_sync_all (&be->sql_be, book);
gnc_sql_sync_all (be, book);
if (qof_backend_check_error (qbe))
{
conn_table_operation (conn, table_list, rollback);
@ -1633,7 +1499,7 @@ gnc_dbi_begin_edit (QofBackend* qbe, QofInstance* inst)
g_return_if_fail (be != nullptr);
g_return_if_fail (inst != nullptr);
gnc_sql_begin_edit (&be->sql_be, inst);
gnc_sql_begin_edit (be, inst);
}
static void
@ -1644,7 +1510,7 @@ gnc_dbi_rollback_edit (QofBackend* qbe, QofInstance* inst)
g_return_if_fail (be != nullptr);
g_return_if_fail (inst != nullptr);
gnc_sql_rollback_edit (&be->sql_be, inst);
gnc_sql_rollback_edit (be, inst);
}
static void
@ -1655,7 +1521,7 @@ gnc_dbi_commit_edit (QofBackend* qbe, QofInstance* inst)
g_return_if_fail (be != nullptr);
g_return_if_fail (inst != nullptr);
gnc_sql_commit_edit (&be->sql_be, inst);
gnc_sql_commit_edit (be, inst);
}
/* ================================================================= */
@ -1684,22 +1550,17 @@ init_sql_backend (GncDbiBackend* dbi_be)
/* CoA Export function not implemented for the SQL backend. */
be->export_fn = nullptr;
gnc_sql_init (&dbi_be->sql_be);
dbi_be->sql_be.conn = nullptr;
dbi_be->sql_be.book = nullptr;
gnc_sql_init (dbi_be);
}
static QofBackend*
new_backend (void (*session_begin) (QofBackend*, QofSession*, const gchar*,
gboolean,
gboolean,
gboolean))
gboolean, gboolean, gboolean),
const char* format)
{
GncDbiBackend* dbi_be;
QofBackend* be;
dbi_be = g_new0 (GncDbiBackend, 1);
auto dbi_be = new GncDbiBackend(nullptr, nullptr, format);
g_assert (dbi_be != nullptr);
be = (QofBackend*)dbi_be;
@ -1714,19 +1575,22 @@ new_backend (void (*session_begin) (QofBackend*, QofSession*, const gchar*,
template<> QofBackend*
QofDbiBackendProvider<DbType::DBI_SQLITE>::create_backend()
{
return new_backend (gnc_dbi_sqlite3_session_begin);
return new_backend (gnc_dbi_sqlite3_session_begin,
SQLITE3_TIMESPEC_STR_FORMAT);
}
template<> QofBackend*
QofDbiBackendProvider<DbType::DBI_MYSQL>::create_backend()
{
return new_backend (gnc_dbi_mysql_session_begin);
return new_backend (gnc_dbi_mysql_session_begin,
MYSQL_TIMESPEC_STR_FORMAT);
}
template<> QofBackend*
QofDbiBackendProvider<DbType::DBI_PGSQL>::create_backend()
{
return new_backend (gnc_dbi_postgres_session_begin);
return new_backend (gnc_dbi_postgres_session_begin,
PGSQL_TIMESPEC_STR_FORMAT);
}

View File

@ -25,8 +25,17 @@
extern "C"
{
#include <dbi/dbi.h>
#ifdef G_OS_WIN32
#include <winsock2.h>
#define GETPID() GetCurrentProcessId()
#else
#include <limits.h>
#include <unistd.h>
#define GETPID() getpid()
#endif
}
#include <gnc-backend-sql.h>
#define GNC_HOST_NAME_MAX 255
/**
* Options to conn_table_operation
@ -76,22 +85,41 @@ public:
/**
* Implementations of GncSqlBackend.
*/
struct GncDbiBackend
class GncDbiBackend : public GncSqlBackend
{
GncSqlBackend sql_be;
dbi_conn conn;
gboolean exists; // Does the database exist?
public:
GncDbiBackend(GncSqlConnection *conn, QofBook* book,
const char* format = nullptr) :
GncSqlBackend(conn, book, format), m_exists{false} {}
bool connected() const noexcept { return m_conn != nullptr; }
/** FIXME: Just a pass-through to m_conn: */
void set_error(int error, int repeat, bool retry) noexcept
{
m_conn->set_error(error, repeat, retry);
}
void retry_connection(const char* msg) const noexcept
{
m_conn->retry_connection(msg);
}
/* Worst of all: */
GncSqlConnection* conn() { return m_conn; }
/*-----*/
bool exists() { return m_exists; }
void set_exists(bool exists) { m_exists = exists; }
friend void gnc_dbi_load(QofBackend*, QofBook*, QofBackendLoadType);
friend void gnc_dbi_safe_sync_all(QofBackend*, QofBook*);
private:
bool m_exists; // Does the database exist?
};
class GncDbiSqlConnection : public GncSqlConnection
{
public:
GncDbiSqlConnection (GncDbiProvider* provider, QofBackend* qbe,
dbi_conn conn) :
dbi_conn conn, const char* lock_table) :
m_qbe{qbe}, m_conn{conn}, m_provider{provider}, m_conn_ok{true},
m_last_error{ERR_BACKEND_NO_ERR}, m_error_repeat{0}, m_retry{false} {}
m_last_error{ERR_BACKEND_NO_ERR}, m_error_repeat{0}, m_retry{false},
m_lock_table{lock_table} {}
~GncDbiSqlConnection() override;
GncSqlResultPtr execute_select_statement (const GncSqlStatementPtr&)
noexcept override;
@ -114,21 +142,21 @@ public:
QofBackend* qbe () const noexcept { return m_qbe; }
dbi_conn conn() const noexcept { return m_conn; }
GncDbiProvider* provider() { return m_provider; }
inline void set_error (int error, int repeat, bool retry) noexcept
inline void set_error(int error, int repeat, bool retry) noexcept override
{
m_last_error = error;
m_error_repeat = repeat;
m_retry = retry;
}
inline void init_error () noexcept
inline void init_error() noexcept
{
set_error(ERR_BACKEND_NO_ERR, 0, false);
}
/** Check if the dbi connection is valid. If not attempt to re-establish it
* Returns TRUE is there is a valid connection in the end or FALSE otherwise
*/
bool verify() noexcept;
bool retry_connection(const char* msg) noexcept;
bool verify() noexcept override;
bool retry_connection(const char* msg) noexcept override;
dbi_result table_manage_backup(const std::string& table_name, TableOpType op);
/* FIXME: These three friend functions should really be members, but doing
* that is too invasive just yet. */
@ -162,6 +190,8 @@ private:
* original query)
*/
gboolean m_retry;
const char* m_lock_table;
void unlock_database();
};

View File

@ -71,10 +71,99 @@ GncDbiSqlStatement::add_where_cond(QofIdTypeConst type_name,
}
}
void
GncDbiSqlConnection::unlock_database ()
{
GncDbiBackend* qe = reinterpret_cast<decltype(qe)>(m_qbe);
if (m_conn == nullptr) return;
g_return_if_fail (dbi_conn_error (m_conn, nullptr) == 0);
auto dbname = dbi_conn_get_option (m_conn, "dbname");
/* Check if the lock table exists */
g_return_if_fail (dbname != nullptr);
auto result = dbi_conn_get_table_list (m_conn, dbname, m_lock_table);
if (! (result && dbi_result_get_numrows (result)))
{
if (result)
{
dbi_result_free (result);
result = nullptr;
}
PWARN ("No lock table in database, so not unlocking it.");
return;
}
dbi_result_free (result);
result = dbi_conn_query (m_conn, "BEGIN");
if (result)
{
/* Delete the entry if it's our hostname and PID */
gchar hostname[ GNC_HOST_NAME_MAX + 1 ];
dbi_result_free (result);
result = nullptr;
memset (hostname, 0, sizeof (hostname));
gethostname (hostname, GNC_HOST_NAME_MAX);
result = dbi_conn_queryf (m_conn,
"SELECT * FROM %s WHERE Hostname = '%s' AND PID = '%d'", m_lock_table, hostname,
(int)GETPID ());
if (result && dbi_result_get_numrows (result))
{
if (result)
{
dbi_result_free (result);
result = nullptr;
}
result = dbi_conn_queryf (m_conn, "DELETE FROM %s", m_lock_table);
if (!result)
{
PERR ("Failed to delete the lock entry");
qof_backend_set_error (m_qbe, ERR_BACKEND_SERVER_ERR);
result = dbi_conn_query (m_conn, "ROLLBACK");
if (result)
{
dbi_result_free (result);
result = nullptr;
}
return;
}
else
{
dbi_result_free (result);
result = nullptr;
}
result = dbi_conn_query (m_conn, "COMMIT");
if (result)
{
dbi_result_free (result);
result = nullptr;
}
return;
}
result = dbi_conn_query (m_conn, "ROLLBACK");
if (result)
{
dbi_result_free (result);
result = nullptr;
}
PWARN ("There was no lock entry in the Lock table");
return;
}
if (result)
{
dbi_result_free (result);
result = nullptr;
}
PWARN ("Unable to get a lock on LOCK, so failed to clear the lock entry.");
qof_backend_set_error (m_qbe, ERR_BACKEND_SERVER_ERR);
}
GncDbiSqlConnection::~GncDbiSqlConnection()
{
if (m_conn)
{
unlock_database();
dbi_conn_close(m_conn);
m_conn = nullptr;
}
@ -336,7 +425,8 @@ GncDbiSqlConnection::quote_string (const std::string& unquoted_str)
}
}
/* Check if the dbi connection is valid. If not attempt to re-establish it
/** Check if the dbi connection is valid. If not attempt to re-establish it
* Returns TRUE is there is a valid connection in the end or FALSE otherwise
*/
bool

View File

@ -0,0 +1,29 @@
/********************************************************************
* gnc-dbisqlconnection.hpp: Encapsulate libdbi dbi_conn *
* *
* Copyright 2016 John Ralls <jralls@ceridwen.us> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License*
* along with this program; if not, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
* Boston, MA 02110-1301, USA gnu@gnu.org *
\********************************************************************/
#ifndef _GNC_DBISQLCONNECTION_HPP_
#define _GNC_DBISQLCONNECTION_HPP_
/**
* Encapsulate a libdbi dbi_conn connection.
*/
#endif //_GNC_DBISQLCONNECTION_HPP_

View File

@ -351,12 +351,11 @@ teardown (Fixture* fixture, gconstpointer pData)
test_clear_error_list ();
}
#if 0 //temporarily disable test pending refactor.
static void
test_conn_index_functions (QofBackend* qbe)
{
GncDbiBackend* be = (GncDbiBackend*)qbe;
GncDbiSqlConnection* conn = (GncDbiSqlConnection*) (be->sql_be.conn);
auto index_list = conn->provider()->get_index_list (be->conn);
g_test_message ("Returned from index list\n");
@ -369,7 +368,7 @@ test_conn_index_functions (QofBackend* qbe)
}
}
#endif
/* Given a synthetic session, use the same logic as
* QofSession::save_as to save it to a specified sql url, then load it
* back and compare. */
@ -485,7 +484,7 @@ test_dbi_safe_save (Fixture* fixture, gconstpointer pData)
compare_books (qof_session_get_book (session_1),
qof_session_get_book (session_2));
be = qof_book_get_backend (qof_session_get_book (session_2));
test_conn_index_functions (be);
// test_conn_index_functions (be);
cleanup:
fixture->hdlrs = test_log_set_fatal_handler (fixture->hdlrs, check,
@ -512,10 +511,9 @@ test_dbi_version_control (Fixture* fixture, gconstpointer pData)
auto url = (gchar*)pData;
QofSession* sess;
QofBook* book;
QofBackend* qbe;
QofBackendError err;
gint ourversion = gnc_prefs_get_long_version ();
GncSqlBackend* be;
// Load the session data
if (fixture->filename)
url = fixture->filename;
@ -531,11 +529,10 @@ test_dbi_version_control (Fixture* fixture, gconstpointer pData)
}
qof_session_swap_data (fixture->session, sess);
qof_session_save (sess, NULL);
qbe = qof_session_get_backend (sess);
be = reinterpret_cast<GncSqlBackend*>(qof_session_get_backend (sess));
book = qof_session_get_book (sess);
qof_book_begin_edit (book);
gnc_sql_set_table_version ((GncSqlBackend*)qbe,
"Gnucash", GNUCASH_RESAVE_VERSION - 1);
be->set_table_version ("Gnucash", GNUCASH_RESAVE_VERSION - 1);
qof_book_commit_edit (book);
qof_session_end (sess);
qof_session_destroy (sess);
@ -544,13 +541,11 @@ test_dbi_version_control (Fixture* fixture, gconstpointer pData)
qof_session_load (sess, NULL);
err = qof_session_pop_error (sess);
g_assert_cmpint (err, == , ERR_SQL_DB_TOO_OLD);
qbe = qof_session_get_backend (sess);
be = reinterpret_cast<GncSqlBackend*>(qof_session_get_backend (sess));
book = qof_session_get_book (sess);
qof_book_begin_edit (book);
gnc_sql_set_table_version ((GncSqlBackend*)qbe,
"Gnucash", ourversion);
gnc_sql_set_table_version ((GncSqlBackend*)qbe,
"Gnucash-Resave", ourversion + 1);
be->set_table_version ("Gnucash", ourversion);
be->set_table_version ("Gnucash-Resave", ourversion + 1);
qof_book_commit_edit (book);
qof_session_end (sess);
qof_session_destroy (sess);
@ -561,11 +556,10 @@ test_dbi_version_control (Fixture* fixture, gconstpointer pData)
err = qof_session_pop_error (sess);
g_assert_cmpint (err, == , ERR_SQL_DB_TOO_NEW);
cleanup:
qbe = qof_session_get_backend (sess);
be = reinterpret_cast<GncSqlBackend*>(qof_session_get_backend (sess));
book = qof_session_get_book (sess);
qof_book_begin_edit (book);
gnc_sql_set_table_version ((GncSqlBackend*)qbe,
"Gnucash-Resave", GNUCASH_RESAVE_VERSION);
be->set_table_version ("Gnucash-Resave", GNUCASH_RESAVE_VERSION);
qof_book_commit_edit (book);
qof_session_end (sess);
qof_session_destroy (sess);

View File

@ -210,12 +210,11 @@ compare_lots (QofBook* book_1, QofBook* book_2)
{
do_compare (book_1, book_2, GNC_ID_LOT, compare_single_lot, "Lot lists match");
}
#if 0 //Disable test temporarily
static void
test_conn_index_functions (QofBackend* qbe)
{
GncDbiBackend* be = (GncDbiBackend*)qbe;
GncDbiSqlConnection* conn = (GncDbiSqlConnection*) (be->sql_be.conn);
auto index_list = conn->provider()->get_index_list (be->conn);
g_test_message ("Returned from index list\n");
@ -227,7 +226,7 @@ test_conn_index_functions (QofBackend* qbe)
g_assert (DBI_ERROR_NONE == dbi_conn_error (conn->conn(), &errmsg));
}
}
#endif
static void
compare_pricedbs (QofBook* book_1, QofBook* book_2)
{

View File

@ -183,11 +183,11 @@ load_single_account (GncSqlBackend* be, GncSqlRow& row,
guid = gnc_sql_load_guid (be, row);
if (guid != NULL)
{
pAccount = xaccAccountLookup (guid, be->book);
pAccount = xaccAccountLookup (guid, be->book());
}
if (pAccount == NULL)
{
pAccount = xaccMallocAccount (be->book);
pAccount = xaccMallocAccount (be->book());
}
xaccAccountBeginEdit (pAccount);
gnc_sql_load_object (be, row, GNC_ID_ACCOUNT, pAccount, col_table);
@ -196,7 +196,7 @@ load_single_account (GncSqlBackend* be, GncSqlRow& row,
/* If we don't have a parent and this isn't the root account, it might be because the parent
account hasn't been loaded yet. Remember the account and its parent guid for later. */
if (gnc_account_get_parent (pAccount) == NULL
&& pAccount != gnc_book_get_root_account (be->book))
&& pAccount != gnc_book_get_root_account (be->book()))
{
account_parent_guid_struct* s = static_cast<decltype (s)> (
g_malloc (sizeof (account_parent_guid_struct)));
@ -222,7 +222,7 @@ GncSqlAccountBackend::load_all (GncSqlBackend* be)
ENTER ("");
pBook = be->book;
pBook = be->book();
auto stmt = gnc_sql_create_select_statement (be, TABLE_NAME);
if (stmt == nullptr)
@ -256,7 +256,7 @@ GncSqlAccountBackend::load_all (GncSqlBackend* be)
for (elem = l_accounts_needing_parents; elem != NULL;)
{
account_parent_guid_struct* s = (account_parent_guid_struct*)elem->data;
pParent = xaccAccountLookup (&s->guid, be->book);
pParent = xaccAccountLookup (&s->guid, be->book());
if (pParent != NULL)
{
GList* next_elem;
@ -347,7 +347,7 @@ GncSqlAccountBackend::commit (GncSqlBackend* be, QofInstance* inst)
{
op = OP_DB_DELETE;
}
else if (be->is_pristine_db || is_infant)
else if (be->pristine() || is_infant)
{
op = OP_DB_INSERT;
}
@ -397,7 +397,7 @@ GncSqlColumnTableEntryImpl<CT_ACCOUNTREF>::load (const GncSqlBackend* be,
{
load_from_guid_ref(row, obj_name, pObject,
[be](GncGUID* g){
return xaccAccountLookup(g, be->book);
return xaccAccountLookup(g, be->book());
});
}

View File

@ -82,10 +82,12 @@ extern "C"
#include "gnc-tax-table-sql.h"
#include "gnc-vendor-sql.h"
#define VERSION_TABLE_NAME "versions"
#define MAX_TABLE_NAME_LEN 50
#define TABLE_COL_NAME "table_name"
#define VERSION_COL_NAME "table_version"
static void gnc_sql_init_object_handlers (void);
static void update_progress (GncSqlBackend* be);
static void finish_progress (GncSqlBackend* be);
static gboolean reset_version_info (GncSqlBackend* be);
static GncSqlStatementPtr build_insert_statement (GncSqlBackend* be,
const gchar* table_name,
QofIdTypeConst obj_name,
@ -183,7 +185,7 @@ create_tables(const OBEEntry& entry, GncSqlBackend* be)
GncSqlObjectBackendPtr obe = nullptr;
std::tie(type, obe) = entry;
g_return_if_fail (obe->is_version (GNC_SQL_BACKEND_VERSION));
update_progress(be);
be->update_progress();
obe->create_tables(be);
}
@ -240,12 +242,12 @@ gnc_sql_load (GncSqlBackend* be, QofBook* book, QofBackendLoadType loadType)
ENTER ("be=%p, book=%p", be, book);
be->loading = TRUE;
be->m_loading = TRUE;
if (loadType == LOAD_TYPE_INITIAL_LOAD)
{
g_assert (be->book == NULL);
be->book = book;
g_assert (be->m_book == NULL);
be->m_book = book;
/* Load any initial stuff. Some of this needs to happen in a certain order */
for (auto type : fixed_load_order)
@ -253,7 +255,7 @@ gnc_sql_load (GncSqlBackend* be, QofBook* book, QofBackendLoadType loadType)
auto obe = gnc_sql_get_object_backend(type);
if (obe)
{
update_progress(be);
be->update_progress();
obe->load_all (be);
}
}
@ -262,7 +264,7 @@ gnc_sql_load (GncSqlBackend* be, QofBook* book, QofBackendLoadType loadType)
auto obe = gnc_sql_get_object_backend(type);
if (obe)
{
update_progress(be);
be->update_progress();
obe->load_all (be);
}
}
@ -284,7 +286,7 @@ gnc_sql_load (GncSqlBackend* be, QofBook* book, QofBackendLoadType loadType)
obe->load_all (be);
}
be->loading = FALSE;
be->m_loading = FALSE;
g_list_free_full (post_load_commodities, commit_commodity);
post_load_commodities = NULL;
@ -292,7 +294,7 @@ gnc_sql_load (GncSqlBackend* be, QofBook* book, QofBackendLoadType loadType)
* dirty with this backend
*/
qof_book_mark_session_saved (book);
finish_progress (be);
be->finish_progress();
LEAVE ("");
}
@ -321,7 +323,7 @@ write_account_tree (GncSqlBackend* be, Account* root)
}
g_list_free (descendants);
}
update_progress (be);
be->update_progress();
return is_ok;
}
@ -333,12 +335,12 @@ write_accounts (GncSqlBackend* be)
g_return_val_if_fail (be != NULL, FALSE);
update_progress (be);
is_ok = write_account_tree (be, gnc_book_get_root_account (be->book));
be->update_progress();
is_ok = write_account_tree (be, gnc_book_get_root_account (be->book()));
if (is_ok)
{
update_progress (be);
is_ok = write_account_tree (be, gnc_book_get_template_root (be->book));
be->update_progress();
is_ok = write_account_tree (be, gnc_book_get_template_root (be->book()));
}
return is_ok;
@ -360,7 +362,7 @@ write_tx (Transaction* tx, gpointer data)
{
s->is_ok = splitbe->commit(s->be, QOF_INSTANCE(split_node->data));
}
update_progress (s->be);
s->be->update_progress ();
return (s->is_ok ? 0 : 1);
}
@ -373,8 +375,8 @@ write_transactions (GncSqlBackend* be)
write_objects_t data{be, true, obe};
(void)xaccAccountTreeForEachTransaction (
gnc_book_get_root_account (be->book), write_tx, &data);
update_progress (be);
gnc_book_get_root_account (be->book()), write_tx, &data);
be->update_progress();
return data.is_ok;
}
@ -385,11 +387,11 @@ write_template_transactions (GncSqlBackend* be)
auto obe = gnc_sql_get_object_backend(GNC_ID_TRANS);
write_objects_t data{be, true, obe};
auto ra = gnc_book_get_template_root (be->book);
auto ra = gnc_book_get_template_root (be->book());
if (gnc_account_n_descendants (ra) > 0)
{
(void)xaccAccountTreeForEachTransaction (ra, write_tx, &data);
update_progress (be);
be->update_progress();
}
return data.is_ok;
@ -404,7 +406,7 @@ write_schedXactions (GncSqlBackend* be)
g_return_val_if_fail (be != NULL, FALSE);
schedXactions = gnc_book_get_schedxactions (be->book)->sx_list;
schedXactions = gnc_book_get_schedxactions (be->book())->sx_list;
auto obe = gnc_sql_get_object_backend(GNC_ID_SCHEDXACTION);
for (; schedXactions != NULL && is_ok; schedXactions = schedXactions->next)
@ -412,25 +414,270 @@ write_schedXactions (GncSqlBackend* be)
tmpSX = static_cast<decltype (tmpSX)> (schedXactions->data);
is_ok = obe->commit (be, QOF_INSTANCE (tmpSX));
}
update_progress (be);
be->update_progress();
return is_ok;
}
static void
update_progress (GncSqlBackend* be)
static EntryVec version_table
{
if (be->be.percentage != NULL)
(be->be.percentage) (NULL, 101.0);
gnc_sql_make_table_entry<CT_STRING>(
TABLE_COL_NAME, MAX_TABLE_NAME_LEN, COL_PKEY | COL_NNUL),
gnc_sql_make_table_entry<CT_INT>(VERSION_COL_NAME, 0, COL_NNUL)
};
GncSqlBackend::GncSqlBackend(GncSqlConnection *conn, QofBook* book,
const char* format) :
be {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, ERR_BACKEND_NO_ERR, nullptr, 0,
nullptr}, m_conn{conn}, m_book{book}, m_loading{false},
m_in_query{false}, m_is_pristine_db{false}, m_versions{nullptr},
m_timespec_format{format}
{
if (conn != nullptr)
connect (conn);
}
static void
finish_progress (GncSqlBackend* be)
void
GncSqlBackend::connect(GncSqlConnection *conn) noexcept
{
if (be->be.percentage != NULL)
(be->be.percentage) (NULL, -1.0);
if (m_conn != nullptr && m_conn != conn)
delete m_conn;
if (m_versions != nullptr)
finalize_version_info();
m_conn = conn;
}
GncSqlStatementPtr
GncSqlBackend::create_statement_from_sql(const std::string& str) const noexcept
{
auto stmt = m_conn->create_statement_from_sql(str);
if (stmt == nullptr)
{
PERR ("SQL error: %s\n", str.c_str());
qof_backend_set_error ((QofBackend*)this, ERR_BACKEND_SERVER_ERR);
}
return stmt;
}
GncSqlResultPtr
GncSqlBackend::execute_select_statement(const GncSqlStatementPtr& stmt) const noexcept
{
auto result = m_conn->execute_select_statement(stmt);
if (result == nullptr)
{
PERR ("SQL error: %s\n", stmt->to_sql());
qof_backend_set_error ((QofBackend*)this, ERR_BACKEND_SERVER_ERR);
}
return result;
}
int
GncSqlBackend::execute_nonselect_statement(const GncSqlStatementPtr& stmt) const noexcept
{
auto result = m_conn->execute_nonselect_statement(stmt);
if (result == -1)
{
PERR ("SQL error: %s\n", stmt->to_sql());
qof_backend_set_error ((QofBackend*)this, ERR_BACKEND_SERVER_ERR);
}
return result;
}
std::string
GncSqlBackend::quote_string(const std::string& str) const noexcept
{
return m_conn->quote_string(str);
}
bool
GncSqlBackend::create_table(const std::string& table_name,
const EntryVec& col_table) const noexcept
{
ColVec info_vec;
gboolean ok = FALSE;
for (auto const& table_row : col_table)
{
table_row->add_to_table (this, info_vec);
}
return m_conn->create_table (table_name, info_vec);
}
bool
GncSqlBackend::create_index(const std::string& index_name,
const std::string& table_name,
const EntryVec& col_table) const noexcept
{
return m_conn->create_index(index_name, table_name, col_table);
}
bool
GncSqlBackend::add_columns_to_table(const std::string& table_name,
const EntryVec& col_table) const noexcept
{
ColVec info_vec;
for (auto const& table_row : col_table)
{
table_row->add_to_table (this, info_vec);
}
return m_conn->add_columns_to_table(table_name, info_vec);
}
void
GncSqlBackend::update_progress() const noexcept
{
if (be.percentage != nullptr)
(be.percentage) (nullptr, 101.0);
}
void
GncSqlBackend::finish_progress() const noexcept
{
if (be.percentage != nullptr)
(be.percentage) (nullptr, -1.0);
}
/**
* Sees if the version table exists, and if it does, loads the info into
* the version hash table. Otherwise, it creates an empty version table.
*
* @param be Backend struct
*/
void
GncSqlBackend::init_version_info() noexcept
{
if (m_versions != NULL)
{
g_hash_table_destroy (m_versions);
}
m_versions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
if (m_conn->does_table_exist (VERSION_TABLE_NAME))
{
std::string sql {"SELECT * FROM "};
sql += VERSION_TABLE_NAME;
auto stmt = m_conn->create_statement_from_sql(sql);
auto result = m_conn->execute_select_statement (stmt);
for (const auto& row : *result)
{
auto name = row.get_string_at_col (TABLE_COL_NAME);
auto version = row.get_int_at_col (VERSION_COL_NAME);
g_hash_table_insert (m_versions, g_strdup (name.c_str()),
GINT_TO_POINTER (version));
}
}
else
{
create_table (VERSION_TABLE_NAME, version_table);
set_table_version("Gnucash", gnc_prefs_get_long_version ());
set_table_version("Gnucash-Resave", GNUCASH_RESAVE_VERSION);
}
}
/**
* Resets the version table information by removing all version table info.
* It also recreates the version table in the db.
*
* @param be Backend struct
* @return TRUE if successful, FALSE if error
*/
bool
GncSqlBackend::reset_version_info() noexcept
{
bool ok = true;
if (!m_conn->does_table_exist (VERSION_TABLE_NAME))
ok = create_table (VERSION_TABLE_NAME, version_table);
if (m_versions == nullptr)
{
m_versions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
}
else
{
g_hash_table_remove_all (m_versions);
}
set_table_version ("Gnucash", gnc_prefs_get_long_version ());
set_table_version ("Gnucash-Resave", GNUCASH_RESAVE_VERSION);
return ok;
}
/**
* Finalizes the version table info by destroying the hash table.
*
* @param be Backend struct
*/
void
GncSqlBackend::finalize_version_info() noexcept
{
if (m_versions != nullptr)
{
g_hash_table_destroy (m_versions);
m_versions = nullptr;
}
}
int
GncSqlBackend::get_table_version(const std::string& table_name) const noexcept
{
/* If the db is pristine because it's being saved, the table does not exist. */
if (m_is_pristine_db)
return 0;
return GPOINTER_TO_INT (g_hash_table_lookup (m_versions,
table_name.c_str()));
}
/**
* Registers the version for a table. Registering involves updating the
* db version table and also the hash table.
*
* @param be Backend struct
* @param table_name Table name
* @param version Version number
* @return TRUE if successful, FALSE if unsuccessful
*/
bool
GncSqlBackend::set_table_version (const std::string& table_name, int version) noexcept
{
gchar* sql;
gint cur_version;
gint status;
g_return_val_if_fail (version > 0, false);
cur_version = get_table_version (table_name);
if (cur_version != version)
{
if (cur_version == 0)
{
sql = g_strdup_printf ("INSERT INTO %s VALUES('%s',%d)", VERSION_TABLE_NAME,
table_name.c_str(), version);
}
else
{
sql = g_strdup_printf ("UPDATE %s SET %s=%d WHERE %s='%s'", VERSION_TABLE_NAME,
VERSION_COL_NAME, version,
TABLE_COL_NAME, table_name.c_str());
}
status = gnc_sql_execute_nonselect_sql (this, sql);
if (status == -1)
{
PERR ("SQL error: %s\n", sql);
qof_backend_set_error ((QofBackend*)this, ERR_BACKEND_SERVER_ERR);
}
g_free (sql);
}
g_hash_table_insert (m_versions, g_strdup (table_name.c_str()),
GINT_TO_POINTER (version));
return true;
}
void
gnc_sql_sync_all (GncSqlBackend* be, QofBook* book)
{
@ -439,18 +686,18 @@ gnc_sql_sync_all (GncSqlBackend* be, QofBook* book)
g_return_if_fail (be != NULL);
g_return_if_fail (book != NULL);
ENTER ("book=%p, be->book=%p", book, be->book);
update_progress (be);
(void)reset_version_info (be);
be->reset_version_info();
ENTER ("book=%p, be->book=%p", book, be->book());
be->update_progress();
/* Create new tables */
be->is_pristine_db = TRUE;
be->m_is_pristine_db = true;
for(auto entry : backend_registry)
create_tables(entry, be);
/* Save all contents */
be->book = book;
is_ok = be->conn->begin_transaction ();
be->m_book = book;
is_ok = be->m_conn->begin_transaction ();
// FIXME: should write the set of commodities that are used
//write_commodities( be, book );
@ -482,11 +729,11 @@ gnc_sql_sync_all (GncSqlBackend* be, QofBook* book)
}
if (is_ok)
{
is_ok = be->conn->commit_transaction ();
is_ok = be->m_conn->commit_transaction ();
}
if (is_ok)
{
be->is_pristine_db = FALSE;
be->m_is_pristine_db = false;
/* Mark the session as clean -- though it shouldn't ever get
* marked dirty with this backend
@ -497,9 +744,9 @@ gnc_sql_sync_all (GncSqlBackend* be, QofBook* book)
{
if (!qof_backend_check_error ((QofBackend*)be))
qof_backend_set_error ((QofBackend*)be, ERR_BACKEND_SERVER_ERR);
is_ok = be->conn->rollback_transaction ();
is_ok = be->m_conn->rollback_transaction ();
}
finish_progress (be);
be->finish_progress();
LEAVE ("book=%p", book);
}
@ -558,15 +805,15 @@ gnc_sql_commit_edit (GncSqlBackend* be, QofInstance* inst)
g_return_if_fail (be != NULL);
g_return_if_fail (inst != NULL);
if (qof_book_is_readonly (be->book))
if (qof_book_is_readonly (be->book()))
{
qof_backend_set_error ((QofBackend*)be, ERR_BACKEND_READONLY);
(void)be->conn->rollback_transaction ();
(void)be->m_conn->rollback_transaction ();
return;
}
/* During initial load where objects are being created, don't commit
anything, but do mark the object as clean. */
if (be->loading)
anything, but do mark the object as clean. */
if (be->m_loading)
{
qof_instance_mark_clean (inst);
return;
@ -576,7 +823,7 @@ gnc_sql_commit_edit (GncSqlBackend* be, QofInstance* inst)
if (strcmp (inst->e_type, "PriceDB") == 0)
{
qof_instance_mark_clean (inst);
qof_book_mark_session_saved (be->book);
qof_book_mark_session_saved (be->book());
return;
}
@ -596,7 +843,7 @@ gnc_sql_commit_edit (GncSqlBackend* be, QofInstance* inst)
return;
}
if (!be->conn->begin_transaction ())
if (!be->m_conn->begin_transaction ())
{
PERR ("gnc_sql_commit_edit(): begin_transaction failed\n");
LEAVE ("Rolled back - database transaction begin error");
@ -614,10 +861,10 @@ gnc_sql_commit_edit (GncSqlBackend* be, QofInstance* inst)
if (!be_data.is_known)
{
PERR ("gnc_sql_commit_edit(): Unknown object type '%s'\n", inst->e_type);
(void)be->conn->rollback_transaction ();
(void)be->m_conn->rollback_transaction ();
// Don't let unknown items still mark the book as being dirty
qof_book_mark_session_saved (be->book);
qof_book_mark_session_saved (be->book());
qof_instance_mark_clean (inst);
LEAVE ("Rolled back - unknown object type");
return;
@ -625,16 +872,16 @@ gnc_sql_commit_edit (GncSqlBackend* be, QofInstance* inst)
if (!be_data.is_ok)
{
// Error - roll it back
(void)be->conn->rollback_transaction ();
(void)be->m_conn->rollback_transaction ();
// This *should* leave things marked dirty
LEAVE ("Rolled back - database error");
return;
}
(void)be->conn->commit_transaction ();
(void)be->m_conn->commit_transaction ();
qof_book_mark_session_saved (be->book);
qof_book_mark_session_saved (be->book());
qof_instance_mark_clean (inst);
LEAVE ("");
@ -1011,7 +1258,7 @@ gnc_sql_run_query (QofBackend* pBEnd, gpointer pQuery)
// }
// Mark the book as clean
qof_instance_mark_clean (QOF_INSTANCE (be->book));
qof_instance_mark_clean (QOF_INSTANCE (be->book()));
// DEBUG( "%s\n", (gchar*)pQueryInfo->pCompiledQuery );
@ -1431,7 +1678,7 @@ gnc_sql_convert_timespec_to_string (const GncSqlBackend* be, Timespec ts)
year = tm->tm_year + 1900;
datebuf = g_strdup_printf (be->timespec_format,
datebuf = g_strdup_printf (be->timespec_format(),
year, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
gnc_tm_free (tm);
return datebuf;
@ -1794,17 +2041,8 @@ gnc_sql_execute_select_statement (GncSqlBackend* be,
{
g_return_val_if_fail (be != NULL, NULL);
g_return_val_if_fail (stmt != NULL, NULL);
auto result = be->conn->execute_select_statement (stmt);
if (result == NULL)
{
PERR ("SQL error: %s\n", stmt->to_sql());
if (!qof_backend_check_error(&be->be))
qof_backend_set_error (&be->be, ERR_BACKEND_SERVER_ERR);
}
return result;
return be->execute_select_statement (stmt);
}
GncSqlStatementPtr
@ -1813,15 +2051,7 @@ gnc_sql_create_statement_from_sql (GncSqlBackend* be, const gchar* sql)
g_return_val_if_fail (be != NULL, NULL);
g_return_val_if_fail (sql != NULL, NULL);
auto stmt = be->conn->create_statement_from_sql (sql);
if (stmt == nullptr)
{
PERR ("SQL error: %s\n", sql);
if (!qof_backend_check_error(&be->be))
qof_backend_set_error (&be->be, ERR_BACKEND_SERVER_ERR);
}
return stmt;
return be->create_statement_from_sql (sql);
}
GncSqlResultPtr
@ -1835,15 +2065,7 @@ gnc_sql_execute_select_sql (GncSqlBackend* be, const gchar* sql)
{
return nullptr;
}
auto result = be->conn->execute_select_statement (stmt);
if (result == nullptr)
{
PERR ("SQL error: %s\n", sql);
if (!qof_backend_check_error(&be->be))
qof_backend_set_error (&be->be, ERR_BACKEND_SERVER_ERR);
}
return result;
return be->execute_select_statement (stmt);
}
gint
@ -1857,8 +2079,7 @@ gnc_sql_execute_nonselect_sql (GncSqlBackend* be, const gchar* sql)
{
return -1;
}
auto result = be->conn->execute_nonselect_statement (stmt);
return result;
return be->execute_nonselect_statement (stmt);
}
guint
@ -1942,7 +2163,7 @@ gnc_sql_do_db_operation (GncSqlBackend* be,
const EntryVec& table)
{
GncSqlStatementPtr stmt;
gboolean ok = FALSE;
bool ok = false;
g_return_val_if_fail (be != NULL, FALSE);
g_return_val_if_fail (table_name != NULL, FALSE);
@ -1965,20 +2186,8 @@ gnc_sql_do_db_operation (GncSqlBackend* be,
{
g_assert (FALSE);
}
if (stmt != nullptr)
{
auto result = be->conn->execute_nonselect_statement (stmt);
if (result == -1)
{
PERR ("SQL error: %s\n", stmt->to_sql());
if (!qof_backend_check_error(&be->be))
qof_backend_set_error (&be->be, ERR_BACKEND_SERVER_ERR);
}
else
{
ok = TRUE;
}
}
if (be->execute_nonselect_statement (stmt) != -1)
ok = true;
return ok;
}
@ -2012,11 +2221,11 @@ build_insert_statement (GncSqlBackend* be,
{
if (col_value != *values.begin())
sql << ",";
sql << be->conn->quote_string(col_value.second);
sql << be->quote_string(col_value.second);
}
sql << ")";
stmt = be->conn->create_statement_from_sql(sql.str());
stmt = be->create_statement_from_sql(sql.str());
return stmt;
}
@ -2045,10 +2254,10 @@ build_update_statement (GncSqlBackend* be,
if (col_value != *values.begin())
sql << ",";
sql << col_value.first << "=" <<
be->conn->quote_string(col_value.second);
be->quote_string(col_value.second);
}
stmt = be->conn->create_statement_from_sql(sql.str());
stmt = be->create_statement_from_sql(sql.str());
/* We want our where condition to be just the first column and
* value, i.e. the guid of the object.
*/
@ -2071,7 +2280,7 @@ build_delete_statement (GncSqlBackend* be,
g_return_val_if_fail (pObject != NULL, NULL);
sql << "DELETE FROM " << table_name;
auto stmt = be->conn->create_statement_from_sql (sql.str());
auto stmt = be->create_statement_from_sql (sql.str());
/* WHERE */
PairVec values;
@ -2096,7 +2305,7 @@ GncSqlObjectBackend::commit (GncSqlBackend* be, QofInstance* inst)
{
op = OP_DB_DELETE;
}
else if (be->is_pristine_db || is_infant)
else if (be->pristine() || is_infant)
{
op = OP_DB_INSERT;
}
@ -2126,51 +2335,28 @@ GncSqlObjectBackend::commit (GncSqlBackend* be, QofInstance* inst)
/* ================================================================= */
static gboolean
do_create_table (const GncSqlBackend* be, const gchar* table_name,
const EntryVec& col_table)
{
ColVec info_vec;
gboolean ok = FALSE;
g_return_val_if_fail (be != NULL, FALSE);
g_return_val_if_fail (table_name != NULL, FALSE);
for (auto const& table_row : col_table)
{
table_row->add_to_table (be, info_vec);
}
ok = be->conn->create_table (table_name, info_vec);
return ok;
}
gboolean
gnc_sql_create_table (GncSqlBackend* be, const char* table_name,
gint table_version, const EntryVec& col_table)
gnc_sql_create_table (GncSqlBackend* be, const gchar* table_name,
int table_version, const EntryVec& col_table)
{
gboolean ok;
g_return_val_if_fail (be != NULL, FALSE);
g_return_val_if_fail (table_name != NULL, FALSE);
DEBUG ("Creating %s table\n", table_name);
ok = do_create_table (be, table_name, col_table);
if (ok)
{
ok = gnc_sql_set_table_version (be, table_name, table_version);
}
return ok;
if (be->create_table (table_name, col_table))
return be->set_table_version (table_name, table_version);
return false;
}
void
GncSqlObjectBackend::create_tables (GncSqlBackend* be)
{
g_return_if_fail (be != nullptr);
int version = gnc_sql_get_table_version (be, m_table_name.c_str());
int version = be->get_table_version (m_table_name);
if (version == 0) //No tables, otherwise version will be >= 1.
gnc_sql_create_table (be, m_table_name.c_str(),
m_version, m_col_table);
{
be->create_table(m_table_name, m_col_table);
be->set_table_version(m_table_name, m_version);
}
else if (version != m_version)
PERR("Version mismatch in table %s, expecting %d but backend is %d."
"Table creation aborted.", m_table_name.c_str(), m_version, version);
@ -2183,7 +2369,7 @@ gnc_sql_create_temp_table (const GncSqlBackend* be, const gchar* table_name,
g_return_val_if_fail (be != NULL, FALSE);
g_return_val_if_fail (table_name != NULL, FALSE);
return do_create_table (be, table_name, col_table);
return be->create_table (table_name, col_table);
}
gboolean
@ -2197,7 +2383,7 @@ gnc_sql_create_index (const GncSqlBackend* be, const gchar* index_name,
g_return_val_if_fail (index_name != NULL, FALSE);
g_return_val_if_fail (table_name != NULL, FALSE);
ok = be->conn->create_index (index_name, table_name, col_table);
ok = be->create_index (index_name, table_name, col_table);
return ok;
}
@ -2206,14 +2392,7 @@ gnc_sql_get_table_version (const GncSqlBackend* be, const gchar* table_name)
{
g_return_val_if_fail (be != NULL, 0);
g_return_val_if_fail (table_name != NULL, 0);
/* If the db is pristine because it's being saved, the table does not exist. */
if (be->is_pristine_db)
{
return 0;
}
return GPOINTER_TO_INT (g_hash_table_lookup (be->versions, table_name));
return be->get_table_version(table_name);
}
/* Create a temporary table, copy the data from the old table, delete the
@ -2252,169 +2431,14 @@ gnc_sql_upgrade_table (GncSqlBackend* be, const gchar* table_name,
gboolean gnc_sql_add_columns_to_table (GncSqlBackend* be, const gchar* table_name,
const EntryVec& new_col_table)
{
ColVec info_vec;
gboolean ok = FALSE;
g_return_val_if_fail (be != NULL, FALSE);
g_return_val_if_fail (table_name != NULL, FALSE);
for (auto const& table_row : new_col_table)
{
table_row->add_to_table (be, info_vec);
}
ok = be->conn->add_columns_to_table(table_name, info_vec);
return ok;
return be->add_columns_to_table(table_name, new_col_table);
}
/* ================================================================= */
#define VERSION_TABLE_NAME "versions"
#define MAX_TABLE_NAME_LEN 50
#define TABLE_COL_NAME "table_name"
#define VERSION_COL_NAME "table_version"
static EntryVec version_table
{
gnc_sql_make_table_entry<CT_STRING>(
TABLE_COL_NAME, MAX_TABLE_NAME_LEN, COL_PKEY | COL_NNUL),
gnc_sql_make_table_entry<CT_INT>(VERSION_COL_NAME, 0, COL_NNUL)
};
/**
* Sees if the version table exists, and if it does, loads the info into
* the version hash table. Otherwise, it creates an empty version table.
*
* @param be Backend struct
*/
void
gnc_sql_init_version_info (GncSqlBackend* be)
{
g_return_if_fail (be != NULL);
if (be->versions != NULL)
{
g_hash_table_destroy (be->versions);
}
be->versions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
if (be->conn->does_table_exist (VERSION_TABLE_NAME))
{
auto sql = g_strdup_printf ("SELECT * FROM %s", VERSION_TABLE_NAME);
auto result = gnc_sql_execute_select_sql (be, sql);
g_free (sql);
for (const auto& row : *result)
{
auto name = row.get_string_at_col (TABLE_COL_NAME);
auto version = row.get_int_at_col (VERSION_COL_NAME);
g_hash_table_insert (be->versions, g_strdup (name.c_str()),
GINT_TO_POINTER (version));
}
}
else
{
do_create_table (be, VERSION_TABLE_NAME, version_table);
gnc_sql_set_table_version (be, "Gnucash",
gnc_prefs_get_long_version ());
gnc_sql_set_table_version (be, "Gnucash-Resave",
GNUCASH_RESAVE_VERSION);
}
}
/**
* Resets the version table information by removing all version table info.
* It also recreates the version table in the db.
*
* @param be Backend struct
* @return TRUE if successful, FALSE if error
*/
static gboolean
reset_version_info (GncSqlBackend* be)
{
gboolean ok;
g_return_val_if_fail (be != NULL, FALSE);
ok = do_create_table (be, VERSION_TABLE_NAME, version_table);
if (be->versions == NULL)
{
be->versions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
}
else
{
g_hash_table_remove_all (be->versions);
}
gnc_sql_set_table_version (be, "Gnucash", gnc_prefs_get_long_version ());
gnc_sql_set_table_version (be, "Gnucash-Resave", GNUCASH_RESAVE_VERSION);
return ok;
}
/**
* Finalizes the version table info by destroying the hash table.
*
* @param be Backend struct
*/
void
gnc_sql_finalize_version_info (GncSqlBackend* be)
{
g_return_if_fail (be != NULL);
if (be->versions != NULL)
{
g_hash_table_destroy (be->versions);
be->versions = NULL;
}
}
/**
* Registers the version for a table. Registering involves updating the
* db version table and also the hash table.
*
* @param be Backend struct
* @param table_name Table name
* @param version Version number
* @return TRUE if successful, FALSE if unsuccessful
*/
gboolean
gnc_sql_set_table_version (GncSqlBackend* be, const gchar* table_name,
gint version)
{
gchar* sql;
gint cur_version;
gint status;
g_return_val_if_fail (be != NULL, FALSE);
g_return_val_if_fail (table_name != NULL, FALSE);
g_return_val_if_fail (version > 0, FALSE);
cur_version = gnc_sql_get_table_version (be, table_name);
if (cur_version != version)
{
if (cur_version == 0)
{
sql = g_strdup_printf ("INSERT INTO %s VALUES('%s',%d)", VERSION_TABLE_NAME,
table_name, version);
}
else
{
sql = g_strdup_printf ("UPDATE %s SET %s=%d WHERE %s='%s'", VERSION_TABLE_NAME,
VERSION_COL_NAME, version,
TABLE_COL_NAME, table_name);
}
status = gnc_sql_execute_nonselect_sql (be, sql);
if (status == -1)
{
PERR ("SQL error: %s\n", sql);
if (!qof_backend_check_error(&be->be))
qof_backend_set_error (&be->be, ERR_BACKEND_SERVER_ERR);
}
g_free (sql);
}
g_hash_table_insert (be->versions, g_strdup (table_name),
GINT_TO_POINTER (version));
return TRUE;
}
/* This is necessary for 64-bit builds because g++ complains
* that reinterpret_casting a void* (64 bits) to an int (32 bits)

View File

@ -59,22 +59,66 @@ using ColVec = std::vector<GncSqlColumnInfo>;
using StrVec = std::vector<std::string>;
using PairVec = std::vector<std::pair<std::string, std::string>>;
class GncSqlConnection;
class GncSqlStatement;
using GncSqlStatementPtr = std::unique_ptr<GncSqlStatement>;
class GncSqlResult;
//using GncSqlResultPtr = std::unique_ptr<GncSqlResult>;
using GncSqlResultPtr = GncSqlResult*;
/**
* @struct GncSqlBackend
*
* Main SQL backend structure.
*/
struct GncSqlBackend
class GncSqlBackend
{
QofBackend be; /**< QOF backend */
GncSqlConnection* conn; /**< SQL connection */
QofBook* book; /**< The primary, main open book */
gboolean loading; /**< We are performing an initial load */
gboolean in_query; /**< We are processing a query */
gboolean is_pristine_db; /**< Are we saving to a new pristine db? */
GHashTable* versions; /**< Version number for each table */
const gchar* timespec_format; /**< Format string for SQL for timespec values */
public:
GncSqlBackend(GncSqlConnection *conn, QofBook* book,
const char* format = nullptr);
virtual ~GncSqlBackend() = default;
/** Connect the backend to a GncSqlConnection.
* Sets up version info. Calling with nullptr clears the connection and
* destroys the version info.
*/
void connect(GncSqlConnection *conn) noexcept;
void init_version_info() noexcept;
bool reset_version_info() noexcept;
void finalize_version_info() noexcept;
/* FIXME: These are just pass-throughs of m_conn functions. */
GncSqlStatementPtr create_statement_from_sql(const std::string& str) const noexcept;
GncSqlResultPtr execute_select_statement(const GncSqlStatementPtr& stmt) const noexcept;
int execute_nonselect_statement(const GncSqlStatementPtr& stmt) const noexcept;
std::string quote_string(const std::string&) const noexcept;
bool create_table(const std::string& table_name, const EntryVec& col_table) const noexcept;
bool create_index(const std::string& index_name,
const std::string& table_name,
const EntryVec& col_table) const noexcept;
bool add_columns_to_table(const std::string& table_name,
const EntryVec& col_table) const noexcept;
int get_table_version(const std::string& table_name) const noexcept;
bool set_table_version (const std::string& table_name, int version) noexcept;
QofBook* book() const noexcept { return m_book; }
bool pristine() const noexcept { return m_is_pristine_db; }
void update_progress() const noexcept;
void finish_progress() const noexcept;
void set_loading(bool val) noexcept { m_loading = val; }
const char* timespec_format() const noexcept { return m_timespec_format; }
friend void gnc_sql_load(GncSqlBackend*, QofBook*, QofBackendLoadType);
friend void gnc_sql_sync_all(GncSqlBackend*, QofBook*);
friend void gnc_sql_commit_edit(GncSqlBackend*, QofInstance*);
protected:
QofBackend be; /**< QOF backend. Not a pointer, nor really a member */
GncSqlConnection* m_conn; /**< SQL connection */
QofBook* m_book; /**< The primary, main open book */
bool m_loading; /**< We are performing an initial load */
bool m_in_query; /**< We are processing a query */
bool m_is_pristine_db; /**< Are we saving to a new pristine db? */
GHashTable* m_versions; /**< Version number for each table */
const char* m_timespec_format; /**< Format string for SQL for timespec values */
private:
};
/**
@ -137,9 +181,6 @@ void gnc_sql_commit_edit (GncSqlBackend* qbe, QofInstance* inst);
/**
*/
class GncSqlResult;
//using GncSqlResultPtr = std::unique_ptr<GncSqlResult>;
using GncSqlResultPtr = GncSqlResult*;
/**
* SQL statement provider.
@ -152,8 +193,6 @@ public:
virtual void add_where_cond (QofIdTypeConst, const PairVec&) = 0;
};
using GncSqlStatementPtr = std::unique_ptr<GncSqlStatement>;
/**
* Encapsulate the connection to the database.
*/
@ -192,6 +231,10 @@ public:
* If not 0 will normally be meaningless outside of implementation code.
*/
virtual int dberror() const noexcept = 0;
virtual void set_error(int error, int repeat, bool retry) noexcept = 0;
virtual bool verify() noexcept = 0;
virtual bool retry_connection(const char* msg) noexcept = 0;
};
/**

View File

@ -197,10 +197,10 @@ load_single_billterm (GncSqlBackend* be, GncSqlRow& row,
g_return_val_if_fail (be != NULL, NULL);
guid = gnc_sql_load_guid (be, row);
pBillTerm = gncBillTermLookup (be->book, guid);
pBillTerm = gncBillTermLookup (be->book(), guid);
if (pBillTerm == NULL)
{
pBillTerm = gncBillTermCreate (be->book);
pBillTerm = gncBillTermCreate (be->book());
}
gnc_sql_load_object (be, row, GNC_ID_BILLTERM, pBillTerm, col_table);
@ -298,7 +298,7 @@ GncSqlBillTermBackend::write (GncSqlBackend* be)
g_return_val_if_fail (be != NULL, FALSE);
write_objects_t data {be, true, this};
qof_object_foreach (GNC_ID_BILLTERM, be->book, do_save_billterm, &data);
qof_object_foreach (GNC_ID_BILLTERM, be->book(), do_save_billterm, &data);
return data.is_ok;
}
@ -319,7 +319,7 @@ GncSqlBillTermBackend::create_tables (GncSqlBackend* be)
{
/* Upgrade 64 bit int handling */
gnc_sql_upgrade_table (be, TABLE_NAME, col_table);
gnc_sql_set_table_version (be, TABLE_NAME, TABLE_VERSION);
be->set_table_version (TABLE_NAME, TABLE_VERSION);
PINFO ("Billterms table upgraded from version 1 to version %d\n",
TABLE_VERSION);
@ -336,7 +336,7 @@ GncSqlColumnTableEntryImpl<CT_BILLTERMREF>::load (const GncSqlBackend* be,
{
load_from_guid_ref(row, obj_name, pObject,
[be](GncGUID* g){
return gncBillTermLookup(be->book, g);
return gncBillTermLookup(be->book(), g);
});
}

View File

@ -152,7 +152,7 @@ load_single_book (GncSqlBackend* be, GncSqlRow& row)
gnc_sql_load_guid (be, row);
pBook = be->book;
pBook = be->book();
if (pBook == NULL)
{
pBook = qof_book_new ();
@ -182,9 +182,9 @@ GncSqlBookBackend::load_all (GncSqlBackend* be)
*/
if (row == result->end())
{
be->loading = FALSE;
commit(be, QOF_INSTANCE (be->book));
be->loading = TRUE;
be->set_loading(false);
commit (be, QOF_INSTANCE (be->book()));
be->set_loading(true);
}
else
{

View File

@ -275,7 +275,7 @@ save_budget_amounts (GncSqlBackend* be, GncBudget* budget)
info.budget = budget;
num_periods = gnc_budget_get_num_periods (budget);
descendants = gnc_account_get_descendants (gnc_book_get_root_account (
be->book));
be->book()));
for (node = descendants; node != NULL && is_ok; node = g_list_next (node))
{
guint i;
@ -308,11 +308,11 @@ load_single_budget (GncSqlBackend* be, GncSqlRow& row)
guid = gnc_sql_load_guid (be, row);
if (guid != NULL)
{
pBudget = gnc_budget_lookup (guid, be->book);
pBudget = gnc_budget_lookup (guid, be->book());
}
if (pBudget == NULL)
{
pBudget = gnc_budget_new (be->book);
pBudget = gnc_budget_new (be->book());
}
gnc_budget_begin_edit (pBudget);
@ -398,7 +398,7 @@ GncSqlBudgetBackend::commit (GncSqlBackend* be, QofInstance* inst)
{
op = OP_DB_DELETE;
}
else if (be->is_pristine_db || is_infant)
else if (be->pristine() || is_infant)
{
op = OP_DB_INSERT;
}
@ -464,7 +464,7 @@ GncSqlBudgetBackend::write (GncSqlBackend* be)
data.be = be;
data.is_ok = TRUE;
data.obe = this;
qof_collection_foreach (qof_book_get_collection (be->book, GNC_ID_BUDGET),
qof_collection_foreach (qof_book_get_collection (be->book(), GNC_ID_BUDGET),
(QofInstanceForeachCB)do_save, &data);
return data.is_ok;
@ -479,7 +479,7 @@ GncSqlColumnTableEntryImpl<CT_BUDGETREF>::load (const GncSqlBackend* be,
{
load_from_guid_ref(row, obj_name, pObject,
[be](GncGUID* g){
return gnc_budget_lookup (g, be->book);
return gnc_budget_lookup (g, be->book());
});
}

View File

@ -129,7 +129,7 @@ set_quote_source_name (gpointer pObject, gpointer pValue)
static gnc_commodity*
load_single_commodity (GncSqlBackend* be, GncSqlRow& row)
{
QofBook* pBook = be->book;
QofBook* pBook = be->book();
gnc_commodity* pCommodity;
pCommodity = gnc_commodity_new (pBook, NULL, NULL, NULL, NULL, 100);
@ -145,7 +145,7 @@ GncSqlCommodityBackend::load_all (GncSqlBackend* be)
{
gnc_commodity_table* pTable;
pTable = gnc_commodity_table_get_table (be->book);
pTable = gnc_commodity_table_get_table (be->book());
auto stmt = gnc_sql_create_select_statement (be, COMMODITIES_TABLE);
if (stmt == nullptr) return;
auto result = gnc_sql_execute_select_statement (be, stmt);
@ -186,7 +186,7 @@ do_commit_commodity (GncSqlBackend* be, QofInstance* inst,
{
op = OP_DB_DELETE;
}
else if (be->is_pristine_db || is_infant || force_insert)
else if (be->pristine() || is_infant || force_insert)
{
op = OP_DB_INSERT;
}
@ -268,7 +268,7 @@ GncSqlColumnTableEntryImpl<CT_COMMODITYREF>::load (const GncSqlBackend* be,
{
load_from_guid_ref(row, obj_name, pObject,
[be](GncGUID* g){
return gnc_commodity_find_commodity_by_guid(g, be->book);
return gnc_commodity_find_commodity_by_guid(g, be->book());
});
}

View File

@ -110,10 +110,10 @@ load_single_customer (GncSqlBackend* be, GncSqlRow& row)
g_return_val_if_fail (be != NULL, NULL);
guid = gnc_sql_load_guid (be, row);
pCustomer = gncCustomerLookup (be->book, guid);
pCustomer = gncCustomerLookup (be->book(), guid);
if (pCustomer == NULL)
{
pCustomer = gncCustomerCreate (be->book);
pCustomer = gncCustomerCreate (be->book());
}
gnc_sql_load_object (be, row, GNC_ID_CUSTOMER, pCustomer, col_table);
qof_instance_mark_clean (QOF_INSTANCE (pCustomer));
@ -164,7 +164,7 @@ GncSqlCustomerBackend::create_tables (GncSqlBackend* be)
{
/* Upgrade 64 bit int handling */
gnc_sql_upgrade_table (be, TABLE_NAME, col_table);
gnc_sql_set_table_version (be, TABLE_NAME, TABLE_VERSION);
be->set_table_version (TABLE_NAME, TABLE_VERSION);
PINFO ("Customers table upgraded from version 1 to version %d\n",
TABLE_VERSION);
@ -214,7 +214,7 @@ GncSqlCustomerBackend::write (GncSqlBackend* be)
data.be = be;
data.is_ok = TRUE;
data.obe = this;
qof_object_foreach (GNC_ID_CUSTOMER, be->book, write_single_customer,
qof_object_foreach (GNC_ID_CUSTOMER, be->book(), write_single_customer,
(gpointer)&data);
return data.is_ok;
}

View File

@ -96,10 +96,10 @@ load_single_employee (GncSqlBackend* be, GncSqlRow& row)
g_return_val_if_fail (be != NULL, NULL);
guid = gnc_sql_load_guid (be, row);
pEmployee = gncEmployeeLookup (be->book, guid);
pEmployee = gncEmployeeLookup (be->book(), guid);
if (pEmployee == NULL)
{
pEmployee = gncEmployeeCreate (be->book);
pEmployee = gncEmployeeCreate (be->book());
}
gnc_sql_load_object (be, row, GNC_ID_EMPLOYEE, pEmployee, col_table);
qof_instance_mark_clean (QOF_INSTANCE (pEmployee));
@ -151,7 +151,7 @@ GncSqlEmployeeBackend::create_tables (GncSqlBackend* be)
{
/* Upgrade 64 bit int handling */
gnc_sql_upgrade_table (be, TABLE_NAME, col_table);
gnc_sql_set_table_version (be, TABLE_NAME, TABLE_VERSION);
be->set_table_version (TABLE_NAME, TABLE_VERSION);
PINFO ("Employees table upgraded from version 1 to version %d\n",
TABLE_VERSION);
@ -179,7 +179,7 @@ GncSqlEmployeeBackend::commit (GncSqlBackend* be, QofInstance* inst)
{
op = OP_DB_DELETE;
}
else if (be->is_pristine_db || is_infant)
else if (be->pristine() || is_infant)
{
op = OP_DB_INSERT;
}
@ -259,7 +259,7 @@ GncSqlEmployeeBackend::write (GncSqlBackend* be)
data.be = be;
data.is_ok = TRUE;
data.obe = this;
qof_object_foreach (GNC_ID_EMPLOYEE, be->book, write_single_employee, &data);
qof_object_foreach (GNC_ID_EMPLOYEE, be->book(), write_single_employee, &data);
return data.is_ok;
}

View File

@ -178,10 +178,10 @@ load_single_entry (GncSqlBackend* be, GncSqlRow& row)
g_return_val_if_fail (be != NULL, NULL);
guid = gnc_sql_load_guid (be, row);
pEntry = gncEntryLookup (be->book, guid);
pEntry = gncEntryLookup (be->book(), guid);
if (pEntry == NULL)
{
pEntry = gncEntryCreate (be->book);
pEntry = gncEntryCreate (be->book());
}
gnc_sql_load_object (be, row, GNC_ID_ENTRY, pEntry, col_table);
qof_instance_mark_clean (QOF_INSTANCE (pEntry));
@ -234,7 +234,7 @@ GncSqlEntryBackend::create_tables (GncSqlBackend* be)
2->3: "entered" -> "date_entered", and it can be NULL
*/
gnc_sql_upgrade_table (be, TABLE_NAME, col_table);
gnc_sql_set_table_version (be, TABLE_NAME, TABLE_VERSION);
be->set_table_version (TABLE_NAME, TABLE_VERSION);
PINFO ("Entries table upgraded from version %d to version %d\n", version,
TABLE_VERSION);
@ -267,7 +267,7 @@ GncSqlEntryBackend::write (GncSqlBackend* be)
g_return_val_if_fail (be != NULL, FALSE);
write_objects_t data{be, true, this};
qof_object_foreach (GNC_ID_ENTRY, be->book, write_single_entry, &data);
qof_object_foreach (GNC_ID_ENTRY, be->book(), write_single_entry, &data);
return data.is_ok;
}

View File

@ -117,10 +117,10 @@ load_single_invoice (GncSqlBackend* be, GncSqlRow& row)
g_return_val_if_fail (be != NULL, NULL);
guid = gnc_sql_load_guid (be, row);
pInvoice = gncInvoiceLookup (be->book, guid);
pInvoice = gncInvoiceLookup (be->book(), guid);
if (pInvoice == NULL)
{
pInvoice = gncInvoiceCreate (be->book);
pInvoice = gncInvoiceCreate (be->book());
}
gnc_sql_load_object (be, row, GNC_ID_INVOICE, pInvoice, col_table);
qof_instance_mark_clean (QOF_INSTANCE (pInvoice));
@ -173,7 +173,7 @@ GncSqlInvoiceBackend::create_tables (GncSqlBackend* be)
2->3: invoice open date can be NULL
*/
gnc_sql_upgrade_table (be, TABLE_NAME, col_table);
gnc_sql_set_table_version (be, TABLE_NAME, TABLE_VERSION);
be->set_table_version (TABLE_NAME, TABLE_VERSION);
PINFO ("Invoices table upgraded from version %d to version %d\n", version,
TABLE_VERSION);
@ -201,7 +201,7 @@ GncSqlInvoiceBackend::commit (GncSqlBackend* be, QofInstance* inst)
{
op = OP_DB_DELETE;
}
else if (be->is_pristine_db || is_infant)
else if (be->pristine() || is_infant)
{
op = OP_DB_INSERT;
}
@ -277,7 +277,7 @@ GncSqlInvoiceBackend::write (GncSqlBackend* be)
g_return_val_if_fail (be != NULL, FALSE);
write_objects_t data{be, true, this};
qof_object_foreach (GNC_ID_INVOICE, be->book, write_single_invoice, &data);
qof_object_foreach (GNC_ID_INVOICE, be->book(), write_single_invoice, &data);
return data.is_ok;
}
@ -291,7 +291,7 @@ GncSqlColumnTableEntryImpl<CT_INVOICEREF>::load (const GncSqlBackend* be,
{
load_from_guid_ref(row, obj_name, pObject,
[be](GncGUID* g){
return gncInvoiceLookup (be->book, g);
return gncInvoiceLookup (be->book(), g);
});
}

View File

@ -89,10 +89,10 @@ load_single_job (GncSqlBackend* be, GncSqlRow& row)
g_return_val_if_fail (be != NULL, NULL);
guid = gnc_sql_load_guid (be, row);
pJob = gncJobLookup (be->book, guid);
pJob = gncJobLookup (be->book(), guid);
if (pJob == NULL)
{
pJob = gncJobCreate (be->book);
pJob = gncJobCreate (be->book());
}
gnc_sql_load_object (be, row, GNC_ID_JOB, pJob, col_table);
qof_instance_mark_clean (QOF_INSTANCE (pJob));
@ -164,7 +164,7 @@ GncSqlJobBackend::write (GncSqlBackend* be)
g_return_val_if_fail (be != NULL, FALSE);
write_objects_t data{be, true, this};
qof_object_foreach (GNC_ID_JOB, be->book, write_single_job, &data);
qof_object_foreach (GNC_ID_JOB, be->book(), write_single_job, &data);
return data.is_ok;
}

View File

@ -113,7 +113,7 @@ load_single_lot (GncSqlBackend* be, GncSqlRow& row)
g_return_val_if_fail (be != NULL, NULL);
lot = gnc_lot_new (be->book);
lot = gnc_lot_new (be->book());
gnc_lot_begin_edit (lot);
gnc_sql_load_object (be, row, GNC_ID_LOT, lot, col_table);
@ -166,7 +166,7 @@ GncSqlLotsBackend::create_tables (GncSqlBackend* be)
old table, then rename the new one. */
gnc_sql_upgrade_table (be, TABLE_NAME, col_table);
(void)gnc_sql_set_table_version (be, TABLE_NAME, TABLE_VERSION);
be->set_table_version (TABLE_NAME, TABLE_VERSION);
PINFO ("Lots table upgraded from version 1 to version %d\n", TABLE_VERSION);
}
@ -189,7 +189,7 @@ GncSqlLotsBackend::write (GncSqlBackend* be)
g_return_val_if_fail (be != NULL, FALSE);
write_objects_t data{be, true, this};
qof_collection_foreach (qof_book_get_collection (be->book, GNC_ID_LOT),
qof_collection_foreach (qof_book_get_collection (be->book(), GNC_ID_LOT),
(QofInstanceForeachCB)do_save_lot, &data);
return data.is_ok;
}
@ -203,7 +203,7 @@ GncSqlColumnTableEntryImpl<CT_LOTREF>::load (const GncSqlBackend* be,
{
load_from_guid_ref(row, obj_name, pObject,
[be](GncGUID* g){
return gnc_lot_lookup(g, be->book);
return gnc_lot_lookup(g, be->book());
});
}

View File

@ -89,10 +89,10 @@ load_single_order (GncSqlBackend* be, GncSqlRow& row)
g_return_val_if_fail (be != NULL, NULL);
guid = gnc_sql_load_guid (be, row);
pOrder = gncOrderLookup (be->book, guid);
pOrder = gncOrderLookup (be->book(), guid);
if (pOrder == NULL)
{
pOrder = gncOrderCreate (be->book);
pOrder = gncOrderCreate (be->book());
}
gnc_sql_load_object (be, row, GNC_ID_ORDER, pOrder, col_table);
qof_instance_mark_clean (QOF_INSTANCE (pOrder));
@ -164,7 +164,7 @@ GncSqlOrderBackend::write (GncSqlBackend* be)
g_return_val_if_fail (be != NULL, FALSE);
write_objects_t data{be, true, this};
qof_object_foreach (GNC_ID_ORDER, be->book, write_single_order, &data);
qof_object_foreach (GNC_ID_ORDER, be->book(), write_single_order, &data);
return data.is_ok;
}
@ -178,7 +178,7 @@ GncSqlColumnTableEntryImpl<CT_ORDERREF>::load (const GncSqlBackend* be,
{
load_from_guid_ref(row, obj_name, pObject,
[be](GncGUID* g){
return gncOrderLookup(be->book, g);
return gncOrderLookup(be->book(), g);
});
}

View File

@ -61,7 +61,7 @@ GncSqlColumnTableEntryImpl<CT_OWNERREF>::load (const GncSqlBackend* be,
g_return_if_fail (be != NULL);
g_return_if_fail (pObject != NULL);
auto book = be->book;
auto book = be->book();
auto buf = std::string{m_col_name} + "_type";
try
{

View File

@ -89,7 +89,7 @@ load_single_price (GncSqlBackend* be, GncSqlRow& row)
g_return_val_if_fail (be != NULL, NULL);
pPrice = gnc_price_create (be->book);
pPrice = gnc_price_create (be->book());
gnc_price_begin_edit (pPrice);
gnc_sql_load_object (be, row, GNC_ID_PRICE, pPrice, col_table);
@ -106,7 +106,7 @@ GncSqlPriceBackend::load_all (GncSqlBackend* be)
g_return_if_fail (be != NULL);
pBook = be->book;
pBook = be->book();
pPriceDB = gnc_pricedb_get_db (pBook);
auto stmt = gnc_sql_create_select_statement (be, TABLE_NAME);
if (stmt != nullptr)
@ -154,7 +154,7 @@ GncSqlPriceBackend::create_tables (GncSqlBackend* be)
{
/* Upgrade 64 bit int handling */
gnc_sql_upgrade_table (be, TABLE_NAME, col_table);
(void)gnc_sql_set_table_version (be, TABLE_NAME, TABLE_VERSION);
be->set_table_version (TABLE_NAME, TABLE_VERSION);
PINFO ("Prices table upgraded from version 1 to version %d\n", TABLE_VERSION);
}
@ -179,7 +179,7 @@ GncSqlPriceBackend::commit (GncSqlBackend* be, QofInstance* inst)
{
op = OP_DB_DELETE;
}
else if (be->is_pristine_db || is_infant)
else if (be->pristine() || is_infant)
{
op = OP_DB_INSERT;
}
@ -226,7 +226,7 @@ GncSqlPriceBackend::write (GncSqlBackend* be)
g_return_val_if_fail (be != NULL, FALSE);
write_objects_t data{be, true, this};
auto priceDB = gnc_pricedb_get_db (be->book);
auto priceDB = gnc_pricedb_get_db (be->book());
return gnc_pricedb_foreach_price (priceDB, write_price, &data, TRUE);
}

View File

@ -321,7 +321,7 @@ gnc_sql_set_recurrences_from_db (GncSqlBackend* be, const GncGUID* guid)
(void)guid_to_string_buff (guid, guid_buf);
buf = g_strdup_printf ("SELECT * FROM %s WHERE obj_guid='%s'", TABLE_NAME,
guid_buf);
auto stmt = be->conn->create_statement_from_sql (buf);
auto stmt = be->create_statement_from_sql (buf);
g_free (buf);
auto result = gnc_sql_execute_select_statement (be, stmt);
return result;
@ -424,7 +424,7 @@ GncSqlRecurrenceBackend::create_tables (GncSqlBackend* be)
{
upgrade_recurrence_table_1_2 (be);
}
(void)gnc_sql_set_table_version (be, TABLE_NAME, TABLE_VERSION);
be->set_table_version (TABLE_NAME, TABLE_VERSION);
PINFO ("Recurrence table upgraded from version %d to version %d\n", version,
TABLE_VERSION);
}

View File

@ -104,7 +104,7 @@ load_single_sx (GncSqlBackend* be, GncSqlRow& row)
guid = gnc_sql_load_guid (be, row);
g_assert (guid != NULL);
pSx = xaccSchedXactionMalloc (be->book);
pSx = xaccSchedXactionMalloc (be->book());
gnc_sx_begin_edit (pSx);
gnc_sql_load_object (be, row, GNC_SX_ID, pSx, col_table);
@ -128,7 +128,7 @@ GncSqlSchedXactionBackend::load_all (GncSqlBackend* be)
auto result = gnc_sql_execute_select_statement (be, stmt);
SchedXactions* sxes;
GList* list = NULL;
sxes = gnc_book_get_schedxactions (be->book);
sxes = gnc_book_get_schedxactions (be->book());
for (auto row : *result)
{
@ -171,7 +171,7 @@ GncSqlSchedXactionBackend::commit (GncSqlBackend* be, QofInstance* inst)
{
op = OP_DB_DELETE;
}
else if (be->is_pristine_db || is_infant)
else if (be->pristine() || is_infant)
{
op = OP_DB_INSERT;
}

View File

@ -714,7 +714,7 @@ gnc_sql_slots_save (GncSqlBackend* be, const GncGUID* guid, gboolean is_infant,
g_return_val_if_fail (pFrame != NULL, FALSE);
// If this is not saving into a new db, clear out the old saved slots first
if (!be->is_pristine_db && !is_infant)
if (!be->pristine() && !is_infant)
{
(void)gnc_sql_slots_delete (be, guid);
}
@ -947,7 +947,7 @@ load_slot_for_book_object (GncSqlBackend* be, GncSqlRow& row,
guid = load_obj_guid (be, row);
g_return_if_fail (guid != NULL);
inst = lookup_fn (guid, be->book);
inst = lookup_fn (guid, be->book());
g_return_if_fail (inst != NULL);
slot_info.be = be;
@ -1045,7 +1045,7 @@ GncSqlSlotsBackend::create_tables (GncSqlBackend* be)
PERR ("Unable to add gdate column\n");
}
}
(void)gnc_sql_set_table_version (be, TABLE_NAME, TABLE_VERSION);
be->set_table_version (TABLE_NAME, TABLE_VERSION);
PINFO ("Slots table upgraded from version %d to version %d\n", version,
TABLE_VERSION);
}

View File

@ -237,7 +237,7 @@ load_taxtable_entries (GncSqlBackend* be, GncTaxTable* tt)
g_value_set_string (&value, guid_buf);
buf = g_strdup_printf ("SELECT * FROM %s WHERE taxtable='%s'",
TTENTRIES_TABLE_NAME, guid_buf);
auto stmt = be->conn->create_statement_from_sql (buf);
auto stmt = be->create_statement_from_sql (buf);
g_free (buf);
auto result = gnc_sql_execute_select_statement (be, stmt);
for (auto row : *result)
@ -254,10 +254,10 @@ load_single_taxtable (GncSqlBackend* be, GncSqlRow& row,
g_return_if_fail (be != NULL);
guid = gnc_sql_load_guid (be, row);
tt = gncTaxTableLookup (be->book, guid);
tt = gncTaxTableLookup (be->book(), guid);
if (tt == NULL)
{
tt = gncTaxTableCreate (be->book);
tt = gncTaxTableCreate (be->book());
}
gnc_sql_load_object (be, row, GNC_ID_TAXTABLE, tt, tt_col_table);
gnc_sql_slots_load (be, QOF_INSTANCE (tt));
@ -342,7 +342,7 @@ GncSqlTaxTableBackend::create_tables (GncSqlBackend* be)
{
/* Upgrade 64 bit int handling */
gnc_sql_upgrade_table (be, TT_TABLE_NAME, tt_col_table);
gnc_sql_set_table_version (be, TT_TABLE_NAME, TT_TABLE_VERSION);
be->set_table_version (TT_TABLE_NAME, TT_TABLE_VERSION);
PINFO ("Taxtables table upgraded from version 1 to version %d\n",
TT_TABLE_VERSION);
}
@ -357,7 +357,7 @@ GncSqlTaxTableBackend::create_tables (GncSqlBackend* be)
{
/* Upgrade 64 bit int handling */
gnc_sql_upgrade_table (be, TTENTRIES_TABLE_NAME, ttentries_col_table);
gnc_sql_set_table_version (be, TTENTRIES_TABLE_NAME, TTENTRIES_TABLE_VERSION);
be->set_table_version (TTENTRIES_TABLE_NAME, TTENTRIES_TABLE_VERSION);
PINFO ("Taxtable entries table upgraded from version 1 to version %d\n",
TTENTRIES_TABLE_VERSION);
}
@ -423,7 +423,7 @@ GncSqlTaxTableBackend::commit (GncSqlBackend* be, QofInstance* inst)
{
op = OP_DB_DELETE;
}
else if (be->is_pristine_db || is_infant)
else if (be->pristine() || is_infant)
{
op = OP_DB_INSERT;
}
@ -477,7 +477,7 @@ GncSqlTaxTableBackend::write (GncSqlBackend* be)
g_return_val_if_fail (be != NULL, FALSE);
write_objects_t data{be, true, this};
qof_object_foreach (GNC_ID_TAXTABLE, be->book, save_next_taxtable, &data);
qof_object_foreach (GNC_ID_TAXTABLE, be->book(), save_next_taxtable, &data);
return data.is_ok;
}
@ -491,7 +491,7 @@ GncSqlColumnTableEntryImpl<CT_TAXTABLEREF>::load (const GncSqlBackend* be,
{
load_from_guid_ref(row, obj_name, pObject,
[be](GncGUID* g){
return gncTaxTableLookup(be->book, g);
return gncTaxTableLookup(be->book(), g);
});
}

View File

@ -231,12 +231,12 @@ load_single_split (GncSqlBackend* be, GncSqlRow& row)
else
{
split_guid = *guid;
pSplit = xaccSplitLookup (&split_guid, be->book);
pSplit = xaccSplitLookup (&split_guid, be->book());
}
if (pSplit == NULL)
{
pSplit = xaccMallocSplit (be->book);
pSplit = xaccMallocSplit (be->book());
}
/* If the split is dirty, don't overwrite it */
@ -246,12 +246,12 @@ load_single_split (GncSqlBackend* be, GncSqlRow& row)
}
/*# -ifempty */
if (pSplit != xaccSplitLookup (&split_guid, be->book))
if (pSplit != xaccSplitLookup (&split_guid, be->book()))
{
gchar guidstr[GUID_ENCODING_LENGTH + 1];
guid_to_string_buff (qof_instance_get_guid (pSplit), guidstr);
PERR ("A malformed split with id %s was found in the dataset.", guidstr);
qof_backend_set_error (&be->be, ERR_BACKEND_DATA_CORRUPT);
qof_backend_set_error ((QofBackend*)be, ERR_BACKEND_DATA_CORRUPT);
pSplit = NULL;
}
return pSplit;
@ -308,22 +308,22 @@ load_single_tx (GncSqlBackend* be, GncSqlRow& row)
tx_guid = *guid;
// Don't overwrite the transaction if it's already been loaded (and possibly modified).
pTx = xaccTransLookup (&tx_guid, be->book);
pTx = xaccTransLookup (&tx_guid, be->book());
if (pTx != NULL)
{
return NULL;
}
pTx = xaccMallocTransaction (be->book);
pTx = xaccMallocTransaction (be->book());
xaccTransBeginEdit (pTx);
gnc_sql_load_object (be, row, GNC_ID_TRANS, pTx, tx_col_table);
if (pTx != xaccTransLookup (&tx_guid, be->book))
if (pTx != xaccTransLookup (&tx_guid, be->book()))
{
gchar guidstr[GUID_ENCODING_LENGTH + 1];
guid_to_string_buff (qof_instance_get_guid (pTx), guidstr);
PERR ("A malformed transaction with id %s was found in the dataset.", guidstr);
qof_backend_set_error (&be->be, ERR_BACKEND_DATA_CORRUPT);
qof_backend_set_error ((QofBackend*)be, ERR_BACKEND_DATA_CORRUPT);
pTx = NULL;
}
@ -370,7 +370,7 @@ query_transactions (GncSqlBackend* be, const GncSqlStatementPtr& stmt)
#if LOAD_TRANSACTIONS_AS_NEEDED
GSList* bal_list = NULL;
GSList* nextbal;
Account* root = gnc_book_get_root_account (be->book);
Account* root = gnc_book_get_root_account (be->book());
qof_event_suspend ();
xaccAccountBeginEdit (root);
@ -506,7 +506,7 @@ GncSqlTransBackend::create_tables (GncSqlBackend* be)
2->3: allow dates to be NULL
*/
gnc_sql_upgrade_table (be, m_table_name.c_str(), tx_col_table);
(void)gnc_sql_set_table_version (be, m_table_name.c_str(), m_version);
be->set_table_version (m_table_name.c_str(), m_version);
PINFO ("Transactions table upgraded from version %d to version %d\n",
version, m_version);
}
@ -544,7 +544,7 @@ GncSqlSplitBackend::create_tables (GncSqlBackend* be)
m_table_name.c_str(),
account_guid_col_table))
PERR ("Unable to create index\n");
(void)gnc_sql_set_table_version (be, m_table_name.c_str(), m_version);
be->set_table_version (m_table_name.c_str(), m_version);
PINFO ("Splits table upgraded from version %d to version %d\n", version,
m_version);
}
@ -625,7 +625,7 @@ GncSqlSplitBackend::commit (GncSqlBackend* be, QofInstance* inst)
{
op = OP_DB_DELETE;
}
else if (be->is_pristine_db || is_infant)
else if (be->pristine() || is_infant)
{
op = OP_DB_INSERT;
}
@ -668,7 +668,7 @@ GncSqlTransBackend::commit (GncSqlBackend* be, QofInstance* inst)
{
op = OP_DB_DELETE;
}
else if (be->is_pristine_db || is_infant)
else if (be->pristine() || is_infant)
{
op = OP_DB_INSERT;
}
@ -685,7 +685,7 @@ GncSqlTransBackend::commit (GncSqlBackend* be, QofInstance* inst)
if (! is_ok)
{
err = "Commodity save failed: Probably an invalid or missing currency";
qof_backend_set_error (&be->be, ERR_BACKEND_DATA_CORRUPT);
qof_backend_set_error ((QofBackend*)be, ERR_BACKEND_DATA_CORRUPT);
}
}
@ -1238,7 +1238,7 @@ set_acct_bal_account_from_guid (gpointer pObject, gpointer pValue)
g_return_if_fail (pObject != NULL);
g_return_if_fail (pValue != NULL);
bal->acct = xaccAccountLookup (guid, bal->be->book);
bal->acct = xaccAccountLookup (guid, bal->be->book());
}
static void
@ -1389,7 +1389,7 @@ GncSqlColumnTableEntryImpl<CT_TXREF>::load (const GncSqlBackend* be,
auto val = row.get_string_at_col (m_col_name);
GncGUID guid;
(void)string_to_guid (val.c_str(), &guid);
auto tx = xaccTransLookup (&guid, be->book);
auto tx = xaccTransLookup (&guid, be->book());
// If the transaction is not found, try loading it
if (tx == nullptr)
@ -1399,7 +1399,7 @@ GncSqlColumnTableEntryImpl<CT_TXREF>::load (const GncSqlBackend* be,
auto stmt = gnc_sql_create_statement_from_sql ((GncSqlBackend*)be,
buf.c_str());
query_transactions ((GncSqlBackend*)be, stmt);
tx = xaccTransLookup (&guid, be->book);
tx = xaccTransLookup (&guid, be->book());
}
if (tx != nullptr)

View File

@ -99,10 +99,10 @@ load_single_vendor (GncSqlBackend* be, GncSqlRow& row)
g_return_val_if_fail (be != NULL, NULL);
guid = gnc_sql_load_guid (be, row);
pVendor = gncVendorLookup (be->book, guid);
pVendor = gncVendorLookup (be->book(), guid);
if (pVendor == NULL)
{
pVendor = gncVendorCreate (be->book);
pVendor = gncVendorCreate (be->book());
}
gnc_sql_load_object (be, row, GNC_ID_VENDOR, pVendor, col_table);
qof_instance_mark_clean (QOF_INSTANCE (pVendor));
@ -154,7 +154,7 @@ GncSqlVendorBackend::commit (GncSqlBackend* be, QofInstance* inst)
{
op = OP_DB_DELETE;
}
else if (be->is_pristine_db || is_infant)
else if (be->pristine() || is_infant)
{
op = OP_DB_INSERT;
}
@ -229,7 +229,7 @@ GncSqlVendorBackend::write (GncSqlBackend* be)
g_return_val_if_fail (be != NULL, FALSE);
write_objects_t data{be, true, this};
qof_object_foreach (GNC_ID_VENDOR, be->book, write_single_vendor, &data);
qof_object_foreach (GNC_ID_VENDOR, be->book(), write_single_vendor, &data);
return data.is_ok;
}

View File

@ -105,6 +105,9 @@ public:
virtual std::string quote_string (const std::string& str)
const noexcept override { return std::string{str}; }
int dberror() const noexcept override { return 0; }
void set_error(int error, int repeat, bool retry) noexcept override { return; }
bool verify() noexcept override { return true; }
bool retry_connection(const char* msg) noexcept override { return true; }
private:
GncMockSqlResult m_result;
};
@ -256,7 +259,6 @@ test_dirty_cb (QofBook* book, gboolean dirty, gpointer data)
static void
test_gnc_sql_commit_edit (void)
{
GncSqlBackend be;
QofInstance* inst;
guint dirty_called = 0;
GncMockSqlConnection conn;
@ -282,52 +284,51 @@ test_gnc_sql_commit_edit (void)
g_test_log_set_fatal_handler ((GTestLogFatalFunc)test_list_handler, NULL);
qof_object_initialize ();
be.book = qof_book_new ();
be.conn = &conn;
auto book = qof_book_new();
GncSqlBackend be (&conn, book);
inst = static_cast<decltype (inst)> (g_object_new (QOF_TYPE_INSTANCE, NULL));
qof_instance_init_data (inst, QOF_ID_NULL, be.book);
be.loading = FALSE;
qof_book_set_dirty_cb (be.book, test_dirty_cb, &dirty_called);
qof_instance_init_data (inst, QOF_ID_NULL, book);
qof_book_set_dirty_cb (book, test_dirty_cb, &dirty_called);
qof_instance_set_dirty_flag (inst, TRUE);
qof_book_mark_session_dirty (be.book);
qof_book_mark_session_dirty (book);
g_assert (qof_instance_get_dirty_flag (inst));
g_assert (qof_book_session_not_saved (be.book));
g_assert (qof_book_session_not_saved (book));
g_assert_cmpint (dirty_called, == , 1);
gnc_sql_commit_edit (&be, inst);
g_assert (!qof_instance_get_dirty_flag (inst));
g_assert (!qof_book_session_not_saved (be.book));
g_assert (!qof_book_session_not_saved (book));
g_assert_cmpint (dirty_called, == , 0);
g_assert_cmpint (check1.hits, == , 2);
g_assert_cmpint (check2.hits, == , 0);
qof_book_mark_session_dirty (be.book);
qof_book_mark_session_dirty (book);
g_assert (!qof_instance_get_dirty_flag (QOF_INSTANCE (be.book)));
g_assert (qof_book_session_not_saved (be.book));
g_assert (!qof_instance_get_dirty_flag (QOF_INSTANCE (book)));
g_assert (qof_book_session_not_saved (book));
g_assert_cmpint (dirty_called, == , 1);
gnc_sql_commit_edit (&be, QOF_INSTANCE (be.book));
g_assert (!qof_instance_get_dirty_flag (QOF_INSTANCE (be.book)));
g_assert (qof_book_session_not_saved (be.book));
gnc_sql_commit_edit (&be, QOF_INSTANCE (book));
g_assert (!qof_instance_get_dirty_flag (QOF_INSTANCE (book)));
g_assert (qof_book_session_not_saved (book));
g_assert_cmpint (dirty_called, == , 1);
g_assert_cmpint (check1.hits, == , 2);
g_assert_cmpint (check2.hits, == , 0);
qof_instance_set_dirty_flag (QOF_INSTANCE (be.book), TRUE);
qof_instance_set_dirty_flag (QOF_INSTANCE (book), TRUE);
g_assert (qof_instance_get_dirty_flag (QOF_INSTANCE (be.book)));
g_assert (qof_book_session_not_saved (be.book));
g_assert (qof_instance_get_dirty_flag (QOF_INSTANCE (book)));
g_assert (qof_book_session_not_saved (book));
g_assert_cmpint (dirty_called, == , 1);
gnc_sql_commit_edit (&be, QOF_INSTANCE (be.book));
g_assert (!qof_instance_get_dirty_flag (QOF_INSTANCE (be.book)));
g_assert (!qof_book_session_not_saved (be.book));
gnc_sql_commit_edit (&be, QOF_INSTANCE (book));
g_assert (!qof_instance_get_dirty_flag (QOF_INSTANCE (book)));
g_assert (!qof_book_session_not_saved (book));
g_assert_cmpint (dirty_called, == , 0);
g_assert_cmpint (check1.hits, == , 2);
g_assert_cmpint (check2.hits, == , 2);
g_log_remove_handler (logdomain, hdlr1);
g_object_unref (inst);
g_object_unref (be.book);
g_object_unref (book);
}
/* handle_and_term
static void
@ -633,14 +634,7 @@ gnc_sql_convert_timespec_to_string (const GncSqlBackend* be, Timespec ts)// C: 1
static void
test_gnc_sql_convert_timespec_to_string ()
{
GncSqlBackend be = {{
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, ERR_BACKEND_NO_ERR, nullptr,
0, nullptr
},
nullptr, nullptr, FALSE, FALSE, FALSE, nullptr,
"%4d-%02d-%02d %02d:%02d:%02d"
};
GncSqlBackend be {nullptr, nullptr, "%4d-%02d-%02d %02d:%02d:%02d"};
const char* date[numtests] = {"1995-03-11 19:17:26",
"2001-04-20 11:44:07",
"1964-02-29 09:15:23",