mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-02-06 20:45:54 +01:00
add DB versioning for linux
This commit is contained in:
parent
b51e3d8d1e
commit
22f62d53e0
1 changed files with 151 additions and 36 deletions
|
@ -23,18 +23,43 @@
|
|||
#include "gtkdraw.h"
|
||||
#include "linuxutl.h"
|
||||
#include "main.h"
|
||||
#include "dbgutil.h"
|
||||
|
||||
#define SNAP_WIDTH 150
|
||||
#define SNAP_HEIGHT 150
|
||||
#define KEY_DB_VERSION "dbvers"
|
||||
|
||||
#define VERS_0_TO_1 \
|
||||
"nPending INT" \
|
||||
",role INT" \
|
||||
",dictlang INT" \
|
||||
",scores TEXT" \
|
||||
|
||||
|
||||
static XP_Bool getColumnText( sqlite3_stmt *ppStmt, int iCol, XP_UCHAR* buf,
|
||||
int* len );
|
||||
static bool db_fetchInt( sqlite3* pDb, const gchar* key, int32_t* resultP );
|
||||
static void db_storeInt( sqlite3* pDb, const gchar* key, int32_t val );
|
||||
static void createTables( sqlite3* pDb );
|
||||
static bool gamesTableExists( sqlite3* pDb );
|
||||
static void upgradeTables( sqlite3* pDb, int32_t oldVersion );
|
||||
static void execNoResult( sqlite3* pDb, const gchar* query, bool errOK );
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
static char* sqliteErr2str( int err );
|
||||
#endif
|
||||
static void assertPrintResult( sqlite3* pDb, int result, int expect );
|
||||
|
||||
/* Versioning:
|
||||
*
|
||||
* Version 0 is defined, and not having a version code means you're 0. For
|
||||
* each subsequent version there needs to be a recipe for upgrading, whether
|
||||
* it's adding new fields or whatever.
|
||||
*/
|
||||
|
||||
#define CUR_DB_VERSION 1
|
||||
|
||||
sqlite3*
|
||||
openGamesDB( const char* dbName )
|
||||
{
|
||||
|
@ -45,6 +70,79 @@ openGamesDB( const char* dbName )
|
|||
result = sqlite3_open( dbName, &pDb );
|
||||
XP_ASSERT( SQLITE_OK == result );
|
||||
|
||||
if ( gamesTableExists( pDb ) ) {
|
||||
int32_t oldVersion;
|
||||
if ( !db_fetchInt( pDb, KEY_DB_VERSION, &oldVersion ) ) {
|
||||
oldVersion = 0;
|
||||
XP_LOGFF( "no version found; assuming %d", oldVersion );
|
||||
}
|
||||
if ( oldVersion < CUR_DB_VERSION ) {
|
||||
upgradeTables( pDb, oldVersion );
|
||||
}
|
||||
} else {
|
||||
createTables( pDb );
|
||||
}
|
||||
|
||||
return pDb;
|
||||
}
|
||||
|
||||
static void
|
||||
upgradeTables( sqlite3* pDb, int32_t oldVersion )
|
||||
{
|
||||
gchar* newCols = NULL;
|
||||
switch ( oldVersion ) {
|
||||
case 0:
|
||||
XP_ASSERT( 1 == CUR_DB_VERSION );
|
||||
newCols = VERS_0_TO_1;
|
||||
break;
|
||||
default:
|
||||
XP_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
||||
if ( !!newCols ) {
|
||||
gchar** strs = g_strsplit( newCols, ",", -1 );
|
||||
for ( int ii = 0; !!strs[ii]; ++ii ) {
|
||||
gchar* str = strs[ii];
|
||||
if ( 0 < strlen(str) ) {
|
||||
gchar* query = g_strdup_printf( "ALTER TABLE games ADD COLUMN %s", strs[ii] );
|
||||
XP_LOGFF( "query: \"%s\"", query );
|
||||
execNoResult( pDb, query, true );
|
||||
g_free( query );
|
||||
}
|
||||
}
|
||||
g_strfreev( strs );
|
||||
|
||||
db_storeInt( pDb, KEY_DB_VERSION, CUR_DB_VERSION );
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
gamesTableExists( sqlite3* pDb )
|
||||
{
|
||||
const gchar* query = "SELECT COUNT(*) FROM sqlite_master "
|
||||
"WHERE type = 'table' AND name = 'games'";
|
||||
|
||||
sqlite3_stmt *ppStmt;
|
||||
int result = sqlite3_prepare_v2( pDb, query, -1, &ppStmt, NULL );
|
||||
assertPrintResult( pDb, result, SQLITE_OK );
|
||||
result = sqlite3_step( ppStmt );
|
||||
XP_ASSERT( SQLITE_ROW == result );
|
||||
bool exists = 1 == sqlite3_column_int( ppStmt, 0 );
|
||||
sqlite3_finalize( ppStmt );
|
||||
LOG_RETURNF( "%s", boolToStr(exists) );
|
||||
return exists;
|
||||
}
|
||||
|
||||
static void
|
||||
createTables( sqlite3* pDb )
|
||||
{
|
||||
/* This can never change! Versioning counts on it. */
|
||||
const char* createValuesStr =
|
||||
"CREATE TABLE pairs ( key TEXT UNIQUE, value TEXT )";
|
||||
int result = sqlite3_exec( pDb, createValuesStr, NULL, NULL, NULL );
|
||||
XP_LOGFF( "sqlite3_exec(%s)=>%d", createValuesStr, result );
|
||||
|
||||
const char* createGamesStr =
|
||||
"CREATE TABLE games ( "
|
||||
"rowid INTEGER PRIMARY KEY AUTOINCREMENT"
|
||||
|
@ -59,25 +157,17 @@ openGamesDB( const char* dbName )
|
|||
",local INT(1)"
|
||||
",nmoves INT"
|
||||
",seed INT"
|
||||
",nPending INT"
|
||||
",role INT"
|
||||
",dictlang INT"
|
||||
",gameid INT"
|
||||
",ntotal INT(2)"
|
||||
",nmissing INT(2)"
|
||||
",lastMoveTime INT"
|
||||
",scores TEXT"
|
||||
",dupTimerExpires INT"
|
||||
","VERS_0_TO_1
|
||||
// ",dupTimerExpires INT"
|
||||
")";
|
||||
result = sqlite3_exec( pDb, createGamesStr, NULL, NULL, NULL );
|
||||
|
||||
const char* createValuesStr =
|
||||
"CREATE TABLE pairs ( key TEXT UNIQUE,value TEXT )";
|
||||
result = sqlite3_exec( pDb, createValuesStr, NULL, NULL, NULL );
|
||||
XP_LOGFF( "sqlite3_exec=>%d", result );
|
||||
XP_USE( result );
|
||||
|
||||
return pDb;
|
||||
db_storeInt( pDb, KEY_DB_VERSION, CUR_DB_VERSION );
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -558,30 +648,46 @@ deleteGame( sqlite3* pDb, sqlite3_int64 rowid )
|
|||
XP_ASSERT( !!pDb );
|
||||
char query[256];
|
||||
snprintf( query, sizeof(query), "DELETE FROM games WHERE rowid = %lld", rowid );
|
||||
sqlite3_stmt* ppStmt;
|
||||
int result = sqlite3_prepare_v2( pDb, query, -1, &ppStmt, NULL );
|
||||
assertPrintResult( pDb, result, SQLITE_OK );
|
||||
result = sqlite3_step( ppStmt );
|
||||
assertPrintResult( pDb, result, SQLITE_DONE );
|
||||
XP_USE( result );
|
||||
sqlite3_finalize( ppStmt );
|
||||
execNoResult( pDb, query, false );
|
||||
}
|
||||
|
||||
void
|
||||
db_store( sqlite3* pDb, const gchar* key, const gchar* value )
|
||||
{
|
||||
XP_ASSERT( !!pDb );
|
||||
gchar* buf =
|
||||
gchar* query =
|
||||
g_strdup_printf( "INSERT OR REPLACE INTO pairs (key, value) VALUES ('%s', '%s')",
|
||||
key, value );
|
||||
sqlite3_stmt *ppStmt;
|
||||
int result = sqlite3_prepare_v2( pDb, buf, -1, &ppStmt, NULL );
|
||||
assertPrintResult( pDb, result, SQLITE_OK );
|
||||
result = sqlite3_step( ppStmt );
|
||||
assertPrintResult( pDb, result, SQLITE_DONE );
|
||||
XP_USE( result );
|
||||
sqlite3_finalize( ppStmt );
|
||||
g_free( buf );
|
||||
execNoResult( pDb, query, false );
|
||||
g_free( query );
|
||||
}
|
||||
|
||||
static bool
|
||||
db_fetchInt( sqlite3* pDb, const gchar* key, int32_t* resultP )
|
||||
{
|
||||
gint buflen = 16;
|
||||
gchar buf[buflen];
|
||||
FetchResult fr = db_fetch( pDb, key, buf, &buflen );
|
||||
bool gotIt = SUCCESS == fr;
|
||||
if ( gotIt ) {
|
||||
buf[buflen] = '\0';
|
||||
sscanf( buf, "%x", resultP );
|
||||
}
|
||||
return gotIt;
|
||||
}
|
||||
|
||||
static void
|
||||
db_storeInt( sqlite3* pDb, const gchar* key, int32_t val )
|
||||
{
|
||||
gchar buf[32];
|
||||
snprintf( buf, VSIZE(buf), "%x", val );
|
||||
db_store( pDb, key, buf );
|
||||
|
||||
#ifdef DEBUG
|
||||
int32_t tmp;
|
||||
bool worked = db_fetchInt( pDb, key, &tmp );
|
||||
XP_ASSERT( worked && tmp == val );
|
||||
#endif
|
||||
}
|
||||
|
||||
FetchResult
|
||||
|
@ -632,13 +738,7 @@ db_remove( sqlite3* pDb, const gchar* key )
|
|||
XP_ASSERT( !!pDb );
|
||||
char query[256];
|
||||
snprintf( query, sizeof(query), "DELETE FROM pairs WHERE key = '%s'", key );
|
||||
sqlite3_stmt *ppStmt;
|
||||
int result = sqlite3_prepare_v2( pDb, query, -1, &ppStmt, NULL );
|
||||
assertPrintResult( pDb, result, SQLITE_OK );
|
||||
result = sqlite3_step( ppStmt );
|
||||
assertPrintResult( pDb, result, SQLITE_DONE );
|
||||
XP_USE( result );
|
||||
sqlite3_finalize( ppStmt );
|
||||
execNoResult( pDb, query, false );
|
||||
}
|
||||
|
||||
static XP_Bool
|
||||
|
@ -656,6 +756,21 @@ getColumnText( sqlite3_stmt *ppStmt, int iCol, XP_UCHAR* buf, int *len )
|
|||
return success;
|
||||
}
|
||||
|
||||
static void
|
||||
execNoResult( sqlite3* pDb, const gchar* query, bool errOk )
|
||||
{
|
||||
sqlite3_stmt *ppStmt;
|
||||
int result = sqlite3_prepare_v2( pDb, query, -1, &ppStmt, NULL );
|
||||
if ( ! errOk ) {
|
||||
assertPrintResult( pDb, result, SQLITE_OK );
|
||||
}
|
||||
result = sqlite3_step( ppStmt );
|
||||
if ( ! errOk ) {
|
||||
assertPrintResult( pDb, result, SQLITE_DONE );
|
||||
}
|
||||
sqlite3_finalize( ppStmt );
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
# define CASESTR(c) case c: return #c
|
||||
static char*
|
||||
|
@ -704,7 +819,7 @@ assertPrintResult( sqlite3* pDb, int XP_UNUSED_DBG(result), int expect )
|
|||
int code = sqlite3_errcode( pDb );
|
||||
XP_ASSERT( code == result ); /* do I need to pass it? */
|
||||
if ( code != expect ) {
|
||||
XP_LOGFF( "sqlite3 error: %s", sqlite3_errmsg( pDb ) );
|
||||
XP_LOGFF( "sqlite3 error: %d (%s)", code, sqlite3_errmsg( pDb ) );
|
||||
XP_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue