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 );
|
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( "" );
|
LEAVE( "" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,6 +114,7 @@ int main (int argc, char ** argv)
|
|||||||
test_dbi_store_and_reload( "sqlite3", session_1, filename );
|
test_dbi_store_and_reload( "sqlite3", session_1, filename );
|
||||||
session_1 = create_session();
|
session_1 = create_session();
|
||||||
test_dbi_safe_save( "sqlite3", filename );
|
test_dbi_safe_save( "sqlite3", filename );
|
||||||
|
test_dbi_version_control( "sqlite3", filename );
|
||||||
#ifdef TEST_MYSQL_URL
|
#ifdef TEST_MYSQL_URL
|
||||||
printf( "TEST_MYSQL_URL='%s'\n", TEST_MYSQL_URL );
|
printf( "TEST_MYSQL_URL='%s'\n", TEST_MYSQL_URL );
|
||||||
if ( strlen( TEST_MYSQL_URL ) > 0 )
|
if ( strlen( TEST_MYSQL_URL ) > 0 )
|
||||||
@ -121,7 +122,8 @@ int main (int argc, char ** argv)
|
|||||||
session_1 = create_session();
|
session_1 = create_session();
|
||||||
test_dbi_store_and_reload( "mysql", session_1, TEST_MYSQL_URL );
|
test_dbi_store_and_reload( "mysql", session_1, TEST_MYSQL_URL );
|
||||||
session_1 = create_session();
|
session_1 = create_session();
|
||||||
test_dbi_safe_save( "msql", filename );
|
test_dbi_safe_save( "mysql", filename );
|
||||||
|
test_dbi_version_control( "mysql", filename );
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef TEST_PGSQL_URL
|
#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 );
|
test_dbi_store_and_reload( "pgsql", session_1, TEST_PGSQL_URL );
|
||||||
session_1 = create_session();
|
session_1 = create_session();
|
||||||
test_dbi_safe_save( "pgsql", filename );
|
test_dbi_safe_save( "pgsql", filename );
|
||||||
|
test_dbi_version_control( "pgsql", filename );
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
print_test_results();
|
print_test_results();
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "qof.h"
|
#include "qof.h"
|
||||||
|
#include "qofsession-p.h"
|
||||||
#include "cashobjects.h"
|
#include "cashobjects.h"
|
||||||
#include "test-engine-stuff.h"
|
#include "test-engine-stuff.h"
|
||||||
#include "test-stuff.h"
|
#include "test-stuff.h"
|
||||||
@ -159,7 +160,9 @@ compare_books( QofBook* book_1, QofBook* book_2 )
|
|||||||
test_conn_get_index_list( be );
|
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
|
void
|
||||||
test_dbi_store_and_reload( const gchar* driver, QofSession* session_1, const gchar* url )
|
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 );
|
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
|
void
|
||||||
test_dbi_safe_save( const gchar* driver, const gchar* url )
|
test_dbi_safe_save( const gchar* driver, const gchar* url )
|
||||||
{
|
{
|
||||||
@ -261,3 +271,64 @@ cleanup:
|
|||||||
qof_session_destroy( session_1 );
|
qof_session_destroy( session_1 );
|
||||||
return;
|
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 );
|
(void)reset_version_info( be );
|
||||||
gnc_sql_set_table_version( be, "Gnucash", gnc_get_svn_version() );
|
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 */
|
/* Create new tables */
|
||||||
be->is_pristine_db = TRUE;
|
be->is_pristine_db = TRUE;
|
||||||
@ -538,6 +539,7 @@ gnc_sql_sync_all( GncSqlBackend* be, /*@ dependent @*/ QofBook *book )
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
qof_backend_set_error( (QofBackend*)be, ERR_BACKEND_SERVER_ERR );
|
||||||
is_ok = gnc_sql_connection_rollback_transaction( be->conn );
|
is_ok = gnc_sql_connection_rollback_transaction( be->conn );
|
||||||
}
|
}
|
||||||
LEAVE( "book=%p", book );
|
LEAVE( "book=%p", book );
|
||||||
|
@ -43,6 +43,25 @@
|
|||||||
#include "qofbackend-p.h"
|
#include "qofbackend-p.h"
|
||||||
#include <gmodule.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;
|
typedef struct GncSqlConnection GncSqlConnection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -152,7 +171,7 @@ struct GncSqlConnection
|
|||||||
GncSqlResult* (*executeSelectStatement)( GncSqlConnection*, GncSqlStatement* ); /**< Returns NULL if error */
|
GncSqlResult* (*executeSelectStatement)( GncSqlConnection*, GncSqlStatement* ); /**< Returns NULL if error */
|
||||||
gint (*executeNonSelectStatement)( GncSqlConnection*, GncSqlStatement* ); /**< Returns -1 if error */
|
gint (*executeNonSelectStatement)( GncSqlConnection*, GncSqlStatement* ); /**< Returns -1 if error */
|
||||||
GncSqlStatement* (*createStatementFromSql)( /*@ observer @*/ GncSqlConnection*, const gchar* );
|
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 (*beginTransaction)( GncSqlConnection* ); /**< Returns TRUE if successful, FALSE if error */
|
||||||
gboolean (*rollbackTransaction)( 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 */
|
gboolean (*commitTransaction)( GncSqlConnection* ); /**< Returns TRUE if successful, FALSE if error */
|
||||||
|
Loading…
Reference in New Issue
Block a user