mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Enhanced database version handling.
* Add a macro GNC_RESAVE_VERSION which holds an svn revision number. * When fully saving a database put this number in versions with table_name Gnucash-Resave. * On database load, compare the current GNC_RESAVE_VERSION with the Gnucash revision and Gnucash-Resave revision saved when the database was created. * If the current GNC_RESAVE_VERSION > the saved Gnucash version, then emit ERR_SQL_DB_TOO_OLD. * If GNC_RESAVE_VERSION < the saved Gnucash-Resave, emit ERR_SQL_DB_TOO_NEW. git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@20123 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
parent
56f1e28e36
commit
8be9b0a9ad
@ -1190,6 +1190,24 @@ gnc_dbi_load( QofBackend* qbe, /*@ dependent @*/ QofBook *book, QofBackendLoadTy
|
||||
|
||||
gnc_sql_load( &be->sql_be, book, loadType );
|
||||
|
||||
if ( GNC_RESAVE_VERSION > gnc_sql_get_table_version( &be->sql_be, "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( qbe, ERR_SQL_DB_TOO_OLD );
|
||||
}
|
||||
else if ( GNC_RESAVE_VERSION < gnc_sql_get_table_version( &be->sql_be,
|
||||
"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( qbe, ERR_SQL_DB_TOO_NEW );
|
||||
}
|
||||
|
||||
|
||||
LEAVE( "" );
|
||||
}
|
||||
|
||||
|
@ -114,6 +114,7 @@ int main (int argc, char ** argv)
|
||||
test_dbi_store_and_reload( "sqlite3", session_1, filename );
|
||||
session_1 = create_session();
|
||||
test_dbi_safe_save( "sqlite3", filename );
|
||||
test_dbi_version_control( "sqlite3", filename );
|
||||
#ifdef TEST_MYSQL_URL
|
||||
printf( "TEST_MYSQL_URL='%s'\n", TEST_MYSQL_URL );
|
||||
if ( strlen( TEST_MYSQL_URL ) > 0 )
|
||||
@ -121,7 +122,8 @@ int main (int argc, char ** argv)
|
||||
session_1 = create_session();
|
||||
test_dbi_store_and_reload( "mysql", session_1, TEST_MYSQL_URL );
|
||||
session_1 = create_session();
|
||||
test_dbi_safe_save( "msql", filename );
|
||||
test_dbi_safe_save( "mysql", filename );
|
||||
test_dbi_version_control( "mysql", filename );
|
||||
}
|
||||
#endif
|
||||
#ifdef TEST_PGSQL_URL
|
||||
@ -132,6 +134,7 @@ int main (int argc, char ** argv)
|
||||
test_dbi_store_and_reload( "pgsql", session_1, TEST_PGSQL_URL );
|
||||
session_1 = create_session();
|
||||
test_dbi_safe_save( "pgsql", filename );
|
||||
test_dbi_version_control( "pgsql", filename );
|
||||
}
|
||||
#endif
|
||||
print_test_results();
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "qof.h"
|
||||
#include "qofsession-p.h"
|
||||
#include "cashobjects.h"
|
||||
#include "test-engine-stuff.h"
|
||||
#include "test-stuff.h"
|
||||
@ -159,7 +160,9 @@ compare_books( QofBook* book_1, QofBook* book_2 )
|
||||
test_conn_get_index_list( be );
|
||||
}
|
||||
|
||||
|
||||
/* Given a synthetic session, use the same logic as
|
||||
* QofSession::save_as to save it to a specified sql url, then load it
|
||||
* back and compare. */
|
||||
void
|
||||
test_dbi_store_and_reload( const gchar* driver, QofSession* session_1, const gchar* url )
|
||||
{
|
||||
@ -213,6 +216,13 @@ test_dbi_store_and_reload( const gchar* driver, QofSession* session_1, const gch
|
||||
qof_session_destroy( session_3 );
|
||||
}
|
||||
|
||||
/* Given an already-created url (yeah, bad testing practice: Should
|
||||
* start fresh from a synthetic session) load and safe-save it, then
|
||||
* load it again into a new session and compare the two. Since
|
||||
* safe-save is a more-or-less atomic function call, there's no way to
|
||||
* be sure that it's actually doing what it's supposed to without
|
||||
* running this test in a debugger and stopping in the middle of the
|
||||
* safe-save and inspecting the database. */
|
||||
void
|
||||
test_dbi_safe_save( const gchar* driver, const gchar* url )
|
||||
{
|
||||
@ -261,3 +271,64 @@ cleanup:
|
||||
qof_session_destroy( session_1 );
|
||||
return;
|
||||
}
|
||||
|
||||
/* Test the gnc_dbi_load logic that forces a newer database to be
|
||||
* opened read-only and an older one to be safe-saved. Again, it would
|
||||
* be better to do this starting from a fresh file, but instead we're
|
||||
* being lazy and using an existing one. */
|
||||
void
|
||||
test_dbi_version_control( const gchar* driver, const gchar* url )
|
||||
{
|
||||
|
||||
QofSession *sess;
|
||||
QofBook *book;
|
||||
QofBackend *qbe;
|
||||
QofBackendError err;
|
||||
gint ourversion = gnc_get_svn_version();
|
||||
|
||||
printf( "Testing safe save %s\n", driver );
|
||||
|
||||
// Load the session data
|
||||
sess = qof_session_new();
|
||||
qof_session_begin( sess, url, TRUE, FALSE, FALSE );
|
||||
if (sess && qof_session_get_error(sess) != ERR_BACKEND_NO_ERR)
|
||||
{
|
||||
g_warning("Session Error: %d, %s", qof_session_get_error(sess),
|
||||
qof_session_get_error_message(sess));
|
||||
do_test( FALSE, "DB Session Creation Failed");
|
||||
goto cleanup;
|
||||
}
|
||||
qof_session_load( sess, NULL );
|
||||
qbe = qof_session_get_backend( sess );
|
||||
book = qof_session_get_book( sess );
|
||||
qof_book_begin_edit( book );
|
||||
gnc_sql_set_table_version( (GncSqlBackend*)qbe,
|
||||
"Gnucash", GNC_RESAVE_VERSION - 1 );
|
||||
qof_book_commit_edit( book );
|
||||
qof_session_end( sess );
|
||||
qof_session_destroy( sess );
|
||||
sess = qof_session_new();
|
||||
qof_session_begin( sess, url, TRUE, FALSE, FALSE );
|
||||
qof_session_load( sess, NULL );
|
||||
err = qof_session_pop_error( sess );
|
||||
do_test( err == ERR_SQL_DB_TOO_OLD, "DB Failed to flag too old" );
|
||||
qbe = qof_session_get_backend( sess );
|
||||
book = qof_session_get_book( sess );
|
||||
qof_book_begin_edit( book );
|
||||
gnc_sql_set_table_version( (GncSqlBackend*)qbe,
|
||||
"Gnucash", ourversion );
|
||||
gnc_sql_set_table_version( (GncSqlBackend*)qbe,
|
||||
"Gnucash-Resave", ourversion + 1 );
|
||||
qof_book_commit_edit( book );
|
||||
qof_session_end( sess );
|
||||
qof_session_destroy( sess );
|
||||
sess = qof_session_new();
|
||||
qof_session_begin( sess, url, TRUE, FALSE, FALSE );
|
||||
qof_session_load( sess, NULL );
|
||||
qof_session_ensure_all_data_loaded( sess );
|
||||
err = qof_session_pop_error( sess );
|
||||
do_test( err == ERR_SQL_DB_TOO_NEW, "DB Failed to flag too new" );
|
||||
cleanup:
|
||||
qof_session_end( sess );
|
||||
qof_session_destroy( sess );
|
||||
}
|
||||
|
@ -485,6 +485,7 @@ gnc_sql_sync_all( GncSqlBackend* be, /*@ dependent @*/ QofBook *book )
|
||||
|
||||
(void)reset_version_info( be );
|
||||
gnc_sql_set_table_version( be, "Gnucash", gnc_get_svn_version() );
|
||||
gnc_sql_set_table_version( be, "Gnucash-Resave", GNC_RESAVE_VERSION );
|
||||
|
||||
/* Create new tables */
|
||||
be->is_pristine_db = TRUE;
|
||||
@ -538,6 +539,7 @@ gnc_sql_sync_all( GncSqlBackend* be, /*@ dependent @*/ QofBook *book )
|
||||
}
|
||||
else
|
||||
{
|
||||
qof_backend_set_error( (QofBackend*)be, ERR_BACKEND_SERVER_ERR );
|
||||
is_ok = gnc_sql_connection_rollback_transaction( be->conn );
|
||||
}
|
||||
LEAVE( "book=%p", book );
|
||||
|
@ -43,6 +43,25 @@
|
||||
#include "qofbackend-p.h"
|
||||
#include <gmodule.h>
|
||||
|
||||
/**
|
||||
* \def GNC_RESAVE_VERSION
|
||||
*
|
||||
* Defines the oldest svn revision of Gnucash which stores data in a
|
||||
* way compatible with the current version. Data stored with an older
|
||||
* version (or with no version indicated) of Gnucash will cause all
|
||||
* tables to be moved aside, new tables saved with the current storage
|
||||
* routines, and the old tables dropped. Any failures will trigger a
|
||||
* rollback to the original tables.
|
||||
*
|
||||
* Encountering a database with a newer resave version will put the
|
||||
* database in "read only" mode; a "save as" will be required to
|
||||
* obtain a new database for storing from this instance, and the user
|
||||
* will be warned of data loss.
|
||||
*
|
||||
*/
|
||||
|
||||
#define GNC_RESAVE_VERSION 19920
|
||||
|
||||
typedef struct GncSqlConnection GncSqlConnection;
|
||||
|
||||
/**
|
||||
@ -152,7 +171,7 @@ struct GncSqlConnection
|
||||
GncSqlResult* (*executeSelectStatement)( GncSqlConnection*, GncSqlStatement* ); /**< Returns NULL if error */
|
||||
gint (*executeNonSelectStatement)( GncSqlConnection*, GncSqlStatement* ); /**< Returns -1 if error */
|
||||
GncSqlStatement* (*createStatementFromSql)( /*@ observer @*/ GncSqlConnection*, const gchar* );
|
||||
gboolean (*doesTableExist)( GncSqlConnection*, const gchar* );
|
||||
gboolean (*doesTableExist)( GncSqlConnection*, const gchar* ); /**< Returns true if successful */
|
||||
gboolean (*beginTransaction)( GncSqlConnection* ); /**< Returns TRUE if successful, FALSE if error */
|
||||
gboolean (*rollbackTransaction)( GncSqlConnection* ); /**< Returns TRUE if successful, FALSE if error */
|
||||
gboolean (*commitTransaction)( GncSqlConnection* ); /**< Returns TRUE if successful, FALSE if error */
|
||||
|
Loading…
Reference in New Issue
Block a user