mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Convert QofBackend to a C++ class and the backend class hierarchy into C++.
Getting rid of all of the casting and different flavors of backend pointers and adopting the C++ member-function calling conventions.
This commit is contained in:
parent
06af7d794f
commit
abb66016bc
@ -111,31 +111,8 @@ static QofLogModule log_module = G_LOG_DOMAIN;
|
||||
|
||||
static void adjust_sql_options (dbi_conn connection);
|
||||
static bool save_may_clobber_data (dbi_conn conn, const std::string& dbname);
|
||||
static void init_sql_backend (GncDbiBackend* dbi_be);
|
||||
|
||||
static bool conn_test_dbi_library (dbi_conn conn, QofBackend* qof_be);
|
||||
|
||||
template <DbType T> void gnc_dbi_session_begin(QofBackend* qof_be,
|
||||
QofSession* session,
|
||||
const char* book_id,
|
||||
gboolean ignore_lock,
|
||||
gboolean create, gboolean force);
|
||||
template <DbType Type> QofBackend*
|
||||
new_backend ()
|
||||
{
|
||||
auto dbi_be = new GncDbiBackend(nullptr, nullptr);
|
||||
assert (dbi_be != nullptr);
|
||||
|
||||
QofBackend* qof_be = reinterpret_cast<decltype(qof_be)>(dbi_be);
|
||||
qof_backend_init (qof_be);
|
||||
|
||||
qof_be->session_begin = gnc_dbi_session_begin<Type>;
|
||||
init_sql_backend (dbi_be);
|
||||
|
||||
return qof_be;
|
||||
}
|
||||
|
||||
template <DbType T>
|
||||
template <DbType Type>
|
||||
class QofDbiBackendProvider : public QofBackendProvider
|
||||
{
|
||||
public:
|
||||
@ -146,7 +123,10 @@ public:
|
||||
QofDbiBackendProvider(QofDbiBackendProvider&&) = delete;
|
||||
QofDbiBackendProvider operator=(QofDbiBackendProvider&&) = delete;
|
||||
~QofDbiBackendProvider () = default;
|
||||
QofBackend* create_backend(void) { return new_backend<T>(); }
|
||||
QofBackend* create_backend(void)
|
||||
{
|
||||
return new GncDbiBackend<Type>(nullptr, nullptr);
|
||||
}
|
||||
bool type_check(const char* type) { return true; }
|
||||
};
|
||||
|
||||
@ -235,14 +215,13 @@ set_options(dbi_conn conn, const PairVec& options)
|
||||
/**
|
||||
* Sets standard db options in a dbi_conn.
|
||||
*
|
||||
* @param qof_be QOF backend
|
||||
* @param conn dbi_conn connection
|
||||
* @param uri UriStrings containing the needed paramters.
|
||||
* @return TRUE if successful, FALSE if error
|
||||
*/
|
||||
static bool
|
||||
set_standard_connection_options (QofBackend* qof_be, dbi_conn conn,
|
||||
const UriStrings& uri)
|
||||
template <DbType Type> bool
|
||||
GncDbiBackend<Type>::set_standard_connection_options (dbi_conn conn,
|
||||
const UriStrings& uri)
|
||||
|
||||
{
|
||||
gint result;
|
||||
@ -266,7 +245,7 @@ set_standard_connection_options (QofBackend* qof_be, dbi_conn conn,
|
||||
}
|
||||
catch (std::runtime_error& err)
|
||||
{
|
||||
qof_backend_set_error (qof_be, ERR_BACKEND_SERVER_ERR);
|
||||
set_error (ERR_BACKEND_SERVER_ERR);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -277,8 +256,7 @@ template <DbType Type> void error_handler(void* conn, void* data);
|
||||
void error_handler(void* conn, void* data);
|
||||
|
||||
template <DbType Type> dbi_conn
|
||||
conn_setup (QofBackend* qof_be, PairVec& options,
|
||||
UriStrings& uri)
|
||||
GncDbiBackend<Type>::conn_setup (PairVec& options, UriStrings& uri)
|
||||
{
|
||||
const char* dbstr = (Type == DbType::DBI_SQLITE ? "sqlite3" :
|
||||
Type == DbType::DBI_MYSQL ? "mysql" : "pgsql");
|
||||
@ -295,13 +273,13 @@ conn_setup (QofBackend* qof_be, PairVec& options,
|
||||
if (conn == nullptr)
|
||||
{
|
||||
PERR ("Unable to create %s dbi connection", dbstr);
|
||||
qof_backend_set_error (qof_be, ERR_BACKEND_BAD_URL);
|
||||
set_error (ERR_BACKEND_BAD_URL);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
dbi_conn_error_handler (conn, error_handler<Type>, qof_be);
|
||||
dbi_conn_error_handler (conn, error_handler<Type>, this);
|
||||
if (!uri.m_dbname.empty() &&
|
||||
!set_standard_connection_options(qof_be, conn, uri))
|
||||
!set_standard_connection_options(conn, uri))
|
||||
{
|
||||
dbi_conn_close(conn);
|
||||
return nullptr;
|
||||
@ -314,7 +292,7 @@ conn_setup (QofBackend* qof_be, PairVec& options,
|
||||
catch (std::runtime_error& err)
|
||||
{
|
||||
dbi_conn_close(conn);
|
||||
qof_backend_set_error (qof_be, ERR_BACKEND_SERVER_ERR);
|
||||
set_error (ERR_BACKEND_SERVER_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@ -322,12 +300,12 @@ conn_setup (QofBackend* qof_be, PairVec& options,
|
||||
return conn;
|
||||
}
|
||||
|
||||
static bool
|
||||
create_database(DbType type, QofBackend *qof_be, dbi_conn conn, const char* db)
|
||||
template <DbType Type>bool
|
||||
GncDbiBackend<Type>::create_database(dbi_conn conn, const char* db)
|
||||
{
|
||||
const char *dbname;
|
||||
const char *dbcreate;
|
||||
if (type == DbType::DBI_MYSQL)
|
||||
if (Type == DbType::DBI_MYSQL)
|
||||
{
|
||||
dbname = "mysql";
|
||||
dbcreate = "CREATE DATABASE %s CHARACTER SET utf8";
|
||||
@ -345,7 +323,7 @@ create_database(DbType type, QofBackend *qof_be, dbi_conn conn, const char* db)
|
||||
}
|
||||
catch (std::runtime_error& err)
|
||||
{
|
||||
qof_backend_set_error (qof_be, ERR_BACKEND_SERVER_ERR);
|
||||
set_error (ERR_BACKEND_SERVER_ERR);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -353,19 +331,19 @@ create_database(DbType type, QofBackend *qof_be, dbi_conn conn, const char* db)
|
||||
if (result < 0)
|
||||
{
|
||||
PERR ("Unable to connect to %s database", dbname);
|
||||
qof_backend_set_error (qof_be, ERR_BACKEND_SERVER_ERR);
|
||||
set_error(ERR_BACKEND_SERVER_ERR);
|
||||
return false;
|
||||
}
|
||||
if (type == DbType::DBI_MYSQL)
|
||||
if (Type == DbType::DBI_MYSQL)
|
||||
adjust_sql_options(conn);
|
||||
auto dresult = dbi_conn_queryf (conn, dbcreate, db);
|
||||
if (dresult == nullptr)
|
||||
{
|
||||
PERR ("Unable to create database '%s'\n", db);
|
||||
qof_backend_set_error (qof_be, ERR_BACKEND_SERVER_ERR);
|
||||
set_error (ERR_BACKEND_SERVER_ERR);
|
||||
return false;
|
||||
}
|
||||
if (type == DbType::DBI_PGSQL)
|
||||
if (Type == DbType::DBI_PGSQL)
|
||||
{
|
||||
const char *alterdb = "ALTER DATABASE %s SET "
|
||||
"standard_conforming_strings TO on";
|
||||
@ -380,26 +358,23 @@ template <> void
|
||||
error_handler<DbType::DBI_SQLITE> (dbi_conn conn, void* user_data)
|
||||
{
|
||||
const char* msg;
|
||||
GncDbiBackend *dbi_be = static_cast<decltype(dbi_be)>(user_data);
|
||||
GncDbiBackend<DbType::DBI_SQLITE> *dbi_be =
|
||||
static_cast<decltype(dbi_be)>(user_data);
|
||||
int errnum = dbi_conn_error (conn, &msg);
|
||||
PERR ("DBI error: %s\n", msg);
|
||||
if (dbi_be->connected())
|
||||
dbi_be->set_error (ERR_BACKEND_MISC, 0, false);
|
||||
dbi_be->set_dbi_error (ERR_BACKEND_MISC, 0, false);
|
||||
}
|
||||
|
||||
template <> void
|
||||
gnc_dbi_session_begin<DbType::DBI_SQLITE>(QofBackend* qof_be,
|
||||
QofSession* session,
|
||||
const char* book_id,
|
||||
gboolean ignore_lock,
|
||||
gboolean create, gboolean force)
|
||||
GncDbiBackend<DbType::DBI_SQLITE>::session_begin(QofSession* session,
|
||||
const char* book_id,
|
||||
bool ignore_lock,
|
||||
bool create, bool force)
|
||||
{
|
||||
GncDbiBackend* dbi_be = reinterpret_cast<decltype(dbi_be)>(qof_be);
|
||||
const char* msg = nullptr;
|
||||
gboolean file_exists;
|
||||
PairVec options;
|
||||
|
||||
g_return_if_fail (qof_be != nullptr);
|
||||
g_return_if_fail (session != nullptr);
|
||||
g_return_if_fail (book_id != nullptr);
|
||||
|
||||
@ -414,9 +389,9 @@ gnc_dbi_session_begin<DbType::DBI_SQLITE>(QofBackend* qof_be,
|
||||
file_exists = g_file_test (filepath.c_str(), ftest);
|
||||
if (!create && !file_exists)
|
||||
{
|
||||
qof_backend_set_error (qof_be, ERR_FILEIO_FILE_NOT_FOUND);
|
||||
qof_backend_set_message (qof_be, "Sqlite3 file %s not found",
|
||||
filepath.c_str());
|
||||
set_error (ERR_FILEIO_FILE_NOT_FOUND);
|
||||
std::string msg{"Sqlite3 file "};
|
||||
set_message (msg + filepath + " not found");
|
||||
PWARN ("Sqlite3 file %s not found", filepath.c_str());
|
||||
LEAVE("Error");
|
||||
return;
|
||||
@ -424,14 +399,14 @@ gnc_dbi_session_begin<DbType::DBI_SQLITE>(QofBackend* qof_be,
|
||||
|
||||
if (create && !force && file_exists)
|
||||
{
|
||||
qof_backend_set_error (qof_be, ERR_BACKEND_STORE_EXISTS);
|
||||
msg = "Might clobber, no force";
|
||||
set_error (ERR_BACKEND_STORE_EXISTS);
|
||||
auto msg = "Might clobber, no force";
|
||||
PWARN ("%s", msg);
|
||||
LEAVE("Error");
|
||||
return;
|
||||
}
|
||||
|
||||
dbi_be->connect(nullptr);
|
||||
connect(nullptr);
|
||||
/* dbi-sqlite3 documentation says that sqlite3 doesn't take a "host" option */
|
||||
options.push_back(std::make_pair("host", "localhost"));
|
||||
auto dirname = g_path_get_dirname (filepath.c_str());
|
||||
@ -441,7 +416,7 @@ gnc_dbi_session_begin<DbType::DBI_SQLITE>(QofBackend* qof_be,
|
||||
if (basename != nullptr) g_free (basename);
|
||||
if (dirname != nullptr) g_free (dirname);
|
||||
UriStrings uri;
|
||||
auto conn = conn_setup<DbType::DBI_SQLITE>(qof_be, options, uri);
|
||||
auto conn = conn_setup(options, uri);
|
||||
if (conn == nullptr)
|
||||
{
|
||||
LEAVE("Error");
|
||||
@ -454,12 +429,12 @@ gnc_dbi_session_begin<DbType::DBI_SQLITE>(QofBackend* qof_be,
|
||||
{
|
||||
dbi_conn_close(conn);
|
||||
PERR ("Unable to connect to %s: %d\n", book_id, result);
|
||||
qof_backend_set_error (qof_be, ERR_BACKEND_BAD_URL);
|
||||
set_error (ERR_BACKEND_BAD_URL);
|
||||
LEAVE("Error");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!conn_test_dbi_library(conn, qof_be))
|
||||
if (!conn_test_dbi_library(conn))
|
||||
{
|
||||
if (create && !file_exists)
|
||||
{
|
||||
@ -477,8 +452,8 @@ gnc_dbi_session_begin<DbType::DBI_SQLITE>(QofBackend* qof_be,
|
||||
|
||||
try
|
||||
{
|
||||
dbi_be->connect(new GncDbiSqlConnection(DbType::DBI_SQLITE,
|
||||
qof_be, conn, ignore_lock));
|
||||
connect(new GncDbiSqlConnection(DbType::DBI_SQLITE,
|
||||
this, conn, ignore_lock));
|
||||
}
|
||||
catch (std::runtime_error& err)
|
||||
{
|
||||
@ -497,7 +472,8 @@ gnc_dbi_session_begin<DbType::DBI_SQLITE>(QofBackend* qof_be,
|
||||
template <> void
|
||||
error_handler<DbType::DBI_MYSQL> (dbi_conn conn, void* user_data)
|
||||
{
|
||||
GncDbiBackend* dbi_be = static_cast<decltype(dbi_be)>(user_data);
|
||||
GncDbiBackend<DbType::DBI_MYSQL>* dbi_be =
|
||||
static_cast<decltype(dbi_be)>(user_data);
|
||||
const char* msg;
|
||||
|
||||
auto err_num = dbi_conn_error (conn, &msg);
|
||||
@ -531,18 +507,18 @@ error_handler<DbType::DBI_MYSQL> (dbi_conn conn, void* user_data)
|
||||
if (err_num == 2006) // Server has gone away
|
||||
{
|
||||
PINFO ("DBI error: %s - Reconnecting...\n", msg);
|
||||
dbi_be->set_error (ERR_BACKEND_CONN_LOST, 1, true);
|
||||
dbi_be->set_dbi_error (ERR_BACKEND_CONN_LOST, 1, true);
|
||||
dbi_be->retry_connection(msg);
|
||||
}
|
||||
else if (err_num == 2003) // Unable to connect
|
||||
{
|
||||
dbi_be->set_error (ERR_BACKEND_CANT_CONNECT, 1, true);
|
||||
dbi_be->set_dbi_error (ERR_BACKEND_CANT_CONNECT, 1, true);
|
||||
dbi_be->retry_connection (msg);
|
||||
}
|
||||
else // Any other error
|
||||
{
|
||||
PERR ("DBI error: %s\n", msg);
|
||||
dbi_be->set_error (ERR_BACKEND_MISC, 0, FALSE);
|
||||
dbi_be->set_dbi_error (ERR_BACKEND_MISC, 0, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -610,16 +586,13 @@ adjust_sql_options (dbi_conn connection)
|
||||
}
|
||||
|
||||
|
||||
template <DbType T> void
|
||||
gnc_dbi_session_begin (QofBackend* qof_be, QofSession* session,
|
||||
const char* book_id, gboolean ignore_lock,
|
||||
gboolean create, gboolean force)
|
||||
template <DbType Type> void
|
||||
GncDbiBackend<Type>::session_begin (QofSession* session, const char* book_id,
|
||||
bool ignore_lock, bool create, bool force)
|
||||
{
|
||||
GncDbiBackend* dbi_be = reinterpret_cast<decltype(dbi_be)>(qof_be);
|
||||
GncDbiTestResult dbi_test_result = GNC_DBI_PASS;
|
||||
PairVec options;
|
||||
|
||||
g_return_if_fail (qof_be != nullptr);
|
||||
g_return_if_fail (session != nullptr);
|
||||
g_return_if_fail (book_id != nullptr);
|
||||
|
||||
@ -630,7 +603,7 @@ gnc_dbi_session_begin (QofBackend* qof_be, QofSession* session,
|
||||
where username, password and port are optional) */
|
||||
UriStrings uri(book_id);
|
||||
|
||||
if (T == DbType::DBI_PGSQL)
|
||||
if (Type == DbType::DBI_PGSQL)
|
||||
{
|
||||
if (uri.m_portnum == 0)
|
||||
uri.m_portnum = PGSQL_DEFAULT_PORT;
|
||||
@ -642,31 +615,31 @@ gnc_dbi_session_begin (QofBackend* qof_be, QofSession* session,
|
||||
uri.m_dbname = std::string{lcname};
|
||||
g_free(lcname);
|
||||
}
|
||||
dbi_be->connect(nullptr);
|
||||
connect(nullptr);
|
||||
|
||||
auto conn = conn_setup<T>(qof_be, options, uri);
|
||||
auto conn = conn_setup(options, uri);
|
||||
if (conn == nullptr)
|
||||
{
|
||||
LEAVE("Error");
|
||||
return;
|
||||
}
|
||||
|
||||
dbi_be->set_exists(true); //May be unset in the error handler.
|
||||
m_exists = true; //May be unset in the error handler.
|
||||
auto result = dbi_conn_connect (conn);
|
||||
if (result == 0)
|
||||
{
|
||||
if (T == DbType::DBI_MYSQL)
|
||||
if (Type == DbType::DBI_MYSQL)
|
||||
adjust_sql_options (conn);
|
||||
if(!conn_test_dbi_library(conn, qof_be))
|
||||
if(!conn_test_dbi_library(conn))
|
||||
{
|
||||
dbi_conn_close(conn);
|
||||
LEAVE("Error");
|
||||
return;
|
||||
}
|
||||
if (create && !force && save_may_clobber_data (conn,
|
||||
uri.quote_dbname(T)))
|
||||
uri.quote_dbname(Type)))
|
||||
{
|
||||
qof_backend_set_error (qof_be, ERR_BACKEND_STORE_EXISTS);
|
||||
set_error (ERR_BACKEND_STORE_EXISTS);
|
||||
PWARN ("Databse already exists, Might clobber it.");
|
||||
dbi_conn_close(conn);
|
||||
LEAVE("Error");
|
||||
@ -677,10 +650,10 @@ gnc_dbi_session_begin (QofBackend* qof_be, QofSession* session,
|
||||
else
|
||||
{
|
||||
|
||||
if (dbi_be->exists())
|
||||
if (m_exists)
|
||||
{
|
||||
PERR ("Unable to connect to database '%s'\n", uri.dbname());
|
||||
qof_backend_set_error (qof_be, ERR_BACKEND_SERVER_ERR);
|
||||
set_error (ERR_BACKEND_SERVER_ERR);
|
||||
dbi_conn_close(conn);
|
||||
LEAVE("Error");
|
||||
return;
|
||||
@ -688,45 +661,46 @@ gnc_dbi_session_begin (QofBackend* qof_be, QofSession* session,
|
||||
|
||||
if (create)
|
||||
{
|
||||
if (!create_database(T, qof_be, conn, uri.quote_dbname(T).c_str()))
|
||||
if (!create_database(conn, uri.quote_dbname(Type).c_str()))
|
||||
{
|
||||
dbi_conn_close(conn);
|
||||
LEAVE("Error");
|
||||
return;
|
||||
}
|
||||
conn = conn_setup<T>(qof_be, options, uri);
|
||||
conn = conn_setup(options, uri);
|
||||
result = dbi_conn_connect (conn);
|
||||
if (result < 0)
|
||||
{
|
||||
PERR ("Unable to create database '%s'\n", uri.dbname());
|
||||
qof_backend_set_error (qof_be, ERR_BACKEND_SERVER_ERR);
|
||||
set_error (ERR_BACKEND_SERVER_ERR);
|
||||
dbi_conn_close(conn);
|
||||
LEAVE("Error");
|
||||
return;
|
||||
}
|
||||
if (T == DbType::DBI_MYSQL)
|
||||
if (Type == DbType::DBI_MYSQL)
|
||||
adjust_sql_options (conn);
|
||||
if (!conn_test_dbi_library(conn, qof_be))
|
||||
if (!conn_test_dbi_library(conn))
|
||||
{
|
||||
if (T == DbType::DBI_PGSQL)
|
||||
if (Type == DbType::DBI_PGSQL)
|
||||
dbi_conn_select_db (conn, "template1");
|
||||
dbi_conn_queryf (conn, "DROP DATABASE %s",
|
||||
uri.quote_dbname(T).c_str());
|
||||
uri.quote_dbname(Type).c_str());
|
||||
dbi_conn_close(conn);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qof_backend_set_error (qof_be, ERR_BACKEND_NO_SUCH_DB);
|
||||
qof_backend_set_message (qof_be, "Database %s not found", uri.dbname());
|
||||
set_error(ERR_BACKEND_NO_SUCH_DB);
|
||||
std::string msg{"Database "};
|
||||
set_message(msg + uri.dbname() + " not found");
|
||||
}
|
||||
}
|
||||
|
||||
dbi_be->connect(nullptr);
|
||||
connect(nullptr);
|
||||
try
|
||||
{
|
||||
dbi_be->connect(new GncDbiSqlConnection(T, qof_be, conn, ignore_lock));
|
||||
connect(new GncDbiSqlConnection(Type, this, conn, ignore_lock));
|
||||
}
|
||||
catch (std::runtime_error& err)
|
||||
{
|
||||
@ -745,7 +719,8 @@ gnc_dbi_session_begin (QofBackend* qof_be, QofSession* session,
|
||||
template<> void
|
||||
error_handler<DbType::DBI_PGSQL> (dbi_conn conn, void* user_data)
|
||||
{
|
||||
GncDbiBackend* dbi_be = static_cast<decltype(dbi_be)>(user_data);
|
||||
GncDbiBackend<DbType::DBI_PGSQL>* dbi_be =
|
||||
static_cast<decltype(dbi_be)>(user_data);
|
||||
const char* msg;
|
||||
|
||||
(void)dbi_conn_error (conn, &msg);
|
||||
@ -764,7 +739,7 @@ error_handler<DbType::DBI_PGSQL> (dbi_conn conn, void* user_data)
|
||||
return;
|
||||
}
|
||||
PINFO ("DBI error: %s - Reconnecting...\n", msg);
|
||||
dbi_be->set_error (ERR_BACKEND_CONN_LOST, 1, true);
|
||||
dbi_be->set_dbi_error (ERR_BACKEND_CONN_LOST, 1, true);
|
||||
dbi_be->retry_connection(msg);
|
||||
}
|
||||
else if (g_str_has_prefix (msg, "connection pointer is NULL") ||
|
||||
@ -776,7 +751,7 @@ error_handler<DbType::DBI_PGSQL> (dbi_conn conn, void* user_data)
|
||||
ERR_BACKEND_CANT_CONNECT);
|
||||
else
|
||||
{
|
||||
dbi_be->set_error(ERR_BACKEND_CANT_CONNECT, 1, true);
|
||||
dbi_be->set_dbi_error(ERR_BACKEND_CANT_CONNECT, 1, true);
|
||||
dbi_be->retry_connection (msg);
|
||||
}
|
||||
}
|
||||
@ -784,38 +759,28 @@ error_handler<DbType::DBI_PGSQL> (dbi_conn conn, void* user_data)
|
||||
{
|
||||
PERR ("DBI error: %s\n", msg);
|
||||
if (dbi_be->connected())
|
||||
dbi_be->set_error (ERR_BACKEND_MISC, 0, false);
|
||||
dbi_be->set_dbi_error (ERR_BACKEND_MISC, 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================================= */
|
||||
|
||||
static void
|
||||
gnc_dbi_session_end (QofBackend* qof_be)
|
||||
template <DbType Type> void
|
||||
GncDbiBackend<Type>::session_end ()
|
||||
{
|
||||
GncDbiBackend* dbi_be = reinterpret_cast<decltype(dbi_be)>(qof_be);
|
||||
|
||||
g_return_if_fail (dbi_be != nullptr);
|
||||
|
||||
ENTER (" ");
|
||||
|
||||
dbi_be->finalize_version_info ();
|
||||
dbi_be->connect(nullptr);
|
||||
finalize_version_info ();
|
||||
connect(nullptr);
|
||||
|
||||
LEAVE (" ");
|
||||
}
|
||||
|
||||
static void
|
||||
gnc_dbi_destroy_backend (QofBackend* qof_be)
|
||||
template <DbType Type>
|
||||
GncDbiBackend<Type>::~GncDbiBackend()
|
||||
{
|
||||
g_return_if_fail (qof_be != nullptr);
|
||||
|
||||
/* Stop transaction logging */
|
||||
xaccLogSetBaseName (nullptr);
|
||||
|
||||
qof_backend_destroy (qof_be);
|
||||
|
||||
g_free (qof_be);
|
||||
}
|
||||
|
||||
/* ================================================================= */
|
||||
@ -829,41 +794,38 @@ gnc_dbi_destroy_backend (QofBackend* qof_be)
|
||||
* then the database will be loaded read-only. A resave will update
|
||||
* both values to match this version of Gnucash.
|
||||
*/
|
||||
void
|
||||
gnc_dbi_load (QofBackend* qof_be, QofBook* book, QofBackendLoadType loadType)
|
||||
template <DbType Type> void
|
||||
GncDbiBackend<Type>::load (QofBook* book, QofBackendLoadType loadType)
|
||||
{
|
||||
GncDbiBackend* dbi_be = reinterpret_cast<decltype(dbi_be)>(qof_be);
|
||||
|
||||
g_return_if_fail (qof_be != nullptr);
|
||||
g_return_if_fail (book != nullptr);
|
||||
|
||||
ENTER ("dbi_be=%p, book=%p", dbi_be, book);
|
||||
ENTER ("dbi_be=%p, book=%p", this, book);
|
||||
|
||||
if (loadType == LOAD_TYPE_INITIAL_LOAD)
|
||||
{
|
||||
|
||||
// Set up table version information
|
||||
dbi_be->init_version_info ();
|
||||
assert (dbi_be->m_book == nullptr);
|
||||
dbi_be->create_tables();
|
||||
init_version_info ();
|
||||
assert (m_book == nullptr);
|
||||
create_tables();
|
||||
}
|
||||
|
||||
dbi_be->load(book, loadType);
|
||||
GncSqlBackend::load(book, loadType);
|
||||
|
||||
if (GNUCASH_RESAVE_VERSION > dbi_be->get_table_version("Gnucash"))
|
||||
if (GNUCASH_RESAVE_VERSION > get_table_version("Gnucash"))
|
||||
{
|
||||
/* The database was loaded with an older database schema or
|
||||
* data semantics. In order to ensure consistency, the whole
|
||||
* thing needs to be saved anew. */
|
||||
qof_backend_set_error (qof_be, ERR_SQL_DB_TOO_OLD);
|
||||
set_error(ERR_SQL_DB_TOO_OLD);
|
||||
}
|
||||
else if (GNUCASH_RESAVE_VERSION < dbi_be->get_table_version("Gnucash-Resave"))
|
||||
else if (GNUCASH_RESAVE_VERSION < get_table_version("Gnucash-Resave"))
|
||||
{
|
||||
/* Worse, the database was created with a newer version. We
|
||||
* can't safely write to this database, so the user will have
|
||||
* to do a "save as" to make one that we can write to.
|
||||
*/
|
||||
qof_backend_set_error (qof_be, ERR_SQL_DB_TOO_NEW);
|
||||
set_error(ERR_SQL_DB_TOO_NEW);
|
||||
}
|
||||
|
||||
|
||||
@ -894,17 +856,14 @@ save_may_clobber_data (dbi_conn conn, const std::string& dbname)
|
||||
* no errors. If there are errors, drop the new tables and restore the
|
||||
* originals.
|
||||
*
|
||||
* @param qof_be: QofBackend for the session.
|
||||
* @param book: QofBook to be saved in the database.
|
||||
*/
|
||||
void
|
||||
gnc_dbi_safe_sync_all (QofBackend* qof_be, QofBook* book)
|
||||
template <DbType Type> void
|
||||
GncDbiBackend<Type>::safe_sync (QofBook* book)
|
||||
{
|
||||
GncDbiBackend* dbi_be = reinterpret_cast<decltype(dbi_be)>(qof_be);
|
||||
auto conn = dynamic_cast<GncDbiSqlConnection*>(dbi_be->m_conn);
|
||||
auto conn = dynamic_cast<GncDbiSqlConnection*>(m_conn);
|
||||
|
||||
g_return_if_fail (conn != nullptr);
|
||||
g_return_if_fail (dbi_be != nullptr);
|
||||
g_return_if_fail (book != nullptr);
|
||||
|
||||
ENTER ("book=%p, primary=%p", book, m_book);
|
||||
@ -921,12 +880,11 @@ gnc_dbi_safe_sync_all (QofBackend* qof_be, QofBook* book)
|
||||
set_error (ERR_BACKEND_SERVER_ERR);
|
||||
set_message("Failed to drop indexes");
|
||||
LEAVE ("Failed to drop indexes");
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
dbi_be->sync_all(book);
|
||||
if (qof_backend_check_error (qof_be))
|
||||
sync(m_book);
|
||||
if (check_error())
|
||||
{
|
||||
conn->table_operation (TableOpType::rollback);
|
||||
LEAVE ("Failed to create new database tables");
|
||||
@ -936,63 +894,6 @@ gnc_dbi_safe_sync_all (QofBackend* qof_be, QofBook* book)
|
||||
LEAVE ("book=%p", m_book);
|
||||
}
|
||||
/* ================================================================= */
|
||||
static void
|
||||
gnc_dbi_begin_edit (QofBackend* qof_be, QofInstance* inst)
|
||||
{
|
||||
GncDbiBackend* dbi_be = reinterpret_cast<decltype(dbi_be)>(qof_be);
|
||||
|
||||
g_return_if_fail (dbi_be != nullptr);
|
||||
g_return_if_fail (inst != nullptr);
|
||||
|
||||
dbi_be->begin_edit(inst);
|
||||
}
|
||||
|
||||
static void
|
||||
gnc_dbi_rollback_edit (QofBackend* qof_be, QofInstance* inst)
|
||||
{
|
||||
GncDbiBackend* dbi_be = reinterpret_cast<decltype(dbi_be)>(qof_be);
|
||||
|
||||
g_return_if_fail (dbi_be != nullptr);
|
||||
g_return_if_fail (inst != nullptr);
|
||||
|
||||
dbi_be->rollback_edit(inst);
|
||||
}
|
||||
|
||||
static void
|
||||
gnc_dbi_commit_edit (QofBackend* qof_be, QofInstance* inst)
|
||||
{
|
||||
GncDbiBackend* dbi_be = reinterpret_cast<decltype(dbi_be)>(qof_be);
|
||||
|
||||
g_return_if_fail (dbi_be != nullptr);
|
||||
g_return_if_fail (inst != nullptr);
|
||||
|
||||
dbi_be->commit_edit(inst);
|
||||
}
|
||||
|
||||
/* ================================================================= */
|
||||
|
||||
static void
|
||||
init_sql_backend (GncDbiBackend* dbi_be)
|
||||
{
|
||||
QofBackend* qof_be = reinterpret_cast<decltype(qof_be)>(dbi_be);
|
||||
|
||||
qof_be->session_end = gnc_dbi_session_end;
|
||||
qof_be->destroy_backend = gnc_dbi_destroy_backend;
|
||||
|
||||
qof_be->load = gnc_dbi_load;
|
||||
|
||||
/* The gda backend treats accounting periods transactionally. */
|
||||
qof_be->begin = gnc_dbi_begin_edit;
|
||||
qof_be->commit = gnc_dbi_commit_edit;
|
||||
qof_be->rollback = gnc_dbi_rollback_edit;
|
||||
|
||||
/* The SQL/DBI backend doesn't need to be synced until it is
|
||||
* configured for multiuser access. */
|
||||
qof_be->sync = gnc_dbi_safe_sync_all;
|
||||
qof_be->safe_sync = gnc_dbi_safe_sync_all;
|
||||
/* CoA Export function not implemented for the SQL backend. */
|
||||
qof_be->export_fn = nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks to see whether the file is an sqlite file or not
|
||||
@ -1254,8 +1155,8 @@ dbi_library_test (dbi_conn conn)
|
||||
return retval;
|
||||
}
|
||||
|
||||
static bool
|
||||
conn_test_dbi_library(dbi_conn conn, QofBackend* qof_be)
|
||||
template <DbType Type> bool
|
||||
GncDbiBackend<Type>::conn_test_dbi_library(dbi_conn conn)
|
||||
{
|
||||
auto result = dbi_library_test (conn);
|
||||
switch (result)
|
||||
@ -1264,15 +1165,13 @@ conn_test_dbi_library(dbi_conn conn, QofBackend* qof_be)
|
||||
break;
|
||||
|
||||
case GNC_DBI_FAIL_SETUP:
|
||||
qof_backend_set_error (qof_be, ERR_SQL_DBI_UNTESTABLE);
|
||||
qof_backend_set_message (qof_be,
|
||||
"DBI library large number test incomplete");
|
||||
set_error(ERR_SQL_DBI_UNTESTABLE);
|
||||
set_message ("DBI library large number test incomplete");
|
||||
break;
|
||||
|
||||
case GNC_DBI_FAIL_TEST:
|
||||
qof_backend_set_error (qof_be, ERR_SQL_BAD_DBI);
|
||||
qof_backend_set_message (qof_be,
|
||||
"DBI library fails large number test");
|
||||
set_error (ERR_SQL_BAD_DBI);
|
||||
set_message ("DBI library fails large number test");
|
||||
break;
|
||||
}
|
||||
return result == GNC_DBI_PASS;
|
||||
|
@ -84,14 +84,22 @@ enum class DbType
|
||||
/**
|
||||
* Implementations of GncSqlBackend.
|
||||
*/
|
||||
struct UriStrings;
|
||||
|
||||
template <DbType Type>
|
||||
class GncDbiBackend : public GncSqlBackend
|
||||
{
|
||||
public:
|
||||
GncDbiBackend(GncSqlConnection *conn, QofBook* book) :
|
||||
GncSqlBackend(conn, book), m_exists{false} {}
|
||||
~GncDbiBackend();
|
||||
void session_begin(QofSession*, const char*, bool, bool, bool) override;
|
||||
void session_end() override;
|
||||
void load(QofBook*, QofBackendLoadType) override;
|
||||
void safe_sync(QofBook*) override;
|
||||
bool connected() const noexcept { return m_conn != nullptr; }
|
||||
/** FIXME: Just a pass-through to m_conn: */
|
||||
void set_error(int error, unsigned int repeat, bool retry) noexcept
|
||||
void set_dbi_error(int error, unsigned int repeat, bool retry) noexcept
|
||||
{
|
||||
m_conn->set_error(error, repeat, retry);
|
||||
}
|
||||
@ -102,15 +110,14 @@ public:
|
||||
/*-----*/
|
||||
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:
|
||||
dbi_conn conn_setup(PairVec& options, UriStrings& uri);
|
||||
bool conn_test_dbi_library(dbi_conn conn);
|
||||
bool set_standard_connection_options(dbi_conn conn, const UriStrings& uri);
|
||||
bool create_database(dbi_conn conn, const char* db);
|
||||
bool m_exists; // Does the database exist?
|
||||
};
|
||||
|
||||
|
||||
void gnc_dbi_safe_sync_all (QofBackend* qbe, QofBook* book);
|
||||
|
||||
/* external access required for tests */
|
||||
std::string adjust_sql_options_string(const std::string&);
|
||||
|
||||
|
@ -124,7 +124,7 @@ GncDbiSqlConnection::lock_database (bool ignore_lock)
|
||||
std::string err{"SQL Backend failed to obtain a transaction: "};
|
||||
err += errstr;
|
||||
qof_backend_set_error (m_qbe, ERR_BACKEND_SERVER_ERR);
|
||||
qof_backend_set_message (m_qbe, err.c_str());
|
||||
m_qbe->set_message (err.c_str());
|
||||
return false;
|
||||
}
|
||||
dbi_result_free(result);
|
||||
@ -147,7 +147,7 @@ GncDbiSqlConnection::lock_database (bool ignore_lock)
|
||||
if (!result)
|
||||
{
|
||||
qof_backend_set_error (m_qbe, ERR_BACKEND_SERVER_ERR);
|
||||
qof_backend_set_message (m_qbe, "Failed to delete lock record");
|
||||
m_qbe->set_message("Failed to delete lock record");
|
||||
result = dbi_conn_query (m_conn, "ROLLBACK");
|
||||
if (result)
|
||||
dbi_result_free (result);
|
||||
@ -165,7 +165,7 @@ GncDbiSqlConnection::lock_database (bool ignore_lock)
|
||||
if (!result)
|
||||
{
|
||||
qof_backend_set_error (m_qbe, ERR_BACKEND_SERVER_ERR);
|
||||
qof_backend_set_message (m_qbe, "Failed to create lock record");
|
||||
m_qbe->set_message("Failed to create lock record");
|
||||
result = dbi_conn_query (m_conn, "ROLLBACK");
|
||||
if (result)
|
||||
dbi_result_free (result);
|
||||
@ -179,7 +179,7 @@ GncDbiSqlConnection::lock_database (bool ignore_lock)
|
||||
qof_backend_set_error(m_qbe, ERR_BACKEND_SERVER_ERR);
|
||||
std::string err{"Failed to commit transaction: "};
|
||||
err += errstr;
|
||||
qof_backend_set_message(m_qbe, err.c_str());
|
||||
m_qbe->set_message(err.c_str());
|
||||
return false;
|
||||
}
|
||||
dbi_result_free (result);
|
||||
@ -189,8 +189,6 @@ GncDbiSqlConnection::lock_database (bool ignore_lock)
|
||||
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);
|
||||
|
||||
@ -225,7 +223,7 @@ GncDbiSqlConnection::unlock_database ()
|
||||
if (!result)
|
||||
{
|
||||
PERR ("Failed to delete the lock entry");
|
||||
qof_backend_set_error (m_qbe, ERR_BACKEND_SERVER_ERR);
|
||||
m_qbe->set_error (ERR_BACKEND_SERVER_ERR);
|
||||
result = dbi_conn_query (m_conn, "ROLLBACK");
|
||||
if (result)
|
||||
{
|
||||
@ -262,7 +260,7 @@ GncDbiSqlConnection::unlock_database ()
|
||||
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);
|
||||
m_qbe->set_error (ERR_BACKEND_SERVER_ERR);
|
||||
}
|
||||
|
||||
GncDbiSqlConnection::~GncDbiSqlConnection()
|
||||
|
@ -80,10 +80,8 @@ static EntryVec version_table
|
||||
};
|
||||
|
||||
GncSqlBackend::GncSqlBackend(GncSqlConnection *conn, QofBook* book) :
|
||||
qof_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}
|
||||
QofBackend {}, m_conn{conn}, m_book{book}, m_loading{false},
|
||||
m_in_query{false}, m_is_pristine_db{false}
|
||||
{
|
||||
if (conn != nullptr)
|
||||
connect (conn);
|
||||
@ -188,15 +186,15 @@ GncSqlBackend::add_columns_to_table(const std::string& table_name,
|
||||
void
|
||||
GncSqlBackend::update_progress() const noexcept
|
||||
{
|
||||
if (qof_be.percentage != nullptr)
|
||||
(qof_be.percentage) (nullptr, 101.0);
|
||||
if (m_percentage != nullptr)
|
||||
(m_percentage) (nullptr, 101.0);
|
||||
}
|
||||
|
||||
void
|
||||
GncSqlBackend::finish_progress() const noexcept
|
||||
{
|
||||
if (qof_be.percentage != nullptr)
|
||||
(qof_be.percentage) (nullptr, -1.0);
|
||||
if (m_percentage != nullptr)
|
||||
(m_percentage) (nullptr, -1.0);
|
||||
}
|
||||
|
||||
void
|
||||
@ -441,7 +439,7 @@ GncSqlBackend::write_schedXactions()
|
||||
#pragma GCC diagnostic warning "-Wformat-nonliteral"
|
||||
|
||||
void
|
||||
GncSqlBackend::sync_all(QofBook* book)
|
||||
GncSqlBackend::sync(QofBook* book)
|
||||
{
|
||||
g_return_if_fail (book != NULL);
|
||||
|
||||
@ -500,8 +498,7 @@ GncSqlBackend::sync_all(QofBook* book)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!qof_backend_check_error (&qof_be))
|
||||
qof_backend_set_error (&qof_be, ERR_BACKEND_SERVER_ERR);
|
||||
set_error (ERR_BACKEND_SERVER_ERR);
|
||||
is_ok = m_conn->rollback_transaction ();
|
||||
}
|
||||
finish_progress();
|
||||
@ -512,7 +509,7 @@ GncSqlBackend::sync_all(QofBook* book)
|
||||
/* Routines to deal with the creation of multiple books. */
|
||||
|
||||
void
|
||||
GncSqlBackend::begin_edit (QofInstance* inst)
|
||||
GncSqlBackend::begin(QofInstance* inst)
|
||||
{
|
||||
g_return_if_fail (inst != NULL);
|
||||
|
||||
@ -521,7 +518,7 @@ GncSqlBackend::begin_edit (QofInstance* inst)
|
||||
}
|
||||
|
||||
void
|
||||
GncSqlBackend::rollback_edit(QofInstance* inst)
|
||||
GncSqlBackend::rollback(QofInstance* inst)
|
||||
{
|
||||
g_return_if_fail (inst != NULL);
|
||||
|
||||
@ -546,7 +543,7 @@ GncSqlBackend::get_object_backend(const std::string& type) const noexcept
|
||||
* type and call its commit handler
|
||||
*/
|
||||
void
|
||||
GncSqlBackend::commit_edit (QofInstance* inst)
|
||||
GncSqlBackend::commit (QofInstance* inst)
|
||||
{
|
||||
sql_backend be_data;
|
||||
gboolean is_dirty;
|
||||
@ -557,7 +554,7 @@ GncSqlBackend::commit_edit (QofInstance* inst)
|
||||
|
||||
if (qof_book_is_readonly(m_book))
|
||||
{
|
||||
qof_backend_set_error (&qof_be, ERR_BACKEND_READONLY);
|
||||
set_error (ERR_BACKEND_READONLY);
|
||||
(void)m_conn->rollback_transaction ();
|
||||
return;
|
||||
}
|
||||
|
@ -62,11 +62,41 @@ typedef enum
|
||||
*
|
||||
* Main SQL backend structure.
|
||||
*/
|
||||
class GncSqlBackend
|
||||
class GncSqlBackend : public QofBackend
|
||||
{
|
||||
public:
|
||||
GncSqlBackend(GncSqlConnection *conn, QofBook* book);
|
||||
virtual ~GncSqlBackend() = default;
|
||||
/**
|
||||
* Load the contents of an SQL database into a book.
|
||||
*
|
||||
* @param book Book to be loaded
|
||||
*/
|
||||
void load(QofBook*, QofBackendLoadType) override;
|
||||
/**
|
||||
* Save the contents of a book to an SQL database.
|
||||
*
|
||||
* @param book Book to be saved
|
||||
*/
|
||||
void sync(QofBook*) override;
|
||||
/**
|
||||
* An object is about to be edited.
|
||||
*
|
||||
* @param inst Object being edited
|
||||
*/
|
||||
void begin(QofInstance*) override;
|
||||
/**
|
||||
* Object editting is complete and the object should be saved.
|
||||
*
|
||||
* @param inst Object being edited
|
||||
*/
|
||||
void commit(QofInstance*) override;
|
||||
/**
|
||||
* Object editing has been cancelled.
|
||||
*
|
||||
* @param inst Object being edited
|
||||
*/
|
||||
void rollback(QofInstance*) override;
|
||||
/** Connect the backend to a GncSqlConnection.
|
||||
* Sets up version info. Calling with nullptr clears the connection and
|
||||
* destroys the version info.
|
||||
@ -157,36 +187,6 @@ public:
|
||||
*/
|
||||
uint_t get_table_version(const std::string& table_name) const noexcept;
|
||||
bool set_table_version (const std::string& table_name, uint_t version) noexcept;
|
||||
/**
|
||||
* Load the contents of an SQL database into a book.
|
||||
*
|
||||
* @param book Book to be loaded
|
||||
*/
|
||||
void load(QofBook*, QofBackendLoadType);
|
||||
/**
|
||||
* Save the contents of a book to an SQL database.
|
||||
*
|
||||
* @param book Book to be saved
|
||||
*/
|
||||
void sync_all(QofBook*);
|
||||
/**
|
||||
* An object is about to be edited.
|
||||
*
|
||||
* @param inst Object being edited
|
||||
*/
|
||||
void begin_edit(QofInstance*);
|
||||
/**
|
||||
* Object editting is complete and the object should be saved.
|
||||
*
|
||||
* @param inst Object being edited
|
||||
*/
|
||||
void commit_edit(QofInstance*);
|
||||
/**
|
||||
* Object editing has been cancelled.
|
||||
*
|
||||
* @param inst Object being edited
|
||||
*/
|
||||
void rollback_edit(QofInstance*);
|
||||
/**
|
||||
* Register a commodity to be committed after loading is complete.
|
||||
*
|
||||
@ -241,7 +241,6 @@ public:
|
||||
void finish_progress() const noexcept;
|
||||
|
||||
protected:
|
||||
QofBackend qof_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 */
|
||||
|
@ -34,6 +34,17 @@ extern "C"
|
||||
|
||||
static const gchar* suitename = "/backend/sql/gnc-backend-sql";
|
||||
void test_suite_gnc_backend_sql (void);
|
||||
|
||||
class GncMockSqlBackend : public GncSqlBackend
|
||||
{
|
||||
public:
|
||||
GncMockSqlBackend(GncSqlConnection* conn, QofBook* book) :
|
||||
GncSqlBackend(conn, book) {}
|
||||
void session_begin(QofSession*, const char*, bool, bool, bool) override {}
|
||||
void session_end() override {}
|
||||
void safe_sync(QofBook* book) override { sync(book); }
|
||||
};
|
||||
|
||||
class GncMockSqlConnection;
|
||||
|
||||
class GncMockSqlResult : public GncSqlResult
|
||||
@ -265,7 +276,7 @@ test_gnc_sql_commit_edit (void)
|
||||
guint dirty_called = 0;
|
||||
GncMockSqlConnection conn;
|
||||
const char* msg1 =
|
||||
"[GncSqlBackend::commit_edit()] Unknown object type 'null'\n";
|
||||
"[GncSqlBackend::commit()] Unknown object type 'null'\n";
|
||||
GLogLevelFlags loglevel = static_cast<decltype (loglevel)>
|
||||
(G_LOG_LEVEL_CRITICAL | G_LOG_FLAG_FATAL);
|
||||
const char* logdomain = "gnc.backend.sql";
|
||||
@ -281,7 +292,8 @@ test_gnc_sql_commit_edit (void)
|
||||
|
||||
qof_object_initialize ();
|
||||
auto book = qof_book_new();
|
||||
GncSqlBackend sql_be (&conn, book);
|
||||
auto sql_be = new GncMockSqlBackend
|
||||
(&conn, book);
|
||||
inst = static_cast<decltype (inst)> (g_object_new (QOF_TYPE_INSTANCE, NULL));
|
||||
qof_instance_init_data (inst, QOF_ID_NULL, book);
|
||||
qof_book_set_dirty_cb (book, test_dirty_cb, &dirty_called);
|
||||
@ -291,7 +303,7 @@ test_gnc_sql_commit_edit (void)
|
||||
g_assert (qof_instance_get_dirty_flag (inst));
|
||||
g_assert (qof_book_session_not_saved (book));
|
||||
g_assert_cmpint (dirty_called, == , 1);
|
||||
sql_be.commit_edit (inst);
|
||||
sql_be->commit(inst);
|
||||
g_assert (!qof_instance_get_dirty_flag (inst));
|
||||
g_assert (!qof_book_session_not_saved (book));
|
||||
g_assert_cmpint (dirty_called, == , 0);
|
||||
@ -302,7 +314,7 @@ test_gnc_sql_commit_edit (void)
|
||||
g_assert (!qof_instance_get_dirty_flag (QOF_INSTANCE (book)));
|
||||
g_assert (qof_book_session_not_saved (book));
|
||||
g_assert_cmpint (dirty_called, == , 1);
|
||||
sql_be.commit_edit (QOF_INSTANCE (book));
|
||||
sql_be->commit(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);
|
||||
@ -313,7 +325,7 @@ test_gnc_sql_commit_edit (void)
|
||||
g_assert (qof_instance_get_dirty_flag (QOF_INSTANCE (book)));
|
||||
g_assert (qof_book_session_not_saved (book));
|
||||
g_assert_cmpint (dirty_called, == , 1);
|
||||
sql_be.commit_edit(QOF_INSTANCE (book));
|
||||
sql_be->commit(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);
|
||||
@ -322,6 +334,7 @@ test_gnc_sql_commit_edit (void)
|
||||
g_log_remove_handler (logdomain, hdlr1);
|
||||
g_object_unref (inst);
|
||||
g_object_unref (book);
|
||||
delete sql_be;
|
||||
}
|
||||
/* handle_and_term
|
||||
static void
|
||||
|
@ -108,42 +108,11 @@ struct QofXmlBackendProvider : public QofBackendProvider
|
||||
QofXmlBackendProvider(QofXmlBackendProvider&&) = delete;
|
||||
QofXmlBackendProvider operator=(QofXmlBackendProvider&&) = delete;
|
||||
~QofXmlBackendProvider () = default;
|
||||
QofBackend* create_backend(void);
|
||||
QofBackend* create_backend(void) { return new GncXmlBackend; }
|
||||
bool type_check(const char* type);
|
||||
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
xml_session_begin (QofBackend* qof_be, QofSession* session,
|
||||
const char* book_id, gboolean ignore_lock,
|
||||
gboolean create, gboolean force)
|
||||
{
|
||||
GncXmlBackend* xml_be = (GncXmlBackend*) qof_be;
|
||||
|
||||
ENTER (" ");
|
||||
xml_be->session_begin(session, book_id, ignore_lock, create, force);
|
||||
LEAVE (" ");
|
||||
return;
|
||||
}
|
||||
|
||||
/* ================================================================= */
|
||||
|
||||
static void
|
||||
xml_session_end (QofBackend* qof_be)
|
||||
{
|
||||
GncXmlBackend* xml_be = (GncXmlBackend*)qof_be;
|
||||
ENTER (" ");
|
||||
xml_be->session_end();
|
||||
LEAVE (" ");
|
||||
}
|
||||
|
||||
static void
|
||||
xml_destroy_backend (QofBackend* qof_be)
|
||||
{
|
||||
delete reinterpret_cast<GncXmlBackend*>(qof_be);
|
||||
}
|
||||
|
||||
bool
|
||||
QofXmlBackendProvider::type_check (const char *uri)
|
||||
{
|
||||
@ -201,86 +170,8 @@ det_exit:
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
xml_sync_all (QofBackend* qof_be, QofBook* book)
|
||||
{
|
||||
GncXmlBackend* xml_be = reinterpret_cast<decltype(xml_be)>(qof_be);
|
||||
xml_be->sync(book);
|
||||
ENTER ("book=%p, xml_be->m_book=%p", book, xml_be->get_book());
|
||||
|
||||
LEAVE ("book=%p", book);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
xml_begin_edit (QofBackend* qof_be, QofInstance* inst)
|
||||
{
|
||||
GncXmlBackend* xml_be = (GncXmlBackend*) qof_be;
|
||||
xml_be->begin(inst);
|
||||
}
|
||||
|
||||
static void
|
||||
xml_rollback_edit (QofBackend* qof_be, QofInstance* inst)
|
||||
{
|
||||
|
||||
GncXmlBackend* xml_be = (GncXmlBackend*) qof_be;
|
||||
xml_be->rollback(inst);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
|
||||
/* Load financial data from a file into the book, automatically
|
||||
detecting the format of the file, if possible. Return FALSE on
|
||||
error, and set the error parameter to indicate what went wrong if
|
||||
it's not NULL. This function does not manage file locks in any
|
||||
way. */
|
||||
|
||||
static void
|
||||
gnc_xml_be_load_from_file (QofBackend* qof_be, QofBook* book,
|
||||
QofBackendLoadType loadType)
|
||||
{
|
||||
GncXmlBackend* xml_be = (GncXmlBackend*) qof_be;
|
||||
xml_be->load(book, loadType);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
|
||||
|
||||
static void
|
||||
gnc_xml_be_write_accounts_to_file (QofBackend* qof_be, QofBook* book)
|
||||
{
|
||||
auto datafile = ((GncXmlBackend*)qof_be)->get_filename();
|
||||
gnc_book_write_accounts_to_xml_file_v2 (qof_be, book, datafile);
|
||||
}
|
||||
|
||||
/* ================================================================= */
|
||||
|
||||
QofBackend*
|
||||
QofXmlBackendProvider::create_backend(void)
|
||||
{
|
||||
|
||||
auto xml_be = new GncXmlBackend;
|
||||
auto qof_be = xml_be->get_qof_be();
|
||||
qof_be->session_begin = xml_session_begin;
|
||||
qof_be->session_end = xml_session_end;
|
||||
qof_be->destroy_backend = xml_destroy_backend;
|
||||
|
||||
qof_be->load = gnc_xml_be_load_from_file;
|
||||
|
||||
/* The file backend treats accounting periods transactionally. */
|
||||
qof_be->begin = xml_begin_edit;
|
||||
qof_be->commit = NULL;
|
||||
qof_be->rollback = xml_rollback_edit;
|
||||
|
||||
qof_be->sync = xml_sync_all;
|
||||
|
||||
qof_be->export_fn = gnc_xml_be_write_accounts_to_file;
|
||||
|
||||
return qof_be;
|
||||
}
|
||||
|
||||
static void
|
||||
business_core_xml_init (void)
|
||||
{
|
||||
|
@ -23,6 +23,7 @@ extern "C"
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
@ -48,21 +49,8 @@ extern "C"
|
||||
#define FILE_URI_PREFIX "file://"
|
||||
static QofLogModule log_module = GNC_MOD_BACKEND;
|
||||
|
||||
GncXmlBackend::GncXmlBackend()
|
||||
{
|
||||
memset(&qof_be, 0, sizeof(qof_be));
|
||||
qof_backend_init(&qof_be);
|
||||
}
|
||||
|
||||
GncXmlBackend::~GncXmlBackend()
|
||||
{
|
||||
/* Stop transaction logging */
|
||||
xaccLogSetBaseName (NULL);
|
||||
qof_backend_destroy (&qof_be);
|
||||
}
|
||||
|
||||
bool
|
||||
check_path (const char* fullpath, QofBackend* qof_be, bool create)
|
||||
GncXmlBackend::check_path (const char* fullpath, bool create)
|
||||
{
|
||||
struct stat statbuf;
|
||||
char* dirname = g_path_get_dirname (fullpath);
|
||||
@ -78,9 +66,9 @@ check_path (const char* fullpath, QofBackend* qof_be, bool create)
|
||||
{
|
||||
/* Error on stat or if it isn't a directory means we
|
||||
cannot find this filename */
|
||||
qof_backend_set_error (qof_be, ERR_FILEIO_FILE_NOT_FOUND);
|
||||
qof_backend_set_message (qof_be, "Couldn't find directory for %s",
|
||||
fullpath);
|
||||
set_error(ERR_FILEIO_FILE_NOT_FOUND);
|
||||
std::string msg {"Couldn't find directory for "};
|
||||
set_message(msg + fullpath);
|
||||
PWARN ("Couldn't find directory for %s", fullpath);
|
||||
g_free(dirname);
|
||||
return false;
|
||||
@ -91,8 +79,9 @@ check_path (const char* fullpath, QofBackend* qof_be, bool create)
|
||||
if ((rc != 0) && (!create))
|
||||
{
|
||||
/* Error on stat means the file doesn't exist */
|
||||
qof_backend_set_error (qof_be, ERR_FILEIO_FILE_NOT_FOUND);
|
||||
qof_backend_set_message (qof_be, "Couldn't find %s", fullpath);
|
||||
set_error(ERR_FILEIO_FILE_NOT_FOUND);
|
||||
std::string msg {"Couldn't find "};
|
||||
set_message(msg + fullpath);
|
||||
PWARN ("Couldn't find %s", fullpath);
|
||||
g_free(dirname);
|
||||
return false;
|
||||
@ -105,8 +94,10 @@ check_path (const char* fullpath, QofBackend* qof_be, bool create)
|
||||
#endif
|
||||
)
|
||||
{
|
||||
qof_backend_set_error (qof_be, ERR_FILEIO_UNKNOWN_FILE_TYPE);
|
||||
qof_backend_set_message (qof_be, "Path %s is a directory", fullpath);
|
||||
set_error(ERR_FILEIO_UNKNOWN_FILE_TYPE);
|
||||
std::string msg {"Path "};
|
||||
msg += fullpath;
|
||||
set_message(msg + " is a directory");
|
||||
PWARN ("Path %s is a directory", fullpath);
|
||||
g_free(dirname);
|
||||
return false;
|
||||
@ -123,20 +114,19 @@ GncXmlBackend::session_begin(QofSession* session, const char* book_id,
|
||||
|
||||
if (m_fullpath.empty())
|
||||
{
|
||||
qof_backend_set_error (&qof_be, ERR_FILEIO_FILE_NOT_FOUND);
|
||||
qof_backend_set_message (&qof_be, "No path specified");
|
||||
set_error(ERR_FILEIO_FILE_NOT_FOUND);
|
||||
set_message("No path specified");
|
||||
return;
|
||||
}
|
||||
if (create && !force && save_may_clobber_data())
|
||||
{
|
||||
qof_backend_set_error (&qof_be, ERR_BACKEND_STORE_EXISTS);
|
||||
set_error(ERR_BACKEND_STORE_EXISTS);
|
||||
PWARN ("Might clobber, no force");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!check_path(m_fullpath.c_str(), &qof_be, create))
|
||||
if (!check_path(m_fullpath.c_str(), create))
|
||||
return;
|
||||
qof_be.fullpath = const_cast<char*>(m_fullpath.c_str());
|
||||
m_dirname = g_path_get_dirname (m_fullpath.c_str());
|
||||
|
||||
|
||||
@ -159,7 +149,7 @@ GncXmlBackend::session_begin(QofSession* session, const char* book_id,
|
||||
|
||||
if (force)
|
||||
{
|
||||
QofBackendError berror = qof_backend_get_error (&qof_be);
|
||||
QofBackendError berror = get_error();
|
||||
if (berror == ERR_BACKEND_LOCKED || berror == ERR_BACKEND_READONLY)
|
||||
{
|
||||
// Even though we couldn't get the lock, we were told to force
|
||||
@ -169,7 +159,7 @@ GncXmlBackend::session_begin(QofSession* session, const char* book_id,
|
||||
else
|
||||
{
|
||||
// Unknown error. Push it again on the error stack.
|
||||
qof_backend_set_error (&qof_be, berror);
|
||||
set_error(berror);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -181,7 +171,7 @@ GncXmlBackend::session_end()
|
||||
{
|
||||
if (m_book && qof_book_is_readonly (m_book))
|
||||
{
|
||||
qof_backend_set_error (&qof_be, ERR_BACKEND_READONLY);
|
||||
set_error(ERR_BACKEND_READONLY);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -307,7 +297,7 @@ GncXmlBackend::load(QofBook* book, QofBackendLoadType loadType)
|
||||
|
||||
if (error != ERR_BACKEND_NO_ERR)
|
||||
{
|
||||
qof_backend_set_error (&qof_be, error);
|
||||
set_error(error);
|
||||
}
|
||||
|
||||
/* We just got done loading, it can't possibly be dirty !! */
|
||||
@ -329,7 +319,7 @@ GncXmlBackend::sync(QofBook* book)
|
||||
if (qof_book_is_readonly (m_book))
|
||||
{
|
||||
/* Are we read-only? Don't continue in this case. */
|
||||
qof_backend_set_error (&qof_be, ERR_BACKEND_READONLY);
|
||||
set_error(ERR_BACKEND_READONLY);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -348,6 +338,20 @@ GncXmlBackend::save_may_clobber_data()
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
GncXmlBackend::export_coa(QofBook* book)
|
||||
{
|
||||
auto out = fopen(m_fullpath.c_str(), "w");
|
||||
if (out == NULL)
|
||||
{
|
||||
set_error(ERR_FILEIO_WRITE_ERROR);
|
||||
set_message(strerror(errno));
|
||||
return;
|
||||
}
|
||||
gnc_book_write_accounts_to_xml_filehandle_v2(this, book, out);
|
||||
fclose(out);
|
||||
}
|
||||
|
||||
bool
|
||||
GncXmlBackend::write_to_file (bool make_backup)
|
||||
{
|
||||
@ -358,7 +362,7 @@ GncXmlBackend::write_to_file (bool make_backup)
|
||||
if (m_book && qof_book_is_readonly (m_book))
|
||||
{
|
||||
/* Are we read-only? Don't continue in this case. */
|
||||
qof_backend_set_error (&qof_be, ERR_BACKEND_READONLY);
|
||||
set_error(ERR_BACKEND_READONLY);
|
||||
LEAVE ("");
|
||||
return FALSE;
|
||||
}
|
||||
@ -374,8 +378,8 @@ GncXmlBackend::write_to_file (bool make_backup)
|
||||
|
||||
if (!mktemp (tmp_name))
|
||||
{
|
||||
qof_backend_set_error (&qof_be, ERR_BACKEND_MISC);
|
||||
qof_backend_set_message (&qof_be, "Failed to make temp file");
|
||||
set_error(ERR_BACKEND_MISC);
|
||||
set_message("Failed to make temp file");
|
||||
LEAVE ("");
|
||||
return FALSE;
|
||||
}
|
||||
@ -403,8 +407,8 @@ GncXmlBackend::write_to_file (bool make_backup)
|
||||
/* Use the permissions from the original data file */
|
||||
if (g_chmod (tmp_name, statbuf.st_mode) != 0)
|
||||
{
|
||||
/* qof_backend_set_error(&qof_be, ERR_BACKEND_PERM); */
|
||||
/* qof_backend_set_message(&qof_be, "Failed to chmod filename %s", tmp_name ); */
|
||||
/* set_error(ERR_BACKEND_PERM); */
|
||||
/* set_message("Failed to chmod filename %s", tmp_name ); */
|
||||
/* Even if the chmod did fail, the save
|
||||
nevertheless completed successfully. It is
|
||||
therefore wrong to signal the ERR_BACKEND_PERM
|
||||
@ -423,8 +427,8 @@ GncXmlBackend::write_to_file (bool make_backup)
|
||||
that. */
|
||||
if (chown (tmp_name, -1, statbuf.st_gid) != 0)
|
||||
{
|
||||
/* qof_backend_set_error(&qof_be, ERR_BACKEND_PERM); */
|
||||
/* qof_backend_set_message(&qof_be, "Failed to chown filename %s", tmp_name ); */
|
||||
/* set_error(ERR_BACKEND_PERM); */
|
||||
/* set_message("Failed to chown filename %s", tmp_name ); */
|
||||
/* A failed chown doesn't mean that the saving itself
|
||||
failed. So don't abort with an error here! */
|
||||
PWARN ("unable to chown filename %s: %s",
|
||||
@ -439,7 +443,7 @@ GncXmlBackend::write_to_file (bool make_backup)
|
||||
}
|
||||
if (g_unlink (m_fullpath.c_str()) != 0 && errno != ENOENT)
|
||||
{
|
||||
qof_backend_set_error (&qof_be, ERR_BACKEND_READONLY);
|
||||
set_error(ERR_BACKEND_READONLY);
|
||||
PWARN ("unable to unlink filename %s: %s",
|
||||
m_fullpath.empty() ? "(null)" : m_fullpath.c_str(),
|
||||
g_strerror (errno) ? g_strerror (errno) : "");
|
||||
@ -449,16 +453,16 @@ GncXmlBackend::write_to_file (bool make_backup)
|
||||
}
|
||||
if (!link_or_make_backup (tmp_name, m_fullpath))
|
||||
{
|
||||
qof_backend_set_error (&qof_be, ERR_FILEIO_BACKUP_ERROR);
|
||||
qof_backend_set_message (&qof_be, "Failed to make backup file %s",
|
||||
m_fullpath.empty() ? "NULL" : m_fullpath.c_str());
|
||||
set_error(ERR_FILEIO_BACKUP_ERROR);
|
||||
std::string msg{"Failed to make backup file "};
|
||||
set_message(msg + (m_fullpath.empty() ? "NULL" : m_fullpath));
|
||||
g_free (tmp_name);
|
||||
LEAVE ("");
|
||||
return FALSE;
|
||||
}
|
||||
if (g_unlink (tmp_name) != 0)
|
||||
{
|
||||
qof_backend_set_error (&qof_be, ERR_BACKEND_PERM);
|
||||
set_error(ERR_BACKEND_PERM);
|
||||
PWARN ("unable to unlink temp filename %s: %s",
|
||||
tmp_name ? tmp_name : "(null)",
|
||||
g_strerror (errno) ? g_strerror (errno) : "");
|
||||
@ -492,7 +496,7 @@ GncXmlBackend::write_to_file (bool make_backup)
|
||||
be_err = ERR_BACKEND_MISC;
|
||||
break;
|
||||
}
|
||||
qof_backend_set_error (&qof_be, be_err);
|
||||
set_error(be_err);
|
||||
PWARN ("unable to unlink temp_filename %s: %s",
|
||||
tmp_name ? tmp_name : "(null)",
|
||||
g_strerror (errno) ? g_strerror (errno) : "");
|
||||
@ -501,9 +505,9 @@ GncXmlBackend::write_to_file (bool make_backup)
|
||||
else
|
||||
{
|
||||
/* Use a generic write error code */
|
||||
qof_backend_set_error (&qof_be, ERR_FILEIO_WRITE_ERROR);
|
||||
qof_backend_set_message (&qof_be, "Unable to write to temp file %s",
|
||||
tmp_name ? tmp_name : "NULL");
|
||||
set_error(ERR_FILEIO_WRITE_ERROR);
|
||||
std::string msg{"Unable to write to temp file "};
|
||||
set_message(msg + (tmp_name ? tmp_name : "NULL"));
|
||||
}
|
||||
g_free (tmp_name);
|
||||
LEAVE ("");
|
||||
@ -519,8 +523,8 @@ copy_file (const std::string& orig, const std::string& bkup)
|
||||
constexpr size_t buf_size = 1024;
|
||||
char buf[buf_size];
|
||||
int flags = 0;
|
||||
ssize_t count_write;
|
||||
ssize_t count_read;
|
||||
ssize_t count_write = 0;
|
||||
ssize_t count_read = 0;
|
||||
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
@ -542,7 +546,7 @@ copy_file (const std::string& orig, const std::string& bkup)
|
||||
|
||||
do
|
||||
{
|
||||
auto count_read = read (orig_fd, buf, buf_size);
|
||||
count_read = read (orig_fd, buf, buf_size);
|
||||
if (count_read == -1 && errno != EINTR)
|
||||
{
|
||||
close (orig_fd);
|
||||
@ -602,7 +606,7 @@ GncXmlBackend::link_or_make_backup (const std::string& orig,
|
||||
|
||||
if (!copy_success)
|
||||
{
|
||||
qof_backend_set_error (&qof_be, ERR_FILEIO_BACKUP_ERROR);
|
||||
set_error(ERR_FILEIO_BACKUP_ERROR);
|
||||
PWARN ("unable to make file backup from %s to %s: %s",
|
||||
orig.c_str(), bkup.c_str(), g_strerror (errno) ? g_strerror (errno) : "");
|
||||
return false;
|
||||
@ -626,7 +630,7 @@ GncXmlBackend::get_file_lock ()
|
||||
if (!rc)
|
||||
{
|
||||
/* oops .. file is locked by another user .. */
|
||||
qof_backend_set_error (&qof_be, ERR_BACKEND_LOCKED);
|
||||
set_error(ERR_BACKEND_LOCKED);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -648,7 +652,7 @@ GncXmlBackend::get_file_lock ()
|
||||
be_err = ERR_BACKEND_LOCKED;
|
||||
break;
|
||||
}
|
||||
qof_backend_set_error (&qof_be, be_err);
|
||||
set_error(be_err);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -693,7 +697,7 @@ GncXmlBackend::get_file_lock ()
|
||||
}
|
||||
|
||||
/* Otherwise, something else is wrong. */
|
||||
qof_backend_set_error (&qof_be, ERR_BACKEND_LOCKED);
|
||||
set_error(ERR_BACKEND_LOCKED);
|
||||
g_unlink (linkfile.str().c_str());
|
||||
close (m_lockfd);
|
||||
g_unlink (m_lockfile.c_str());
|
||||
@ -704,9 +708,9 @@ GncXmlBackend::get_file_lock ()
|
||||
if (rc)
|
||||
{
|
||||
/* oops .. stat failed! This can't happen! */
|
||||
qof_backend_set_error (&qof_be, ERR_BACKEND_LOCKED);
|
||||
qof_backend_set_message (&qof_be, "Failed to stat lockfile %s",
|
||||
m_lockfile.c_str());
|
||||
set_error(ERR_BACKEND_LOCKED);
|
||||
std::string msg{"Failed to stat lockfile "};
|
||||
set_message(msg + m_lockfile);
|
||||
g_unlink (linkfile.str().c_str());
|
||||
close (m_lockfd);
|
||||
g_unlink (m_lockfile.c_str());
|
||||
@ -715,7 +719,7 @@ GncXmlBackend::get_file_lock ()
|
||||
|
||||
if (statbuf.st_nlink != 2)
|
||||
{
|
||||
qof_backend_set_error (&qof_be, ERR_BACKEND_LOCKED);
|
||||
set_error(ERR_BACKEND_LOCKED);
|
||||
g_unlink (linkfile.str().c_str());
|
||||
close (m_lockfd);
|
||||
g_unlink (m_lockfile.c_str());
|
||||
|
@ -26,28 +26,26 @@ extern "C"
|
||||
#include <string>
|
||||
#include <qof-backend.hpp>
|
||||
|
||||
class GncXmlBackend
|
||||
class GncXmlBackend : public QofBackend
|
||||
{
|
||||
public:
|
||||
GncXmlBackend();
|
||||
GncXmlBackend() = default;
|
||||
GncXmlBackend(const GncXmlBackend&) = delete;
|
||||
GncXmlBackend operator=(const GncXmlBackend&) = delete;
|
||||
GncXmlBackend(const GncXmlBackend&&) = delete;
|
||||
GncXmlBackend operator=(const GncXmlBackend&&) = delete;
|
||||
~GncXmlBackend();
|
||||
~GncXmlBackend() = default;
|
||||
void session_begin(QofSession* session, const char* book_id,
|
||||
bool ignore_lock, bool create, bool force);
|
||||
void session_end();
|
||||
void load(QofBook* book, QofBackendLoadType loadType);
|
||||
bool ignore_lock, bool create, bool force) override;
|
||||
void session_end() override;
|
||||
void load(QofBook* book, QofBackendLoadType loadType) override;
|
||||
/* The XML backend isn't able to do anything with individual instances. */
|
||||
void begin(QofInstance* inst) {}
|
||||
void commit(QofInstance* inst) {}
|
||||
void rollback(QofInstance* inst) {}
|
||||
void sync(QofBook* book);
|
||||
QofBackend* get_qof_be() { return &qof_be; }
|
||||
void export_coa(QofBook*) override;
|
||||
void sync(QofBook* book) override;
|
||||
void safe_sync(QofBook* book) override { sync(book); } // XML sync is inherently safe.
|
||||
const char * get_filename() { return m_fullpath.c_str(); }
|
||||
QofBook* get_book() { return m_book; }
|
||||
|
||||
|
||||
private:
|
||||
bool save_may_clobber_data();
|
||||
bool get_file_lock();
|
||||
@ -56,10 +54,9 @@ private:
|
||||
bool write_to_file(bool make_backup);
|
||||
void remove_old_files();
|
||||
void write_accounts(QofBook* book);
|
||||
QofBackend qof_be;
|
||||
bool check_path(const char* fullpath, bool create);
|
||||
|
||||
std::string m_dirname;
|
||||
std::string m_fullpath; /* Fully qualified path to book */
|
||||
std::string m_lockfile;
|
||||
std::string m_linkfile;
|
||||
int m_lockfd;
|
||||
|
@ -698,7 +698,6 @@ qof_session_load_from_xml_file_v2_full (
|
||||
QofBookFileType type)
|
||||
{
|
||||
Account* root;
|
||||
QofBackend* qof_be = reinterpret_cast<decltype(qof_be)>(xml_be);
|
||||
sixtp_gdv2* gd;
|
||||
sixtp* top_parser;
|
||||
sixtp* main_parser;
|
||||
@ -707,7 +706,8 @@ qof_session_load_from_xml_file_v2_full (
|
||||
gboolean retval;
|
||||
char* v2type = NULL;
|
||||
|
||||
gd = gnc_sixtp_gdv2_new (book, FALSE, file_rw_feedback, qof_be->percentage);
|
||||
gd = gnc_sixtp_gdv2_new (book, FALSE, file_rw_feedback,
|
||||
xml_be->get_percentage());
|
||||
|
||||
top_parser = sixtp_new ();
|
||||
main_parser = sixtp_new ();
|
||||
@ -1340,7 +1340,8 @@ gnc_book_write_to_xml_filehandle_v2 (QofBook* book, FILE* out)
|
||||
return FALSE;
|
||||
|
||||
qof_be = qof_book_get_backend (book);
|
||||
gd = gnc_sixtp_gdv2_new (book, FALSE, file_rw_feedback, qof_be->percentage);
|
||||
gd = gnc_sixtp_gdv2_new (book, FALSE, file_rw_feedback,
|
||||
qof_be->get_percentage());
|
||||
gd->counter.commodities_total =
|
||||
gnc_commodity_table_get_size (gnc_commodity_table_get_table (book));
|
||||
gd->counter.accounts_total = 1 +
|
||||
@ -1386,7 +1387,8 @@ gnc_book_write_accounts_to_xml_filehandle_v2 (QofBackend* qof_be, QofBook* book,
|
||||
|| !write_counts (out, "commodity", ncom, "account", nacc, NULL))
|
||||
return FALSE;
|
||||
|
||||
gd = gnc_sixtp_gdv2_new (book, TRUE, file_rw_feedback, qof_be->percentage);
|
||||
gd = gnc_sixtp_gdv2_new (book, TRUE, file_rw_feedback,
|
||||
qof_be->get_percentage());
|
||||
gd->counter.commodities_total = ncom;
|
||||
gd->counter.accounts_total = nacc;
|
||||
|
||||
@ -1651,10 +1653,8 @@ gnc_book_write_to_xml_file_v2 (
|
||||
* postgress or anything else.
|
||||
*/
|
||||
gboolean
|
||||
gnc_book_write_accounts_to_xml_file_v2 (
|
||||
QofBackend* qof_be,
|
||||
QofBook* book,
|
||||
const char* filename)
|
||||
gnc_book_write_accounts_to_xml_file_v2 (QofBackend* qof_be, QofBook* book,
|
||||
const char* filename)
|
||||
{
|
||||
FILE* out;
|
||||
gboolean success = TRUE;
|
||||
@ -1671,7 +1671,7 @@ gnc_book_write_accounts_to_xml_file_v2 (
|
||||
if (out && fclose (out))
|
||||
success = FALSE;
|
||||
|
||||
if (!success && !qof_backend_check_error (qof_be))
|
||||
if (!success && !qof_be->check_error())
|
||||
{
|
||||
|
||||
/* Use a generic write error code */
|
||||
|
@ -32,12 +32,12 @@ extern "C"
|
||||
|
||||
#include "test-stuff.h"
|
||||
#include "test-engine-stuff.h"
|
||||
#include "test-file-stuff.h"
|
||||
#include "cashobjects.h"
|
||||
#include "gnc-engine.h"
|
||||
#include "gnc-commodity.h"
|
||||
}
|
||||
|
||||
#include "test-file-stuff.h"
|
||||
#include "gnc-xml-helper.h"
|
||||
#include "sixtp.h"
|
||||
#include "sixtp-parsers.h"
|
||||
|
@ -32,11 +32,12 @@ extern "C"
|
||||
|
||||
#include "test-stuff.h"
|
||||
#include "test-engine-stuff.h"
|
||||
#include "test-file-stuff.h"
|
||||
|
||||
#include "gnc-engine.h"
|
||||
#include "TransLog.h"
|
||||
}
|
||||
|
||||
#include "test-file-stuff.h"
|
||||
#include "io-gncxml-v2.h"
|
||||
|
||||
const char* possible_envs[] =
|
||||
|
@ -26,9 +26,9 @@ extern "C"
|
||||
|
||||
#include "test-stuff.h"
|
||||
#include "test-engine-stuff.h"
|
||||
#include "test-file-stuff.h"
|
||||
}
|
||||
|
||||
#include "test-file-stuff.h"
|
||||
#include "sixtp-dom-parsers.h"
|
||||
#include "sixtp-dom-generators.h"
|
||||
|
||||
|
@ -52,7 +52,7 @@ extern "C"
|
||||
#include "../sixtp-parsers.h"
|
||||
#include "../sixtp-dom-parsers.h"
|
||||
#include "../io-gncxml-gen.h"
|
||||
#include <test-file-stuff.h>
|
||||
#include "test-file-stuff.h"
|
||||
|
||||
static QofBook* book;
|
||||
|
||||
|
@ -86,47 +86,49 @@ typedef struct
|
||||
Account *gains_acc;
|
||||
} GainsFixture;
|
||||
|
||||
typedef struct
|
||||
class MockBackend : public QofBackend
|
||||
{
|
||||
QofBackend be;
|
||||
gchar last_call[12];
|
||||
QofBackendError result_err;
|
||||
} MockBackend;
|
||||
|
||||
static void
|
||||
mock_backend_set_error (MockBackend *mbe, QofBackendError err)
|
||||
{
|
||||
mbe->result_err = err;
|
||||
}
|
||||
|
||||
static void
|
||||
mock_backend_rollback (QofBackend *be, QofInstance *foo)
|
||||
{
|
||||
MockBackend *mbe = (MockBackend *)be;
|
||||
g_strlcpy (mbe->last_call, "rollback", sizeof (mbe->last_call));
|
||||
mbe->be.last_err = mbe->result_err;
|
||||
}
|
||||
|
||||
static MockBackend*
|
||||
mock_backend_new (void)
|
||||
{
|
||||
MockBackend *mbe = g_new0 (MockBackend, 1);
|
||||
mbe->be.rollback = mock_backend_rollback;
|
||||
memset (mbe->last_call, 0, sizeof (mbe->last_call));
|
||||
return mbe;
|
||||
}
|
||||
public:
|
||||
MockBackend() : QofBackend(), m_last_call{"Constructor"},
|
||||
m_result_err{ERR_BACKEND_NO_ERR} {}
|
||||
void session_begin(QofSession*, const char*, bool, bool, bool) override {
|
||||
m_last_call = "session_begin";
|
||||
}
|
||||
void session_end() override {
|
||||
m_last_call = "session_end";
|
||||
}
|
||||
void load(QofBook*, QofBackendLoadType) override {
|
||||
m_last_call = "load";
|
||||
}
|
||||
void sync(QofBook*) override {
|
||||
m_last_call = "sync";
|
||||
}
|
||||
void safe_sync(QofBook*) override {
|
||||
m_last_call = "safe_sync";
|
||||
}
|
||||
void rollback(QofInstance*) override {
|
||||
set_error(m_result_err);
|
||||
m_last_call = "rollback";
|
||||
}
|
||||
void inject_error(QofBackendError err) {
|
||||
m_result_err = err;
|
||||
}
|
||||
std::string m_last_call;
|
||||
private:
|
||||
QofBackendError m_result_err;
|
||||
};
|
||||
|
||||
static void
|
||||
setup (Fixture *fixture, gconstpointer pData)
|
||||
{
|
||||
QofBook *book = qof_book_new ();
|
||||
MockBackend *mbe = mock_backend_new ();
|
||||
MockBackend *mbe = new MockBackend;
|
||||
Transaction *txn;
|
||||
Timespec entered = gnc_dmy2timespec (20, 4, 2012);
|
||||
Timespec posted = gnc_dmy2timespec (21, 4, 2012);
|
||||
auto frame = new KvpFrame ();
|
||||
|
||||
qof_book_set_backend (book, (QofBackend*)mbe);
|
||||
qof_book_set_backend (book, mbe);
|
||||
auto split1 = xaccMallocSplit (book);
|
||||
auto split2 = xaccMallocSplit (book);
|
||||
txn = xaccMallocTransaction (book);
|
||||
@ -211,14 +213,14 @@ static void
|
||||
teardown (Fixture *fixture, gconstpointer pData)
|
||||
{
|
||||
QofBook *book = qof_instance_get_book (QOF_INSTANCE (fixture->txn));
|
||||
MockBackend *mbe = (MockBackend *)qof_book_get_backend (book);
|
||||
auto mbe = static_cast<MockBackend*>(qof_book_get_backend (book));
|
||||
|
||||
test_destroy (fixture->txn);
|
||||
test_destroy (fixture->acc1);
|
||||
test_destroy (fixture->acc2);
|
||||
test_destroy (fixture->curr);
|
||||
test_destroy (fixture->comm);
|
||||
g_free (mbe);
|
||||
delete mbe;
|
||||
qof_book_destroy(book);
|
||||
g_slist_free_full (fixture->hdlrs, test_free_log_handler);
|
||||
test_clear_error_list();
|
||||
@ -1695,7 +1697,7 @@ test_xaccTransRollbackEdit (Fixture *fixture, gconstpointer pData)
|
||||
KvpFrame *base_frame = NULL;
|
||||
auto sig_account = test_signal_new (QOF_INSTANCE (fixture->acc1),
|
||||
GNC_EVENT_ITEM_CHANGED, NULL);
|
||||
MockBackend *mbe = (MockBackend*)qof_book_get_backend (book);
|
||||
auto mbe = static_cast<MockBackend*>(qof_book_get_backend (book));
|
||||
auto split_00 = static_cast<Split*>(txn->splits->data);
|
||||
auto split_01 = static_cast<Split*>(txn->splits->next->data);
|
||||
auto split_02 = xaccMallocSplit (book);
|
||||
@ -1743,7 +1745,7 @@ test_xaccTransRollbackEdit (Fixture *fixture, gconstpointer pData)
|
||||
FALSE, FALSE, FALSE));
|
||||
g_assert (xaccSplitEqual (static_cast<Split*>(txn->splits->next->data),
|
||||
split_10, FALSE, FALSE, FALSE));
|
||||
g_assert_cmpstr (mbe->last_call, ==, "rollback");
|
||||
g_assert_cmpstr (mbe->m_last_call.c_str(), ==, "rollback");
|
||||
g_assert_cmpuint (qof_instance_get_editlevel (QOF_INSTANCE (txn)), ==, 0);
|
||||
g_assert (qof_instance_get_destroying (txn) == FALSE);
|
||||
test_signal_free (sig_account);
|
||||
@ -1757,7 +1759,7 @@ test_xaccTransRollbackEdit (Fixture *fixture, gconstpointer pData)
|
||||
static void
|
||||
test_xaccTransRollbackEdit_BackendErrors (Fixture *fixture, gconstpointer pData)
|
||||
{
|
||||
MockBackend *mbe = (MockBackend*)qof_book_get_backend (qof_instance_get_book (fixture->txn));
|
||||
auto mbe = static_cast<MockBackend*>(qof_book_get_backend (qof_instance_get_book (fixture->txn)));
|
||||
auto loglevel = static_cast<GLogLevelFlags>(G_LOG_LEVEL_CRITICAL | G_LOG_FLAG_FATAL);
|
||||
auto msg = "[xaccTransRollbackEdit()] Rollback Failed. Ouch!";
|
||||
auto check = test_error_struct_new ("gnc.engine", loglevel, msg);
|
||||
@ -1765,16 +1767,16 @@ test_xaccTransRollbackEdit_BackendErrors (Fixture *fixture, gconstpointer pData)
|
||||
(GLogFunc)test_checked_handler);
|
||||
g_object_ref (fixture->txn);
|
||||
xaccTransBeginEdit (fixture->txn);
|
||||
mock_backend_set_error (mbe, ERR_BACKEND_MODIFIED);
|
||||
mbe->inject_error(ERR_BACKEND_MODIFIED);
|
||||
xaccTransRollbackEdit (fixture->txn);
|
||||
g_assert_cmpint (check->hits, ==, 1);
|
||||
g_assert_cmpstr (mbe->last_call, ==, "rollback");
|
||||
memset (mbe->last_call, 0, sizeof (mbe->last_call));
|
||||
g_assert_cmpstr (mbe->m_last_call.c_str(), ==, "rollback");
|
||||
mbe->m_last_call.clear();
|
||||
xaccTransBeginEdit (fixture->txn);
|
||||
mock_backend_set_error (mbe, ERR_BACKEND_MOD_DESTROY);
|
||||
mbe->inject_error (ERR_BACKEND_MOD_DESTROY);
|
||||
xaccTransRollbackEdit (fixture->txn);
|
||||
g_assert_cmpint (GPOINTER_TO_INT(fixture->txn->num), ==, 1);
|
||||
g_assert_cmpstr (mbe->last_call, ==, "rollback");
|
||||
g_assert_cmpstr (mbe->m_last_call.c_str(), ==, "rollback");
|
||||
|
||||
}
|
||||
/* xaccTransIsOpen C: 23 in 7 SCM: 1 Local: 0:0:0
|
||||
@ -1941,7 +1943,7 @@ test_xaccTransReverse (Fixture *fixture, gconstpointer pData)
|
||||
g_assert (guid_equal (frame->get_slot(TRANS_REVERSED_BY)->get<GncGUID*>(),
|
||||
xaccTransGetGUID (rev)));
|
||||
|
||||
g_assert (qof_instance_is_dirty (QOF_INSTANCE (rev)));
|
||||
g_assert (!qof_instance_is_dirty (QOF_INSTANCE (rev))); //Cleared by commit
|
||||
g_assert_cmpint (g_list_length (fixture->txn->splits), ==,
|
||||
g_list_length (rev->splits));
|
||||
for (orig_splits = fixture->txn->splits,
|
||||
|
@ -25,17 +25,14 @@
|
||||
extern "C"
|
||||
{
|
||||
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <regex.h>
|
||||
#include <glib.h>
|
||||
#include <gmodule.h>
|
||||
#include <errno.h>
|
||||
#include <config.h>
|
||||
#include "qof.h"
|
||||
}
|
||||
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "qof-backend.hpp"
|
||||
|
||||
G_GNUC_UNUSED static QofLogModule log_module = QOF_MOD_BACKEND;
|
||||
@ -47,238 +44,128 @@ G_GNUC_UNUSED static QofLogModule log_module = QOF_MOD_BACKEND;
|
||||
* error handling *
|
||||
\********************************************************************/
|
||||
|
||||
void
|
||||
qof_backend_set_error (QofBackend *be, QofBackendError err)
|
||||
{
|
||||
if (!be) return;
|
||||
GModuleVec QofBackend::c_be_registry{};
|
||||
|
||||
void
|
||||
QofBackend::set_error(QofBackendError err)
|
||||
{
|
||||
/* use stack-push semantics. Only the earliest error counts */
|
||||
if (ERR_BACKEND_NO_ERR != be->last_err) return;
|
||||
be->last_err = err;
|
||||
if (m_last_err != ERR_BACKEND_NO_ERR) return;
|
||||
m_last_err = err;
|
||||
}
|
||||
|
||||
QofBackendError
|
||||
qof_backend_get_error (QofBackend *be)
|
||||
QofBackend::get_error()
|
||||
{
|
||||
QofBackendError err;
|
||||
if (!be) return ERR_BACKEND_NO_BACKEND;
|
||||
|
||||
/* use 'stack-pop' semantics */
|
||||
err = be->last_err;
|
||||
be->last_err = ERR_BACKEND_NO_ERR;
|
||||
auto err = m_last_err;
|
||||
m_last_err = ERR_BACKEND_NO_ERR;
|
||||
return err;
|
||||
}
|
||||
|
||||
gboolean
|
||||
qof_backend_check_error (QofBackend *be)
|
||||
bool
|
||||
QofBackend::check_error()
|
||||
{
|
||||
g_return_val_if_fail (be != NULL, TRUE);
|
||||
return be->last_err != ERR_BACKEND_NO_ERR;
|
||||
}
|
||||
|
||||
gboolean
|
||||
qof_backend_can_rollback (QofBackend* be)
|
||||
{
|
||||
if (be == nullptr) return FALSE;
|
||||
return be->rollback != nullptr;
|
||||
return m_last_err != ERR_BACKEND_NO_ERR;
|
||||
}
|
||||
|
||||
void
|
||||
qof_backend_rollback_instance (QofBackend* be, QofInstance* inst)
|
||||
QofBackend::set_message (std::string&& msg)
|
||||
{
|
||||
if (be == nullptr || be->rollback == nullptr) return;
|
||||
(be->rollback)(be, inst);
|
||||
m_error_msg = msg;
|
||||
}
|
||||
|
||||
void
|
||||
qof_backend_set_message (QofBackend *be, const char *format, ...)
|
||||
const std::string&&
|
||||
QofBackend::get_message ()
|
||||
{
|
||||
va_list args;
|
||||
char * buffer;
|
||||
|
||||
if (!be) return;
|
||||
|
||||
/* If there's already something here, free it */
|
||||
if (be->error_msg) g_free(be->error_msg);
|
||||
|
||||
if (!format)
|
||||
{
|
||||
be->error_msg = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
va_start(args, format);
|
||||
buffer = (char *)g_strdup_vprintf(format, args);
|
||||
va_end(args);
|
||||
|
||||
be->error_msg = buffer;
|
||||
return std::move(m_error_msg);
|
||||
}
|
||||
|
||||
char *
|
||||
qof_backend_get_message (QofBackend *be)
|
||||
bool
|
||||
QofBackend::register_backend(const char* directory, const char* module_name)
|
||||
{
|
||||
char * msg;
|
||||
|
||||
if (!be) return g_strdup("ERR_BACKEND_NO_BACKEND");
|
||||
if (!be->error_msg) return NULL;
|
||||
|
||||
/*
|
||||
* Just return the contents of the error_msg and then set it to
|
||||
* NULL. This is necessary, because the Backends don't seem to
|
||||
* have a destroy_backend function to take care of freeing stuff
|
||||
* up. The calling function should free the copy.
|
||||
* Also, this is consistent with the qof_backend_get_error() popping.
|
||||
*/
|
||||
|
||||
msg = be->error_msg;
|
||||
be->error_msg = NULL;
|
||||
return msg;
|
||||
}
|
||||
|
||||
/***********************************************************************/
|
||||
/* Get a clean backend */
|
||||
void
|
||||
qof_backend_init(QofBackend *be)
|
||||
{
|
||||
be->session_begin = NULL;
|
||||
be->session_end = NULL;
|
||||
be->destroy_backend = NULL;
|
||||
|
||||
be->load = NULL;
|
||||
|
||||
be->begin = NULL;
|
||||
be->commit = NULL;
|
||||
be->rollback = NULL;
|
||||
|
||||
be->sync = NULL;
|
||||
be->safe_sync = NULL;
|
||||
|
||||
be->export_fn = NULL;
|
||||
|
||||
be->last_err = ERR_BACKEND_NO_ERR;
|
||||
if (be->error_msg) g_free (be->error_msg);
|
||||
be->error_msg = NULL;
|
||||
be->percentage = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
qof_backend_destroy(QofBackend *be)
|
||||
{
|
||||
g_free(be->error_msg);
|
||||
be->error_msg = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
qof_backend_run_begin(QofBackend *be, QofInstance *inst)
|
||||
{
|
||||
if (!be || !inst)
|
||||
if (!g_module_supported ())
|
||||
{
|
||||
return;
|
||||
PWARN("Modules not supported.");
|
||||
return false;
|
||||
}
|
||||
if (!be->begin)
|
||||
{
|
||||
return;
|
||||
}
|
||||
(be->begin) (be, inst);
|
||||
}
|
||||
|
||||
gboolean
|
||||
qof_backend_begin_exists(const QofBackend *be)
|
||||
{
|
||||
if (be->begin)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
qof_backend_run_commit(QofBackend *be, QofInstance *inst)
|
||||
{
|
||||
if (!be || !inst)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!be->commit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
(be->commit) (be, inst);
|
||||
}
|
||||
|
||||
|
||||
gboolean
|
||||
qof_backend_commit_exists(const QofBackend *be)
|
||||
{
|
||||
if (!be)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if (be->commit)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static GSList* backend_module_list = NULL;
|
||||
|
||||
gboolean
|
||||
qof_load_backend_library (const char *directory, const char* module_name)
|
||||
{
|
||||
gchar *fullpath;
|
||||
GModule *backend;
|
||||
void (*module_init_func) (void);
|
||||
|
||||
g_return_val_if_fail(g_module_supported (), FALSE);
|
||||
fullpath = g_module_build_path (directory, module_name);
|
||||
auto fullpath = g_module_build_path (directory, module_name);
|
||||
/* Darwin modules can have either .so or .dylib for a suffix */
|
||||
if (!g_file_test (fullpath, G_FILE_TEST_EXISTS) &&
|
||||
g_strcmp0 (G_MODULE_SUFFIX, "so") == 0)
|
||||
{
|
||||
gchar *modname = g_strdup_printf ("lib%s.dylib", module_name);
|
||||
auto modname = g_strdup_printf ("lib%s.dylib", module_name);
|
||||
g_free (fullpath);
|
||||
fullpath = g_build_filename (directory, modname, NULL);
|
||||
g_free (modname);
|
||||
}
|
||||
backend = g_module_open (fullpath, G_MODULE_BIND_LAZY);
|
||||
auto backend = g_module_open (fullpath, G_MODULE_BIND_LAZY);
|
||||
g_free (fullpath);
|
||||
if (!backend)
|
||||
{
|
||||
g_message ("%s: %s\n", PACKAGE, g_module_error ());
|
||||
return FALSE;
|
||||
PINFO ("%s: %s\n", PACKAGE, g_module_error ());
|
||||
return false;
|
||||
}
|
||||
void (*module_init_func)(void);
|
||||
if (g_module_symbol (backend, "qof_backend_module_init",
|
||||
reinterpret_cast<void**>(&module_init_func)))
|
||||
reinterpret_cast<void**>(&module_init_func)))
|
||||
module_init_func ();
|
||||
|
||||
g_module_make_resident (backend);
|
||||
backend_module_list = g_slist_prepend (backend_module_list, backend);
|
||||
c_be_registry.push_back(backend);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
QofBackend::release_backends()
|
||||
{
|
||||
for (auto backend : c_be_registry)
|
||||
{
|
||||
void (*module_finalize_func)(void);
|
||||
if (g_module_symbol(backend, "qof_backend_module_finalize",
|
||||
reinterpret_cast<void**>(&module_finalize_func)))
|
||||
module_finalize_func();
|
||||
}
|
||||
}
|
||||
/***********************************************************************/
|
||||
QofBackendError
|
||||
qof_backend_get_error (QofBackend* qof_be)
|
||||
{
|
||||
if (qof_be == nullptr) return ERR_BACKEND_NO_ERR;
|
||||
return ((QofBackend*)qof_be)->get_error();
|
||||
}
|
||||
|
||||
void
|
||||
qof_backend_set_error (QofBackend* qof_be, QofBackendError err)
|
||||
{
|
||||
if (qof_be == nullptr) return;
|
||||
((QofBackend*)qof_be)->set_error(err);
|
||||
}
|
||||
|
||||
gboolean
|
||||
qof_backend_can_rollback (QofBackend* qof_be)
|
||||
{
|
||||
if (qof_be == nullptr) return FALSE;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
qof_backend_rollback_instance (QofBackend* qof_be, QofInstance* inst)
|
||||
{
|
||||
if (qof_be == nullptr) return;
|
||||
((QofBackend*)qof_be)->rollback(inst);
|
||||
}
|
||||
|
||||
gboolean
|
||||
qof_load_backend_library (const char *directory, const char* module_name)
|
||||
{
|
||||
return QofBackend::register_backend(directory, module_name);
|
||||
}
|
||||
|
||||
void
|
||||
qof_finalize_backend_libraries(void)
|
||||
{
|
||||
GSList* node;
|
||||
GModule* backend;
|
||||
void (*module_finalize_func) (void);
|
||||
|
||||
for (node = backend_module_list; node != NULL; node = node->next)
|
||||
{
|
||||
backend = (GModule*)node->data;
|
||||
|
||||
if (g_module_symbol(backend, "qof_backend_module_finalize",
|
||||
reinterpret_cast<void**>(&module_finalize_func)))
|
||||
module_finalize_func();
|
||||
|
||||
}
|
||||
QofBackend::release_backends();
|
||||
}
|
||||
|
||||
/************************* END OF FILE ********************************/
|
||||
|
@ -1,5 +1,6 @@
|
||||
/********************************************************************\
|
||||
* qofbackend-p.h -- private api for data storage backend *
|
||||
* qof-backend.hpp Declare QofBackend class *
|
||||
* 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 *
|
||||
@ -38,81 +39,26 @@
|
||||
@author Copyright (c) 2005 Neil Williams <linux@codehelp.co.uk>
|
||||
@{ */
|
||||
|
||||
#ifndef QOF_BACKEND_P_H
|
||||
#define QOF_BACKEND_P_H
|
||||
|
||||
#ifndef __QOF_BACKEND_HPP__
|
||||
#define __QOF_BACKEND_HPP__
|
||||
extern "C"
|
||||
{
|
||||
#include "qofbackend.h"
|
||||
#include "qofbook.h"
|
||||
#include "qofinstance-p.h"
|
||||
#include "qofquery.h"
|
||||
#include "qofsession.h"
|
||||
#include <gmodule.h>
|
||||
}
|
||||
|
||||
/**
|
||||
* The backend_new routine sets the functions that will be used
|
||||
* by the backend to perform the actions required by QOF. A
|
||||
* basic minimum is session_begin, session_end, load and
|
||||
* sync. Any unused functions should be set to NULL. If the
|
||||
* backend uses configuration options, backend_new must ensure
|
||||
* that these are set to usable defaults before returning. To use
|
||||
* configuration options, load_config and get_config must also
|
||||
* be defined.
|
||||
*
|
||||
* The session_begin() routine gives the backend a second initialization
|
||||
* opportunity. It is suggested that the backend check that
|
||||
* the URL is syntactically correct, and that it is actually
|
||||
* reachable. This is probably(?) a good time to initialize
|
||||
* the actual network connection.
|
||||
*
|
||||
* The 'ignore_lock' argument indicates whether the single-user
|
||||
* lock on the backend should be cleared. The typical GUI sequence
|
||||
* leading to this is: (1) GUI attempts to open the backend
|
||||
* by calling this routine with FALSE==ignore_lock. (2) If backend
|
||||
* error'ed BACKEND_LOCK, then GUI asks user what to do. (3) if user
|
||||
* answers 'break & enter' then this routine is called again with
|
||||
* TRUE==ignore_lock.
|
||||
*
|
||||
* The 'create_if_nonexistent' argument indicates whether this
|
||||
* routine should create a new 'database', if it doesn't already
|
||||
* exist. For example, for a file-backend, this would create the
|
||||
* file, if it didn't already exist. For an SQL backend, this
|
||||
* would create the database (the schema) if it didn't already
|
||||
* exist. This flag is used to implement the 'SaveAs' GUI, where
|
||||
* the user requests to save data to a new backend.
|
||||
*
|
||||
* The load() routine should load the minimal set of application data
|
||||
* needed for the application to be operable at initial startup.
|
||||
* It is assumed that the application will perform a 'run_query()'
|
||||
* to obtain any additional data that it needs. For file-based
|
||||
* backends, it is acceptable for the backend to return all data
|
||||
* at load time; for SQL-based backends, it is acceptable for the
|
||||
* backend to return no data.
|
||||
*
|
||||
* Thus, for example, the GnuCash postgres backend returned
|
||||
* the account tree, all currencies, and the pricedb, as these
|
||||
* were needed at startup. It did not have to return any
|
||||
* transactions whatsoever, as these were obtained at a later stage
|
||||
* when a user opened a register, resulting in a query being sent to
|
||||
* the backend.
|
||||
*
|
||||
* (Its OK to send over entities at this point, but one should
|
||||
* be careful of the network load; also, its possible that whatever
|
||||
* is sent is not what the user wanted anyway, which is why its
|
||||
* better to wait for the query).
|
||||
*
|
||||
* The begin() routine is called when the engine is about to
|
||||
* make a change to a data structure. It can provide an advisory
|
||||
* lock on data.
|
||||
*
|
||||
* The commit() routine commits the changes from the engine to the
|
||||
* backend data storage.
|
||||
*
|
||||
* The rollback() routine is used to revert changes in the engine
|
||||
* and unlock the backend.
|
||||
*
|
||||
* If the second user tries to modify an entity that
|
||||
* the first user deleted, then the backend should set the error
|
||||
* to ERR_BACKEND_MOD_DESTROY from this routine, so that the
|
||||
* engine can properly clean up.
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
/* NOTE: The following comments were musings by the original developer about how
|
||||
* some additional API might work. The compile/free/run_query functions were
|
||||
* implemented for the DBI backend but never put into use; the rest were never
|
||||
* implemented. They're here as something to consider if we ever decide to
|
||||
* implement them.
|
||||
*
|
||||
* The compile_query() method compiles a QOF query object into
|
||||
* a backend-specific data structure and returns the compiled
|
||||
@ -140,22 +86,6 @@
|
||||
* continue functioning even when disconnected from the server:
|
||||
* this is because it will have its local cache of data from which to work.
|
||||
*
|
||||
* The sync() routine synchronizes the engine contents to the backend.
|
||||
* This should done by using version numbers (hack alert -- the engine
|
||||
* does not currently contain version numbers).
|
||||
* If the engine contents are newer than what is in the backend, the
|
||||
* data is stored to the backend. If the engine contents are older,
|
||||
* then the engine contents are updated.
|
||||
*
|
||||
* Note that this sync operation is only meant to apply to the
|
||||
* current contents of the engine. This routine is not intended
|
||||
* to be used to fetch entity data from the backend.
|
||||
*
|
||||
* File based backends tend to use sync as if it was called dump.
|
||||
* Data is written out into the backend, overwriting the previous
|
||||
* data. Database backends should implement a more intelligent
|
||||
* solution.
|
||||
*
|
||||
* The events_pending() routines should return true if there are
|
||||
* external events which need to be processed to bring the
|
||||
* engine up to date with the backend.
|
||||
@ -164,10 +94,6 @@
|
||||
* by the events_pending() routine. It should return TRUE if
|
||||
* the engine was changed while engine events were suspended.
|
||||
*
|
||||
* The last_err member indicates the last error that occurred.
|
||||
* It should probably be implemented as an array (actually,
|
||||
* a stack) of all the errors that have occurred.
|
||||
*
|
||||
* For support of book partitioning, use special "Book" begin_edit()
|
||||
* and commit_edit() QOF_ID types.
|
||||
*
|
||||
@ -242,80 +168,147 @@ typedef enum
|
||||
LOAD_TYPE_LOAD_ALL
|
||||
} QofBackendLoadType;
|
||||
|
||||
struct QofBackend_s
|
||||
using GModuleVec = std::vector<GModule*>;
|
||||
struct QofBackend
|
||||
{
|
||||
void (*session_begin) (QofBackend *be,
|
||||
QofSession *session,
|
||||
const char *book_id,
|
||||
gboolean ignore_lock,
|
||||
gboolean create,
|
||||
gboolean force);
|
||||
void (*session_end) (QofBackend *);
|
||||
void (*destroy_backend) (/*@ only @*/ QofBackend *);
|
||||
|
||||
void (*load) (QofBackend *, /*@ dependent @*/ QofBook *, QofBackendLoadType);
|
||||
|
||||
void (*begin) (QofBackend *, QofInstance *);
|
||||
void (*commit) (QofBackend *, QofInstance *);
|
||||
void (*rollback) (QofBackend *, QofInstance *);
|
||||
|
||||
void (*sync) (QofBackend *, /*@ dependent @*/ QofBook *);
|
||||
void (*safe_sync) (QofBackend *, /*@ dependent @*/ QofBook *);
|
||||
/* This is implented only in the XML backend where it exports only a chart
|
||||
* of accounts.
|
||||
public:
|
||||
/* For reasons that aren't a bit clear, using the default constructor
|
||||
* sometimes initializes m_last_err incorrectly with Xcode8 and a 32-bit
|
||||
* build unless the initialization is stepped-through in a debugger.
|
||||
*/
|
||||
void (*export_fn) (QofBackend *, QofBook *);
|
||||
QofBePercentageFunc percentage;
|
||||
|
||||
QofBackendError last_err;
|
||||
char * error_msg;
|
||||
|
||||
gint config_count;
|
||||
QofBackend() :
|
||||
m_percentage{nullptr}, m_fullpath{}, m_last_err{ERR_BACKEND_NO_ERR},
|
||||
m_error_msg{} {}
|
||||
QofBackend(const QofBackend&) = delete;
|
||||
QofBackend(const QofBackend&&) = delete;
|
||||
virtual ~QofBackend() = default;
|
||||
/**
|
||||
* Open the file or connect to the server.
|
||||
* @param session The QofSession that will control the backend.
|
||||
* @param book_id The book's string identifier.
|
||||
* @param ignore_lock indicates whether the single-user lock on the backend
|
||||
* should be cleared. The typical GUI sequence leading to this is:
|
||||
* (1) GUI attempts to open the backend by calling this routine with
|
||||
* ignore_lock false.
|
||||
* (2) If backend error'ed BACKEND_LOCK, then GUI asks user what to do.
|
||||
* (3) if user answers 'break & enter' then this routine is called again with
|
||||
* ignore_lock true.
|
||||
* @param create indicates whether this routine should create a new
|
||||
* 'database', if it doesn't already exist. For example, for a file-backend,
|
||||
* this would create the file, if it didn't already exist. For an SQL
|
||||
* backend, this would create the database (the schema) if it didn't already
|
||||
* exist. This flag is used to implement the 'SaveAs' GUI, where the user
|
||||
* requests to save data to a new backend.
|
||||
*
|
||||
* @param force works with create to force creating a new database even if
|
||||
* one already exists at the same URI.
|
||||
*/
|
||||
virtual void session_begin(QofSession *session, const char* book_id,
|
||||
bool ignore_lock, bool create, bool force) = 0;
|
||||
virtual void session_end() = 0;
|
||||
/**
|
||||
* Load the minimal set of application data needed for the application to be
|
||||
* operable at initial startup. It is assumed that the application will
|
||||
* perform a 'run_query()' to obtain any additional data that it needs. For
|
||||
* file-based backends, it is acceptable for the backend to return all data
|
||||
* at load time; for SQL-based backends, it is acceptable for the backend to
|
||||
* return no data.
|
||||
*
|
||||
* Thus, for example, the old GnuCash postgres backend returned the account
|
||||
* tree, all currencies, and the pricedb, as these were needed at startup.
|
||||
* It did not have to return any transactions whatsoever, as these were
|
||||
* obtained at a later stage when a user opened a register, resulting in a
|
||||
* query being sent to the backend. The current DBI backend on the other hand
|
||||
* loads the entire database into memory.
|
||||
*
|
||||
* (Its OK to send over entities at this point, but one should
|
||||
* be careful of the network load; also, its possible that whatever
|
||||
* is sent is not what the user wanted anyway, which is why its
|
||||
* better to wait for the query).
|
||||
*/
|
||||
virtual void load (QofBook*, QofBackendLoadType) = 0;
|
||||
/**
|
||||
* Called when the engine is about to make a change to a data structure. It
|
||||
* could provide an advisory lock on data, but no backend does this.
|
||||
*/
|
||||
virtual void begin(QofInstance*) {}
|
||||
/**
|
||||
* Commits the changes from the engine to the backend data storage.
|
||||
*/
|
||||
virtual void commit (QofInstance*) {}
|
||||
/**
|
||||
* Revert changes in the engine and unlock the backend.
|
||||
*/
|
||||
virtual void rollback(QofInstance*) {}
|
||||
/**
|
||||
* Synchronizes the engine contents to the backend.
|
||||
* This should done by using version numbers (hack alert -- the engine
|
||||
* does not currently contain version numbers).
|
||||
* If the engine contents are newer than what is in the backend, the
|
||||
* data is stored to the backend. If the engine contents are older,
|
||||
* then the engine contents are updated.
|
||||
*
|
||||
* Note that this sync operation is only meant to apply to the
|
||||
* current contents of the engine. This routine is not intended
|
||||
* to be used to fetch entity data from the backend.
|
||||
*
|
||||
* File based backends tend to use sync as if it was called dump.
|
||||
* Data is written out into the backend, overwriting the previous
|
||||
* data. Database backends should implement a more intelligent
|
||||
* solution.
|
||||
*/
|
||||
virtual void sync(QofBook *) = 0;
|
||||
/** Perform a sync in a way that prevents data loss on a DBI backend.
|
||||
*/
|
||||
virtual void safe_sync(QofBook *) = 0;
|
||||
/** Extract the chart of accounts from the current database and create a new
|
||||
* database with it. Implemented only in the XML backend at present.
|
||||
*/
|
||||
virtual void export_coa(QofBook *) {}
|
||||
/** Set the error value only if there isn't already an error already.
|
||||
*/
|
||||
void set_error(QofBackendError err);
|
||||
/** Retrieve the currently-stored error and clear it.
|
||||
*/
|
||||
QofBackendError get_error();
|
||||
/** Report if there is an error.
|
||||
*/
|
||||
bool check_error();
|
||||
/** Set a descriptive message that can be displayed to the user when there's an
|
||||
* error.
|
||||
*/
|
||||
void set_message(std::string&&);
|
||||
/** Retrieve and clear the stored error message.
|
||||
*/
|
||||
const std::string&& get_message();
|
||||
/** Store and retrieve a backend-specific function for determining the progress
|
||||
* in completing a long operation, for use with a progress meter.
|
||||
*/
|
||||
void set_percentage(QofBePercentageFunc pctfn) { m_percentage = pctfn; }
|
||||
QofBePercentageFunc get_percentage() { return m_percentage; }
|
||||
/** Retrieve the backend's storage URI.
|
||||
*/
|
||||
std::string get_uri() { return m_fullpath; }
|
||||
/**
|
||||
* Class methods for dynamically loading the several backends and for freeing
|
||||
* them at shutdown.
|
||||
*/
|
||||
static bool register_backend(const char*, const char*);
|
||||
static void release_backends();
|
||||
protected:
|
||||
QofBePercentageFunc m_percentage;
|
||||
/** Each backend resolves a fully-qualified file path.
|
||||
* This holds the filepath and communicates it to the frontends.
|
||||
*/
|
||||
char * fullpath;
|
||||
std::string m_fullpath;
|
||||
private:
|
||||
static GModuleVec c_be_registry;
|
||||
QofBackendError m_last_err;
|
||||
std::string m_error_msg;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/** The qof_backend_set_message() assigns a string to the backend error message.
|
||||
*/
|
||||
void qof_backend_set_message(QofBackend *be, const char *format, ...);
|
||||
|
||||
/** The qof_backend_get_message() pops the error message string from
|
||||
* the Backend. This string should be freed with g_free().
|
||||
*/
|
||||
char * qof_backend_get_message(QofBackend *be);
|
||||
|
||||
void qof_backend_init(QofBackend *be);
|
||||
void qof_backend_destroy(QofBackend *be);
|
||||
|
||||
/** Allow backends to see if the book is open
|
||||
|
||||
@return 'y' if book is open, otherwise 'n'.
|
||||
*/
|
||||
gchar qof_book_get_open_marker(const QofBook *book);
|
||||
|
||||
/** get the book version
|
||||
|
||||
used for tracking multiuser updates in backends.
|
||||
|
||||
@return -1 if no book exists, 0 if the book is
|
||||
new, otherwise the book version number.
|
||||
*/
|
||||
gint32 qof_book_get_version (const QofBook *book);
|
||||
|
||||
void qof_book_set_version (QofBook *book, gint32 version);
|
||||
|
||||
/* @} */
|
||||
/* @} */
|
||||
/* @} */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* QOF_BACKEND_P_H */
|
||||
#endif /* __QOF_BACKEND_HPP__ */
|
||||
|
@ -127,15 +127,8 @@ typedef enum
|
||||
ERR_RPC_FAILED, /**< Operation failed */
|
||||
ERR_RPC_NOT_ADDED, /**< object not added */
|
||||
} QofBackendError;
|
||||
/** \brief Pseudo-object providing an interface between the
|
||||
* engine and a persistant data store (e.g. a server, a database,
|
||||
* or a file).
|
||||
*
|
||||
* There are no backend functions that are 'public' to users of the
|
||||
* engine. The backend can, however, report errors to the GUI & other
|
||||
* front-end users.
|
||||
*/
|
||||
typedef struct QofBackend_s QofBackend;
|
||||
|
||||
typedef struct QofBackend QofBackend;
|
||||
|
||||
/* The following functions are used in C files. */
|
||||
/** Get the last backend error. */
|
||||
@ -143,7 +136,7 @@ typedef enum
|
||||
/** Set the error on the specified QofBackend. */
|
||||
void qof_backend_set_error (QofBackend*, QofBackendError);
|
||||
|
||||
/* Temporary wrapper so that we don't have to expose qofbackend-p.h to Transaction.c */
|
||||
/* Temporary wrapper so that we don't have to expose qof-backend.hpp to Transaction.c */
|
||||
gboolean qof_backend_can_rollback (QofBackend*);
|
||||
void qof_backend_rollback_instance (QofBackend*, QofInstance*);
|
||||
|
||||
@ -173,30 +166,7 @@ typedef void (*QofBePercentageFunc) (/*@ null @*/ const char *message, double pe
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
/** @name Allow access to the begin routine for this backend. */
|
||||
//@{
|
||||
|
||||
void qof_backend_run_begin(QofBackend *be, QofInstance *inst);
|
||||
|
||||
gboolean qof_backend_begin_exists(const QofBackend *be);
|
||||
|
||||
void qof_backend_run_commit(QofBackend *be, QofInstance *inst);
|
||||
|
||||
gboolean qof_backend_commit_exists(const QofBackend *be);
|
||||
//@}
|
||||
|
||||
/** Report if the backend is in an error state.
|
||||
* Since get_error resets the error state, its use for branching as the backend
|
||||
* bubbles back up to the session would make the session think that there was
|
||||
* no error.
|
||||
* \param be The backend being tested.
|
||||
* \return TRUE if the backend has an error set.
|
||||
*/
|
||||
gboolean qof_backend_check_error (QofBackend *be);
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* QOF_BACKEND_H */
|
||||
/** @} */
|
||||
/** @} */
|
||||
|
@ -958,7 +958,6 @@ gboolean
|
||||
qof_begin_edit (QofInstance *inst)
|
||||
{
|
||||
QofInstancePrivate *priv;
|
||||
QofBackend * be;
|
||||
|
||||
if (!inst) return FALSE;
|
||||
|
||||
@ -968,9 +967,9 @@ qof_begin_edit (QofInstance *inst)
|
||||
if (0 >= priv->editlevel)
|
||||
priv->editlevel = 1;
|
||||
|
||||
be = qof_book_get_backend(priv->book);
|
||||
if (be && qof_backend_begin_exists(be))
|
||||
qof_backend_run_begin(be, inst);
|
||||
auto be = qof_book_get_backend(priv->book);
|
||||
if (be)
|
||||
be->begin(inst);
|
||||
else
|
||||
priv->dirty = TRUE;
|
||||
|
||||
@ -1002,7 +1001,6 @@ qof_commit_edit_part2(QofInstance *inst,
|
||||
void (*on_free)(QofInstance *))
|
||||
{
|
||||
QofInstancePrivate *priv;
|
||||
QofBackend * be;
|
||||
|
||||
priv = GET_PRIVATE(inst);
|
||||
|
||||
@ -1013,27 +1011,27 @@ qof_commit_edit_part2(QofInstance *inst,
|
||||
}
|
||||
|
||||
/* See if there's a backend. If there is, invoke it. */
|
||||
be = qof_book_get_backend(priv->book);
|
||||
if (be && qof_backend_commit_exists(be))
|
||||
auto be = qof_book_get_backend(priv->book);
|
||||
if (be)
|
||||
{
|
||||
QofBackendError errcode;
|
||||
|
||||
/* clear errors */
|
||||
do
|
||||
{
|
||||
errcode = qof_backend_get_error(be);
|
||||
errcode = be->get_error();
|
||||
}
|
||||
while (ERR_BACKEND_NO_ERR != errcode);
|
||||
while (errcode != ERR_BACKEND_NO_ERR);
|
||||
|
||||
qof_backend_run_commit(be, inst);
|
||||
errcode = qof_backend_get_error(be);
|
||||
if (ERR_BACKEND_NO_ERR != errcode)
|
||||
be->commit(inst);
|
||||
errcode = be->get_error();
|
||||
if (errcode != ERR_BACKEND_NO_ERR)
|
||||
{
|
||||
/* XXX Should perform a rollback here */
|
||||
priv->do_free = FALSE;
|
||||
|
||||
/* Push error back onto the stack */
|
||||
qof_backend_set_error (be, errcode);
|
||||
be->set_error (errcode);
|
||||
if (on_error)
|
||||
on_error(inst, errcode);
|
||||
return FALSE;
|
||||
|
@ -122,6 +122,7 @@ QofSessionImpl::QofSessionImpl () noexcept
|
||||
: m_book {qof_book_new ()},
|
||||
m_book_id {},
|
||||
m_saving {false},
|
||||
m_last_err {},
|
||||
m_error_message {}
|
||||
{
|
||||
clear_error ();
|
||||
@ -157,10 +158,7 @@ QofSessionImpl::destroy_backend () noexcept
|
||||
if (backend)
|
||||
{
|
||||
clear_error ();
|
||||
if (backend->destroy_backend)
|
||||
backend->destroy_backend (backend);
|
||||
else
|
||||
g_free(backend);
|
||||
delete backend;
|
||||
qof_book_set_backend (m_book, nullptr);
|
||||
}
|
||||
}
|
||||
@ -218,7 +216,7 @@ QofSessionImpl::load (QofPercentageFunc percentage_func) noexcept
|
||||
* top-level account group out of the backend, and that is a
|
||||
* generic, backend-independent operation.
|
||||
*/
|
||||
QofBackend * be {qof_book_get_backend (oldbook)};
|
||||
auto be (qof_book_get_backend (oldbook));
|
||||
qof_book_set_backend (newbook, be);
|
||||
|
||||
/* Starting the session should result in a bunch of accounts
|
||||
@ -227,12 +225,9 @@ QofSessionImpl::load (QofPercentageFunc percentage_func) noexcept
|
||||
*/
|
||||
if (be)
|
||||
{
|
||||
be->percentage = percentage_func;
|
||||
if (be->load)
|
||||
{
|
||||
be->load (be, newbook, LOAD_TYPE_INITIAL_LOAD);
|
||||
push_error (qof_backend_get_error(be), {});
|
||||
}
|
||||
be->set_percentage(percentage_func);
|
||||
be->load (newbook, LOAD_TYPE_INITIAL_LOAD);
|
||||
push_error (be->get_error(), {});
|
||||
}
|
||||
|
||||
/* XXX if the load fails, then we try to restore the old set of books;
|
||||
@ -262,7 +257,8 @@ QofSessionImpl::load (QofPercentageFunc percentage_func) noexcept
|
||||
}
|
||||
|
||||
void
|
||||
QofSessionImpl::begin (std::string new_book_id, bool ignore_lock, bool create, bool force) noexcept
|
||||
QofSessionImpl::begin (std::string new_book_id, bool ignore_lock,
|
||||
bool create, bool force) noexcept
|
||||
{
|
||||
ENTER (" sess=%p ignore_lock=%d, book-id=%s",
|
||||
this, ignore_lock, new_book_id.c_str ());
|
||||
@ -313,7 +309,8 @@ QofSessionImpl::begin (std::string new_book_id, bool ignore_lock, bool create, b
|
||||
g_free (scheme);
|
||||
|
||||
/* No backend was found. That's bad. */
|
||||
if (!qof_book_get_backend (m_book))
|
||||
auto backend = qof_book_get_backend (m_book);
|
||||
if (backend == nullptr)
|
||||
{
|
||||
m_book_id = {};
|
||||
if (ERR_BACKEND_NO_ERR == get_error ())
|
||||
@ -324,26 +321,22 @@ QofSessionImpl::begin (std::string new_book_id, bool ignore_lock, bool create, b
|
||||
}
|
||||
|
||||
/* If there's a begin method, call that. */
|
||||
if (qof_book_get_backend (m_book)->session_begin)
|
||||
backend->session_begin(this, m_book_id.c_str(), ignore_lock, create, force);
|
||||
PINFO ("Done running session_begin on backend");
|
||||
QofBackendError const err {backend->get_error()};
|
||||
auto msg (backend->get_message());
|
||||
if (err != ERR_BACKEND_NO_ERR)
|
||||
{
|
||||
auto backend = qof_book_get_backend (m_book);
|
||||
(backend->session_begin) (backend, this, m_book_id.c_str (), ignore_lock, create, force);
|
||||
PINFO ("Done running session_begin on backend");
|
||||
QofBackendError const err {qof_backend_get_error (backend)};
|
||||
char * msg {qof_backend_get_message (backend)};
|
||||
if (err != ERR_BACKEND_NO_ERR)
|
||||
{
|
||||
m_book_id = {};
|
||||
push_error (err, msg ? msg : "");
|
||||
LEAVE (" backend error %d %s", err, msg ? msg : "(null)");
|
||||
return;
|
||||
}
|
||||
if (msg != nullptr)
|
||||
{
|
||||
PWARN("%s", msg);
|
||||
g_free(msg);
|
||||
}
|
||||
m_book_id = {};
|
||||
push_error (err, msg);
|
||||
LEAVE (" backend error %d %s", err, msg.empty() ? "(null)" : msg.c_str());
|
||||
return;
|
||||
}
|
||||
if (!msg.empty())
|
||||
{
|
||||
PWARN("%s", msg.c_str());
|
||||
}
|
||||
|
||||
LEAVE (" sess=%p book-id=%s", this, new_book_id.c_str ());
|
||||
}
|
||||
|
||||
@ -352,10 +345,10 @@ QofSessionImpl::end () noexcept
|
||||
{
|
||||
ENTER ("sess=%p book_id=%s", this, m_book_id.c_str ());
|
||||
auto backend = qof_book_get_backend (m_book);
|
||||
if (backend && backend->session_end)
|
||||
(backend->session_end) (backend);
|
||||
if (backend != nullptr)
|
||||
backend->session_end();
|
||||
clear_error ();
|
||||
m_book_id = {};
|
||||
m_book_id.clear();
|
||||
LEAVE ("sess=%p book_id=%s", this, m_book_id.c_str ());
|
||||
}
|
||||
|
||||
@ -368,12 +361,12 @@ QofSessionImpl::clear_error () noexcept
|
||||
m_error_message = {};
|
||||
|
||||
/* pop the stack on the backend as well. */
|
||||
if (qof_book_get_backend (m_book))
|
||||
if (auto backend = qof_book_get_backend (m_book))
|
||||
{
|
||||
QofBackendError err;
|
||||
QofBackendError err = ERR_BACKEND_NO_ERR;
|
||||
do
|
||||
err = qof_backend_get_error (qof_book_get_backend (m_book));
|
||||
while (ERR_BACKEND_NO_ERR != err);
|
||||
err = backend->get_error();
|
||||
while (err != ERR_BACKEND_NO_ERR);
|
||||
}
|
||||
}
|
||||
|
||||
@ -388,12 +381,12 @@ QofBackendError
|
||||
QofSessionImpl::get_error () noexcept
|
||||
{
|
||||
/* if we have a local error, return that. */
|
||||
if (ERR_BACKEND_NO_ERR != m_last_err)
|
||||
if (m_last_err != ERR_BACKEND_NO_ERR)
|
||||
return m_last_err;
|
||||
auto qof_be = qof_book_get_backend (m_book);
|
||||
if (qof_be == nullptr) return ERR_BACKEND_NO_ERR;
|
||||
|
||||
if (!qof_book_get_backend (m_book)) return ERR_BACKEND_NO_ERR;
|
||||
|
||||
m_last_err = qof_backend_get_error (qof_book_get_backend (m_book));
|
||||
m_last_err = qof_be->get_error();
|
||||
return m_last_err;
|
||||
}
|
||||
|
||||
@ -431,8 +424,9 @@ QofSession::get_backend () const noexcept
|
||||
std::string
|
||||
QofSessionImpl::get_file_path () const noexcept
|
||||
{
|
||||
if (!qof_book_get_backend (m_book)) return nullptr;
|
||||
return qof_book_get_backend (m_book)->fullpath;
|
||||
auto backend = qof_book_get_backend (m_book);
|
||||
if (!backend) return nullptr;
|
||||
return backend->get_uri();
|
||||
}
|
||||
|
||||
std::string const &
|
||||
@ -469,17 +463,14 @@ QofSessionImpl::save (QofPercentageFunc percentage_func) noexcept
|
||||
{
|
||||
/* if invoked as SaveAs(), then backend not yet set */
|
||||
qof_book_set_backend (m_book, backend);
|
||||
backend->percentage = percentage_func;
|
||||
if (backend->sync)
|
||||
backend->set_percentage(percentage_func);
|
||||
backend->sync(m_book);
|
||||
auto err = backend->get_error();
|
||||
if (err != ERR_BACKEND_NO_ERR)
|
||||
{
|
||||
(backend->sync)(backend, m_book);
|
||||
QofBackendError err {qof_backend_get_error (backend)};
|
||||
if (ERR_BACKEND_NO_ERR != err)
|
||||
{
|
||||
push_error (err, {});
|
||||
m_saving = false;
|
||||
return;
|
||||
}
|
||||
push_error (err, {});
|
||||
m_saving = false;
|
||||
return;
|
||||
}
|
||||
/* If we got to here, then the backend saved everything
|
||||
* just fine, and we are done. So return. */
|
||||
@ -499,17 +490,15 @@ QofSessionImpl::safe_save (QofPercentageFunc percentage_func) noexcept
|
||||
{
|
||||
auto backend = qof_book_get_backend (m_book);
|
||||
if (!backend) return;
|
||||
if (!backend->safe_sync) return;
|
||||
backend->percentage = percentage_func;
|
||||
(backend->safe_sync) (backend, get_book ());
|
||||
auto err = qof_backend_get_error (qof_book_get_backend (m_book));
|
||||
auto msg = qof_backend_get_message (qof_book_get_backend (m_book));
|
||||
backend->set_percentage(percentage_func);
|
||||
backend->safe_sync(get_book ());
|
||||
auto err = backend->get_error();
|
||||
auto msg = backend->get_message();
|
||||
if (err != ERR_BACKEND_NO_ERR)
|
||||
{
|
||||
m_book_id = {};
|
||||
push_error (err, msg ? msg : "");
|
||||
m_book_id = nullptr;
|
||||
push_error (err, msg);
|
||||
}
|
||||
g_free (msg);
|
||||
}
|
||||
|
||||
void
|
||||
@ -517,9 +506,8 @@ QofSessionImpl::ensure_all_data_loaded () noexcept
|
||||
{
|
||||
auto backend = qof_book_get_backend (m_book);
|
||||
if (!backend) return;
|
||||
if (!backend->load) return;
|
||||
backend->load(backend, get_book (), LOAD_TYPE_LOAD_ALL);
|
||||
push_error (qof_backend_get_error (backend), {});
|
||||
backend->load(m_book, LOAD_TYPE_LOAD_ALL);
|
||||
push_error (backend->get_error(), {});
|
||||
}
|
||||
|
||||
void
|
||||
@ -552,7 +540,8 @@ QofSessionImpl::process_events () const noexcept
|
||||
* book-closing is implemented.
|
||||
*/
|
||||
bool
|
||||
QofSessionImpl::export_session (QofSessionImpl & real_session, QofPercentageFunc percentage_func) noexcept
|
||||
QofSessionImpl::export_session (QofSessionImpl & real_session,
|
||||
QofPercentageFunc percentage_func) noexcept
|
||||
{
|
||||
auto real_book = real_session.get_book ();
|
||||
ENTER ("tmp_session=%p real_session=%p book=%p book_id=%s",
|
||||
@ -564,12 +553,11 @@ QofSessionImpl::export_session (QofSessionImpl & real_session, QofPercentageFunc
|
||||
auto backend2 = qof_book_get_backend(m_book);
|
||||
if (!backend2) return false;
|
||||
|
||||
backend2->percentage = percentage_func;
|
||||
if (!backend2->export_fn) return true;
|
||||
backend2->set_percentage(percentage_func);
|
||||
|
||||
(backend2->export_fn)(backend2, real_book);
|
||||
auto err = qof_backend_get_error(backend2);
|
||||
if (ERR_BACKEND_NO_ERR != err)
|
||||
backend2->export_coa(real_book);
|
||||
auto err = backend2->get_error();
|
||||
if (err != ERR_BACKEND_NO_ERR)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
@ -749,4 +737,3 @@ qof_session_get_error (QofSession * session)
|
||||
if (!session) return ERR_BACKEND_NO_BACKEND;
|
||||
return session->get_error();
|
||||
}
|
||||
|
||||
|
@ -273,7 +273,7 @@ qof_close(void)
|
||||
{
|
||||
qof_query_shutdown ();
|
||||
qof_object_shutdown ();
|
||||
qof_finalize_backend_libraries();
|
||||
QofBackend::release_backends();
|
||||
qof_string_cache_destroy ();
|
||||
qof_log_shutdown();
|
||||
}
|
||||
|
@ -41,11 +41,86 @@ static gboolean is_called;
|
||||
#define _Q "`"
|
||||
#endif
|
||||
|
||||
static struct
|
||||
{
|
||||
QofInstance *m_inst = nullptr;
|
||||
QofBackend *m_be = nullptr;
|
||||
|
||||
bool m_commit_called = false;
|
||||
bool m_commit_with_err_called = false;
|
||||
bool m_on_error_called = false;
|
||||
bool m_on_free_called = false;
|
||||
bool m_on_done_called = false;
|
||||
QofBackendError m_err = ERR_BACKEND_NO_ERR;
|
||||
} commit_test;
|
||||
|
||||
|
||||
class MockBackend : public QofBackend
|
||||
{
|
||||
public:
|
||||
MockBackend() : m_qof_error{ERR_BACKEND_NO_ERR} {
|
||||
commit_test.m_be = this;
|
||||
}
|
||||
void session_begin(QofSession* sess, const char* book_name,
|
||||
bool ignore_lock, bool create, bool force) override {}
|
||||
void session_end() override {}
|
||||
void load(QofBook*, QofBackendLoadType) override {}
|
||||
void sync(QofBook* book) override {}
|
||||
void safe_sync(QofBook* book) override {}
|
||||
void begin(QofInstance* inst) override {
|
||||
g_assert(inst);
|
||||
g_assert(QOF_IS_INSTANCE(inst));
|
||||
commit_test.m_inst = inst;
|
||||
}
|
||||
void set_error(QofBackendError err) {
|
||||
QofBackend::set_error(err);
|
||||
commit_test.m_err = err;
|
||||
}
|
||||
void commit(QofInstance* inst) override {
|
||||
g_assert( inst );
|
||||
g_assert( QOF_IS_INSTANCE( inst ) );
|
||||
g_assert( commit_test.m_inst == inst );
|
||||
g_assert( commit_test.m_be == this );
|
||||
commit_test.m_commit_called = true;
|
||||
set_error(m_qof_error);
|
||||
|
||||
}
|
||||
void rollback(QofInstance* inst) override {}
|
||||
void inject_error(QofBackendError err) {
|
||||
m_qof_error = err;
|
||||
}
|
||||
private:
|
||||
QofBackendError m_qof_error;
|
||||
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
QofInstance *inst;
|
||||
} Fixture;
|
||||
|
||||
static void
|
||||
on_error(QofInstance* inst, QofBackendError err) {
|
||||
g_assert( inst );
|
||||
g_assert( QOF_IS_INSTANCE( inst ) );
|
||||
g_assert( commit_test.m_err == err );
|
||||
commit_test.m_on_error_called = true;
|
||||
}
|
||||
static void
|
||||
on_done(QofInstance* inst) {
|
||||
g_assert( inst );
|
||||
g_assert( QOF_IS_INSTANCE( inst ) );
|
||||
g_assert( commit_test.m_inst == inst );
|
||||
commit_test.m_on_done_called = true;
|
||||
}
|
||||
static void
|
||||
on_free(QofInstance* inst) {
|
||||
g_assert( inst );
|
||||
g_assert( QOF_IS_INSTANCE( inst ) );
|
||||
g_assert(commit_test.m_inst == inst );
|
||||
commit_test.m_on_free_called = true;
|
||||
}
|
||||
|
||||
/* use g_free on error_message after this function been called */
|
||||
static gboolean
|
||||
fatal_handler ( const char * log_domain,
|
||||
@ -423,7 +498,6 @@ mock_backend_begin( QofBackend *be, QofInstance *inst )
|
||||
{
|
||||
g_assert( be );
|
||||
g_assert( inst );
|
||||
g_assert( be->begin == mock_backend_begin );
|
||||
g_assert_cmpstr( inst->e_type, == , "test type" );
|
||||
is_called = TRUE;
|
||||
}
|
||||
@ -431,14 +505,12 @@ mock_backend_begin( QofBackend *be, QofInstance *inst )
|
||||
static void
|
||||
test_instance_begin_edit( Fixture *fixture, gconstpointer pData )
|
||||
{
|
||||
QofBackend *be;
|
||||
QofBook *book;
|
||||
gboolean result;
|
||||
|
||||
/* setup */
|
||||
be = g_new0( QofBackend, 1 );
|
||||
auto be = new MockBackend;
|
||||
g_assert( be );
|
||||
qof_backend_init( be );
|
||||
book = qof_book_new();
|
||||
g_assert( book );
|
||||
g_assert( QOF_IS_BOOK( book ) );
|
||||
@ -467,22 +539,19 @@ test_instance_begin_edit( Fixture *fixture, gconstpointer pData )
|
||||
|
||||
g_test_message( "Test when instance's editlevel is <= 0 and backend is set" );
|
||||
result = FALSE;
|
||||
is_called = FALSE;
|
||||
qof_instance_reset_editlevel( fixture->inst );
|
||||
qof_instance_set_dirty_flag( fixture->inst, FALSE );
|
||||
qof_instance_set_book( fixture->inst, book );
|
||||
be->begin = mock_backend_begin;
|
||||
result = qof_begin_edit( fixture->inst );
|
||||
g_assert( result == TRUE );
|
||||
g_assert_cmpint( qof_instance_get_editlevel( fixture->inst ), == , 1 );
|
||||
g_assert( qof_instance_get_dirty_flag( fixture->inst ) == FALSE );
|
||||
g_assert( is_called );
|
||||
|
||||
/* clean up */
|
||||
qof_book_set_backend( book, NULL );
|
||||
qof_book_destroy( book );
|
||||
qof_backend_destroy( be );
|
||||
g_free( be );
|
||||
delete be;
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
@ -527,82 +596,16 @@ test_instance_commit_edit( Fixture *fixture, gconstpointer pData )
|
||||
|
||||
/* backend commit test start */
|
||||
|
||||
static struct
|
||||
{
|
||||
gpointer inst;
|
||||
gpointer be;
|
||||
|
||||
gboolean commit_called;
|
||||
gboolean commit_with_err_called;
|
||||
gboolean on_error_called;
|
||||
gboolean on_free_called;
|
||||
gboolean on_done_called;
|
||||
QofBackendError err;
|
||||
} commit_test_part2;
|
||||
|
||||
static void
|
||||
mock_backend_commit( QofBackend *be, QofInstance *inst )
|
||||
{
|
||||
g_assert( inst );
|
||||
g_assert( be );
|
||||
g_assert( QOF_IS_INSTANCE( inst ) );
|
||||
g_assert( commit_test_part2.inst == inst );
|
||||
g_assert( commit_test_part2.be == be );
|
||||
commit_test_part2.commit_called = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
mock_backend_commit_with_error( QofBackend *be, QofInstance *inst )
|
||||
{
|
||||
g_assert( inst );
|
||||
g_assert( be );
|
||||
g_assert( QOF_IS_INSTANCE( inst ) );
|
||||
g_assert( commit_test_part2.inst == inst );
|
||||
g_assert( commit_test_part2.be == be );
|
||||
qof_backend_set_error( be, ERR_BACKEND_NO_HANDLER );
|
||||
commit_test_part2.err = ERR_BACKEND_NO_HANDLER;
|
||||
commit_test_part2.commit_with_err_called = TRUE;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
mock_on_error( QofInstance *inst, QofBackendError be_error )
|
||||
{
|
||||
g_assert( inst );
|
||||
g_assert( QOF_IS_INSTANCE( inst ) );
|
||||
g_assert( commit_test_part2.err == be_error );
|
||||
commit_test_part2.on_error_called = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
mock_on_done( QofInstance *inst )
|
||||
{
|
||||
g_assert( inst );
|
||||
g_assert( QOF_IS_INSTANCE( inst ) );
|
||||
g_assert( commit_test_part2.inst == inst );
|
||||
commit_test_part2.on_done_called = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
mock_on_free( QofInstance *inst )
|
||||
{
|
||||
g_assert( inst );
|
||||
g_assert( QOF_IS_INSTANCE( inst ) );
|
||||
g_assert( commit_test_part2.inst == inst );
|
||||
commit_test_part2.on_free_called = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
test_instance_commit_edit_part2( Fixture *fixture, gconstpointer pData )
|
||||
{
|
||||
QofBackend *be;
|
||||
QofBook *book;
|
||||
gboolean result;
|
||||
|
||||
/* setup */
|
||||
be = g_new0( QofBackend, 1 );
|
||||
auto be = new MockBackend;
|
||||
g_assert( be );
|
||||
qof_backend_init( be );
|
||||
book = qof_book_new();
|
||||
g_assert( book );
|
||||
g_assert( QOF_IS_BOOK( book ) );
|
||||
@ -610,13 +613,13 @@ test_instance_commit_edit_part2( Fixture *fixture, gconstpointer pData )
|
||||
|
||||
/* init */
|
||||
result = FALSE;
|
||||
commit_test_part2.commit_called = FALSE;
|
||||
commit_test_part2.commit_with_err_called = FALSE;
|
||||
commit_test_part2.on_error_called = FALSE;
|
||||
commit_test_part2.on_free_called = FALSE;
|
||||
commit_test_part2.on_done_called = FALSE;
|
||||
commit_test_part2.inst = fixture->inst;
|
||||
commit_test_part2.be = be;
|
||||
commit_test.m_commit_called = false;
|
||||
commit_test.m_commit_with_err_called = false;
|
||||
commit_test.m_on_error_called = false;
|
||||
commit_test.m_on_free_called = false;
|
||||
commit_test.m_on_done_called = false;
|
||||
commit_test.m_inst = fixture->inst;
|
||||
commit_test.m_be = be;
|
||||
qof_instance_set_dirty_flag( fixture->inst, TRUE );
|
||||
|
||||
g_test_message( "Test when instance's backend not set, callbacks not set" );
|
||||
@ -626,70 +629,67 @@ test_instance_commit_edit_part2( Fixture *fixture, gconstpointer pData )
|
||||
g_assert( result );
|
||||
g_assert( qof_instance_get_dirty_flag( fixture->inst ) );
|
||||
g_assert( !qof_instance_get_infant( fixture->inst ) );
|
||||
g_assert( !commit_test_part2.commit_called );
|
||||
g_assert( !commit_test_part2.commit_with_err_called );
|
||||
g_assert( !commit_test_part2.on_error_called );
|
||||
g_assert( !commit_test_part2.on_free_called );
|
||||
g_assert( !commit_test_part2.on_done_called );
|
||||
g_assert( !commit_test.m_commit_called );
|
||||
g_assert( !commit_test.m_commit_with_err_called );
|
||||
g_assert( !commit_test.m_on_error_called );
|
||||
g_assert( !commit_test.m_on_free_called );
|
||||
g_assert( !commit_test.m_on_done_called );
|
||||
|
||||
g_test_message( "Test when instance's backend not set, do_free is true" );
|
||||
g_test_message( "Test when instance's backend not set, do_free is false" );
|
||||
qof_instance_set_destroying( fixture->inst, TRUE );
|
||||
result = qof_commit_edit_part2( fixture->inst, mock_on_error, mock_on_done, mock_on_free );
|
||||
result = qof_commit_edit_part2( fixture->inst, on_error, on_done, on_free );
|
||||
g_assert( result );
|
||||
g_assert( qof_instance_get_dirty_flag( fixture->inst ) );
|
||||
g_assert( !commit_test_part2.commit_called );
|
||||
g_assert( !commit_test_part2.commit_with_err_called );
|
||||
g_assert( !commit_test_part2.on_error_called );
|
||||
g_assert( commit_test_part2.on_free_called );
|
||||
g_assert( !commit_test_part2.on_done_called );
|
||||
g_assert( !commit_test.m_commit_called );
|
||||
g_assert( !commit_test.m_commit_with_err_called );
|
||||
g_assert( !commit_test.m_on_error_called );
|
||||
g_assert( commit_test.m_on_free_called );
|
||||
g_assert( !commit_test.m_on_done_called );
|
||||
|
||||
g_test_message( "Test when instance's backend not set, do_free is false" );
|
||||
qof_instance_set_destroying( fixture->inst, FALSE );
|
||||
commit_test_part2.on_free_called = FALSE;
|
||||
result = qof_commit_edit_part2( fixture->inst, mock_on_error, mock_on_done, mock_on_free );
|
||||
commit_test.m_on_free_called = false;
|
||||
result = qof_commit_edit_part2( fixture->inst, on_error, on_done, on_free );
|
||||
g_assert( result );
|
||||
g_assert( qof_instance_get_dirty_flag( fixture->inst ) );
|
||||
g_assert( !commit_test_part2.commit_called );
|
||||
g_assert( !commit_test_part2.commit_with_err_called );
|
||||
g_assert( !commit_test_part2.on_error_called );
|
||||
g_assert( !commit_test_part2.on_free_called );
|
||||
g_assert( commit_test_part2.on_done_called );
|
||||
g_assert( !commit_test.m_commit_called );
|
||||
g_assert( !commit_test.m_commit_with_err_called );
|
||||
g_assert( !commit_test.m_on_error_called );
|
||||
g_assert( !commit_test.m_on_free_called );
|
||||
g_assert( commit_test.m_on_done_called );
|
||||
|
||||
g_test_message( "Test when instance's backend is set, all cb set, no error produced" );
|
||||
qof_instance_set_book( fixture->inst, book );
|
||||
qof_instance_set_destroying( fixture->inst, FALSE );
|
||||
commit_test_part2.on_done_called = FALSE;
|
||||
be->commit = mock_backend_commit;
|
||||
result = qof_commit_edit_part2( fixture->inst, mock_on_error, mock_on_done, mock_on_free );
|
||||
commit_test.m_on_done_called = false;
|
||||
result = qof_commit_edit_part2( fixture->inst, on_error, on_done, on_free );
|
||||
g_assert( result );
|
||||
g_assert( !qof_instance_get_dirty_flag( fixture->inst ) );
|
||||
g_assert( commit_test_part2.commit_called );
|
||||
g_assert( !commit_test_part2.commit_with_err_called );
|
||||
g_assert( !commit_test_part2.on_error_called );
|
||||
g_assert( !commit_test_part2.on_free_called );
|
||||
g_assert( commit_test_part2.on_done_called );
|
||||
g_assert( commit_test.m_commit_called );
|
||||
g_assert( !commit_test.m_commit_with_err_called );
|
||||
g_assert( !commit_test.m_on_error_called );
|
||||
g_assert( !commit_test.m_on_free_called );
|
||||
g_assert( commit_test.m_on_done_called );
|
||||
|
||||
g_test_message( "Test when instance's backend is set, all cb set, error produced" );
|
||||
commit_test_part2.commit_called = FALSE;
|
||||
commit_test_part2.on_done_called = FALSE;
|
||||
be->commit = mock_backend_commit_with_error;
|
||||
commit_test.m_commit_called = false;
|
||||
commit_test.m_on_done_called = false;
|
||||
be->inject_error(ERR_BACKEND_NO_HANDLER);
|
||||
qof_instance_set_dirty_flag( fixture->inst, TRUE );
|
||||
qof_instance_set_destroying( fixture->inst, TRUE );
|
||||
result = qof_commit_edit_part2( fixture->inst, mock_on_error, mock_on_done, mock_on_free );
|
||||
result = qof_commit_edit_part2( fixture->inst, on_error, on_done, on_free );
|
||||
g_assert( !result );
|
||||
g_assert( qof_instance_get_dirty_flag( fixture->inst ) );
|
||||
g_assert( !qof_instance_get_destroying( fixture->inst ) );
|
||||
g_assert( !commit_test_part2.commit_called );
|
||||
g_assert( commit_test_part2.commit_with_err_called );
|
||||
g_assert( commit_test_part2.on_error_called );
|
||||
g_assert( !commit_test_part2.on_free_called );
|
||||
g_assert( !commit_test_part2.on_done_called );
|
||||
g_assert( commit_test.m_commit_called );
|
||||
g_assert( commit_test.m_on_error_called );
|
||||
g_assert( !commit_test.m_on_free_called );
|
||||
g_assert( !commit_test.m_on_done_called );
|
||||
|
||||
/* clean up */
|
||||
qof_book_set_backend( book, NULL );
|
||||
qof_book_destroy( book );
|
||||
qof_backend_destroy( be );
|
||||
g_free( be );
|
||||
delete be;
|
||||
}
|
||||
|
||||
/* backend commit test end */
|
||||
|
@ -35,51 +35,51 @@ static bool sync_called {false};
|
||||
static bool load_error {true};
|
||||
static bool hook_called {false};
|
||||
static bool data_loaded {false};
|
||||
|
||||
class MockBackend : public QofBackend
|
||||
{
|
||||
public:
|
||||
MockBackend() = default;
|
||||
MockBackend(const MockBackend&) = delete;
|
||||
MockBackend(const MockBackend&&) = delete;
|
||||
virtual ~MockBackend() = default;
|
||||
void session_begin(QofSession*, const char*, bool, bool, bool) {}
|
||||
void session_end() {}
|
||||
void load(QofBook*, QofBackendLoadType);
|
||||
void sync(QofBook*);
|
||||
void safe_sync(QofBook*);
|
||||
void export_coa(QofBook*);
|
||||
};
|
||||
|
||||
void example_hook (QofSession & session)
|
||||
{
|
||||
hook_called = true;
|
||||
}
|
||||
|
||||
void test_load (QofBackend * be, QofBook *, QofBackendLoadType)
|
||||
void MockBackend::load (QofBook *, QofBackendLoadType)
|
||||
{
|
||||
if (load_error) be->last_err = ERR_BACKEND_NO_BACKEND;
|
||||
if (load_error) set_error(ERR_BACKEND_NO_BACKEND);
|
||||
data_loaded = true;
|
||||
}
|
||||
|
||||
void test_safe_sync (QofBackend *, QofBook *)
|
||||
void MockBackend::safe_sync (QofBook *)
|
||||
{
|
||||
safe_sync_called = true;
|
||||
}
|
||||
|
||||
void test_sync (QofBackend *, QofBook *)
|
||||
void MockBackend::sync (QofBook *)
|
||||
{
|
||||
sync_called = true;
|
||||
}
|
||||
|
||||
void test_export_fn (QofBackend *, QofBook * book)
|
||||
void MockBackend::export_coa(QofBook * book)
|
||||
{
|
||||
exported_book = book;
|
||||
}
|
||||
|
||||
QofBackend * test_backend_factory ()
|
||||
{
|
||||
QofBackend * ret = (QofBackend*) std::malloc (sizeof (QofBackend));
|
||||
ret->session_begin = nullptr;
|
||||
ret->session_end = nullptr;
|
||||
ret->destroy_backend = nullptr;
|
||||
ret->load = &test_load;
|
||||
ret->sync = &test_sync;
|
||||
ret->safe_sync = &test_safe_sync;
|
||||
ret->export_fn = &test_export_fn;
|
||||
ret->error_msg = nullptr;
|
||||
ret->fullpath = nullptr;
|
||||
ret->last_err = ERR_BACKEND_NO_ERR;
|
||||
ret->begin = nullptr;
|
||||
ret->commit = nullptr;
|
||||
ret->rollback = nullptr;
|
||||
ret->percentage = nullptr;
|
||||
ret->config_count = 0;
|
||||
return ret;
|
||||
return new MockBackend;
|
||||
}
|
||||
|
||||
struct MockProvider : public QofBackendProvider
|
||||
|
Loading…
Reference in New Issue
Block a user