diff --git a/ext/ota/ota1.test b/ext/ota/ota1.test index 9903d9216e..97fd2a3f95 100644 --- a/ext/ota/ota1.test +++ b/ext/ota/ota1.test @@ -129,6 +129,25 @@ proc step_ota_uri {target ota} { set rc } +# Same as [step_ota], except using an external state database - "state.db" +# +proc step_ota_state {target ota} { + while 1 { + sqlite3ota ota $target $ota state.db + set rc [ota step] + ota close + if {$rc != "SQLITE_OK"} break + } + set rc +} + +proc dbfilecksum {file} { + sqlite3 ck $file + set cksum [dbcksum ck main] + ck close + set cksum +} + foreach {tn3 create_vfs destroy_vfs} { 1 {} {} 2 { @@ -140,7 +159,10 @@ foreach {tn3 create_vfs destroy_vfs} { eval $create_vfs - foreach {tn2 cmd} {1 run_ota 2 step_ota 3 step_ota_uri} { + foreach {tn2 cmd} { + 1 run_ota + 2 step_ota 3 step_ota_uri 4 step_ota_state + } { foreach {tn schema} { 1 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c); @@ -218,9 +240,11 @@ foreach {tn3 create_vfs destroy_vfs} { } { reset_db execsql $schema + create_ota1 ota.db + set check [dbfilecksum ota.db] + forcedelete state.db do_test $tn3.1.$tn2.$tn.1 { - create_ota1 ota.db $cmd test.db ota.db } {SQLITE_DONE} @@ -241,6 +265,14 @@ foreach {tn3 create_vfs destroy_vfs} { } do_execsql_test $tn3.1.$tn2.$tn.5 { PRAGMA integrity_check } ok + + if {$cmd=="step_ota_state"} { + do_test $tn3.1.$tn2.$tn.6 { file exists state.db } 1 + do_test $tn3.1.$tn2.$tn.7 { expr {$check == [dbfilecksum ota.db]} } 1 + } else { + do_test $tn3.1.$tn2.$tn.8 { file exists state.db } 0 + do_test $tn3.1.$tn2.$tn.9 { expr {$check == [dbfilecksum ota.db]} } 0 + } } } @@ -325,7 +357,7 @@ foreach {tn3 create_vfs destroy_vfs} { #------------------------------------------------------------------------- # - foreach {tn2 cmd} {1 run_ota 2 step_ota} { + foreach {tn2 cmd} {1 run_ota 2 step_ota 3 step_ota_state } { foreach {tn schema} { 1 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c); @@ -373,9 +405,12 @@ foreach {tn3 create_vfs destroy_vfs} { INSERT INTO t1 VALUES(4, 'hello', 'planet'); INSERT INTO t1 VALUES(6, 'hello', 'xyz'); } + + create_ota4 ota.db + set check [dbfilecksum ota.db] + forcedelete state.db do_test $tn3.4.$tn2.$tn.1 { - create_ota4 ota.db $cmd test.db ota.db } {SQLITE_DONE} @@ -388,10 +423,18 @@ foreach {tn3 create_vfs destroy_vfs} { } do_execsql_test $tn3.4.$tn2.$tn.3 { PRAGMA integrity_check } ok + + if {$cmd=="step_ota_state"} { + do_test $tn3.4.$tn2.$tn.4 { file exists state.db } 1 + do_test $tn3.4.$tn2.$tn.5 { expr {$check == [dbfilecksum ota.db]} } 1 + } else { + do_test $tn3.4.$tn2.$tn.6 { file exists state.db } 0 + do_test $tn3.4.$tn2.$tn.7 { expr {$check == [dbfilecksum ota.db]} } 0 + } } } - foreach {tn2 cmd} {1 run_ota 2 step_ota} { + foreach {tn2 cmd} {1 run_ota 2 step_ota 3 step_ota_state} { foreach {tn schema} { 1 { CREATE TABLE t1(c, b, '(a)' INTEGER PRIMARY KEY); @@ -411,13 +454,16 @@ foreach {tn3 create_vfs destroy_vfs} { INSERT INTO t1('(a)', b, c) VALUES(4, 'hello', 'planet'); INSERT INTO t1('(a)', b, c) VALUES(6, 'hello', 'xyz'); } + + create_ota4b ota.db + set check [dbfilecksum ota.db] + forcedelete state.db - do_test $tn3.4.$tn2.$tn.1 { - create_ota4b ota.db + do_test $tn3.5.$tn2.$tn.1 { $cmd test.db ota.db } {SQLITE_DONE} - do_execsql_test $tn3.4.$tn2.$tn.2 { + do_execsql_test $tn3.5.$tn2.$tn.2 { SELECT * FROM t1 ORDER BY "(a)" ASC; } { 3 2 1 @@ -426,12 +472,20 @@ foreach {tn3 create_vfs destroy_vfs} { } do_execsql_test $tn3.4.$tn2.$tn.3 { PRAGMA integrity_check } ok + + if {$cmd=="step_ota_state"} { + do_test $tn3.5.$tn2.$tn.4 { file exists state.db } 1 + do_test $tn3.5.$tn2.$tn.5 { expr {$check == [dbfilecksum ota.db]} } 1 + } else { + do_test $tn3.5.$tn2.$tn.6 { file exists state.db } 0 + do_test $tn3.5.$tn2.$tn.7 { expr {$check == [dbfilecksum ota.db]} } 0 + } } } #------------------------------------------------------------------------- # - foreach {tn2 cmd} {1 run_ota 2 step_ota} { + foreach {tn2 cmd} {1 run_ota 2 step_ota 3 step_ota_state} { foreach {tn schema} { 1 { CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d); @@ -472,8 +526,11 @@ foreach {tn3 create_vfs destroy_vfs} { INSERT INTO t1 VALUES(3, 8, 9, 10); } + create_ota5 ota.db + set check [dbfilecksum ota.db] + forcedelete state.db + do_test $tn3.5.$tn2.$tn.1 { - create_ota5 ota.db $cmd test.db ota.db } {SQLITE_DONE} @@ -485,7 +542,15 @@ foreach {tn3 create_vfs destroy_vfs} { 3 11 9 10 } - do_execsql_test $tn3.5.$tn2.$tn.3 { PRAGMA integrity_check } ok + do_execsql_test $tn3.6.$tn2.$tn.3 { PRAGMA integrity_check } ok + + if {$cmd=="step_ota_state"} { + do_test $tn3.6.$tn2.$tn.4 { file exists state.db } 1 + do_test $tn3.6.$tn2.$tn.5 { expr {$check == [dbfilecksum ota.db]} } 1 + } else { + do_test $tn3.6.$tn2.$tn.6 { file exists state.db } 0 + do_test $tn3.6.$tn2.$tn.7 { expr {$check == [dbfilecksum ota.db]} } 0 + } } } @@ -566,7 +631,7 @@ foreach {tn3 create_vfs destroy_vfs} { execsql { ATTACH 'ota.db' AS ota } execsql $schema - do_test $tn3.6.$tn { + do_test $tn3.7.$tn { list [catch { run_ota test.db ota.db } msg] $msg } [list 1 $error] } @@ -576,7 +641,7 @@ foreach {tn3 create_vfs destroy_vfs} { # correctly. reset_db forcedelete ota.db - do_test $tn3.7 { + do_test $tn3.8 { list [catch { run_ota test.db ota.db } msg] $msg } {0 SQLITE_DONE} @@ -584,7 +649,7 @@ foreach {tn3 create_vfs destroy_vfs} { # reset_db forcedelete ota.db - do_execsql_test $tn3.8.1 { + do_execsql_test $tn3.9.1 { CREATE TABLE t1(a PRIMARY KEY, b, c); CREATE INDEX i1 ON t1(b, c); INSERT INTO t1 VALUES(1, 1, NULL); @@ -597,14 +662,14 @@ foreach {tn3 create_vfs destroy_vfs} { INSERT INTO data_t1 VALUES(3, NULL, NULL, 1); } {} - do_test $tn3.8.2 { + do_test $tn3.9.2 { list [catch { run_ota test.db ota.db } msg] $msg } {0 SQLITE_DONE} - do_execsql_test $tn3.8.3 { + do_execsql_test $tn3.9.3 { SELECT * FROM t1 } {2 {} 2} - do_execsql_test $tn3.8.4 { PRAGMA integrity_check } {ok} + do_execsql_test $tn3.9.4 { PRAGMA integrity_check } {ok} catch { db close } eval $destroy_vfs diff --git a/ext/ota/sqlite3ota.c b/ext/ota/sqlite3ota.c index fb798b7607..e365de4fe3 100644 --- a/ext/ota/sqlite3ota.c +++ b/ext/ota/sqlite3ota.c @@ -160,8 +160,8 @@ #define OTA_STAGE_DONE 5 -#define OTA_CREATE_STATE "CREATE TABLE IF NOT EXISTS ota_state" \ - "(k INTEGER PRIMARY KEY, v)" +#define OTA_CREATE_STATE \ + "CREATE TABLE IF NOT EXISTS %s.ota_state(k INTEGER PRIMARY KEY, v)" typedef struct OtaFrame OtaFrame; typedef struct OtaObjIter OtaObjIter; @@ -299,6 +299,8 @@ struct sqlite3ota { sqlite3 *dbOta; /* ota database handle */ char *zTarget; /* Path to target db */ char *zOta; /* Path to ota db */ + char *zState; /* Path to state db (or NULL if zOta) */ + char zStateDb[5]; /* Db name for state ("stat" or "main") */ int rc; /* Value returned by last ota_step() call */ char *zErrmsg; /* Error message if rc!=SQLITE_OK */ int nStep; /* Rows processed for current object */ @@ -1512,8 +1514,8 @@ static void otaObjIterPrepareTmpInsert( assert( pIter->pTmpInsert==0 ); p->rc = prepareFreeAndCollectError( p->dbOta, &pIter->pTmpInsert, &p->zErrmsg, sqlite3_mprintf( - "INSERT INTO 'ota_tmp_%q'(ota_control,%s%s) VALUES(%z)", - pIter->zTbl, zCollist, zOtaRowid, zBind + "INSERT INTO %s.'ota_tmp_%q'(ota_control,%s%s) VALUES(%z)", + p->zStateDb, pIter->zTbl, zCollist, zOtaRowid, zBind )); } } @@ -1608,8 +1610,8 @@ static int otaObjIterPrepareAll( char *zSql; if( pIter->eType==OTA_PK_EXTERNAL || pIter->eType==OTA_PK_NONE ){ zSql = sqlite3_mprintf( - "SELECT %s, ota_control FROM 'ota_tmp_%q' ORDER BY %s%s", - zCollist, pIter->zTbl, + "SELECT %s, ota_control FROM %s.'ota_tmp_%q' ORDER BY %s%s", + zCollist, p->zStateDb, pIter->zTbl, zCollist, zLimit ); }else{ @@ -1617,10 +1619,10 @@ static int otaObjIterPrepareAll( "SELECT %s, ota_control FROM 'data_%q' " "WHERE typeof(ota_control)='integer' AND ota_control!=1 " "UNION ALL " - "SELECT %s, ota_control FROM 'ota_tmp_%q' " + "SELECT %s, ota_control FROM %s.'ota_tmp_%q' " "ORDER BY %s%s", zCollist, pIter->zTbl, - zCollist, pIter->zTbl, + zCollist, p->zStateDb, pIter->zTbl, zCollist, zLimit ); } @@ -1686,8 +1688,9 @@ static int otaObjIterPrepareAll( /* Create the ota_tmp_xxx table and the triggers to populate it. */ otaMPrintfExec(p, p->dbOta, - "CREATE TABLE IF NOT EXISTS 'ota_tmp_%q' AS " + "CREATE TABLE IF NOT EXISTS %s.'ota_tmp_%q' AS " "SELECT *%s FROM 'data_%q' WHERE 0;" + , p->zStateDb , zTbl, (pIter->eType==OTA_PK_EXTERNAL ? ", 0 AS ota_rowid" : "") , zTbl ); @@ -1840,6 +1843,15 @@ static void otaOpenDatabase(sqlite3ota *p){ p->dbMain = otaOpenDbhandle(p, p->zTarget); p->dbOta = otaOpenDbhandle(p, p->zOta); + /* If using separate OTA and state databases, attach the state database to + ** the OTA db handle now. */ + if( p->zState ){ + otaMPrintfExec(p, p->dbOta, "ATTACH %Q AS stat", p->zState); + memcpy(p->zStateDb, "stat", 4); + }else{ + memcpy(p->zStateDb, "main", 4); + } + if( p->rc==SQLITE_OK ){ p->rc = sqlite3_create_function(p->dbMain, "ota_tmp_insert", -1, SQLITE_UTF8, (void*)p, otaTmpInsertFunc, 0, 0 @@ -2340,7 +2352,7 @@ static void otaSaveState(sqlite3ota *p, int eStage){ assert( p->zErrmsg==0 ); rc = prepareFreeAndCollectError(p->dbOta, &pInsert, &p->zErrmsg, sqlite3_mprintf( - "INSERT OR REPLACE INTO ota_state(k, v) VALUES " + "INSERT OR REPLACE INTO %s.ota_state(k, v) VALUES " "(%d, %d), " "(%d, %Q), " "(%d, %Q), " @@ -2349,6 +2361,7 @@ static void otaSaveState(sqlite3ota *p, int eStage){ "(%d, %lld), " "(%d, %lld), " "(%d, %lld) ", + p->zStateDb, OTA_STATE_STAGE, eStage, OTA_STATE_TBL, p->objiter.zTbl, OTA_STATE_IDX, p->objiter.zIdx, @@ -2385,8 +2398,9 @@ int sqlite3ota_step(sqlite3ota *p){ ** cannot be dropped as there are currently active SQL statements. ** But the contents can be deleted. */ if( pIter->abIndexed ){ - const char *zTbl = pIter->zTbl; - otaMPrintfExec(p, p->dbOta, "DELETE FROM 'ota_tmp_%q'", zTbl); + otaMPrintfExec(p, p->dbOta, + "DELETE FROM %s.'ota_tmp_%q'", p->zStateDb, pIter->zTbl + ); } }else{ otaObjIterPrepareAll(p, pIter, 0); @@ -2491,7 +2505,6 @@ static void otaFreeState(OtaState *p){ ** and return NULL. */ static OtaState *otaLoadState(sqlite3ota *p){ - const char *zSelect = "SELECT k, v FROM ota_state"; OtaState *pRet = 0; sqlite3_stmt *pStmt = 0; int rc; @@ -2500,7 +2513,9 @@ static OtaState *otaLoadState(sqlite3ota *p){ pRet = (OtaState*)otaMalloc(p, sizeof(OtaState)); if( pRet==0 ) return 0; - rc = prepareAndCollectError(p->dbOta, &pStmt, &p->zErrmsg, zSelect); + rc = prepareFreeAndCollectError(p->dbOta, &pStmt, &p->zErrmsg, + sqlite3_mprintf("SELECT k, v FROM %s.ota_state", p->zStateDb) + ); while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ switch( sqlite3_column_int(pStmt, 0) ){ case OTA_STATE_STAGE: @@ -2645,15 +2660,17 @@ static void otaDeleteVfs(sqlite3ota *p){ } } -/* -** Open and return a new OTA handle. -*/ -sqlite3ota *sqlite3ota_open(const char *zTarget, const char *zOta){ +static sqlite3ota *otaOpen( + const char *zTarget, + const char *zOta, + const char *zState +){ sqlite3ota *p; int nTarget = strlen(zTarget); int nOta = strlen(zOta); + int nState = zState ? strlen(zState) : 0; - p = (sqlite3ota*)sqlite3_malloc(sizeof(sqlite3ota)+nTarget+1+nOta+1); + p = (sqlite3ota*)sqlite3_malloc(sizeof(sqlite3ota)+nTarget+1+nOta+1+nState+1); if( p ){ OtaState *pState = 0; @@ -2667,13 +2684,15 @@ sqlite3ota *sqlite3ota_open(const char *zTarget, const char *zOta){ memcpy(p->zTarget, zTarget, nTarget+1); p->zOta = &p->zTarget[nTarget+1]; memcpy(p->zOta, zOta, nOta+1); + if( zState ){ + p->zState = &p->zOta[nOta+1]; + memcpy(p->zState, zState, nState+1); + } otaOpenDatabase(p); } /* If it has not already been created, create the ota_state table */ - if( p->rc==SQLITE_OK ){ - p->rc = sqlite3_exec(p->dbOta, OTA_CREATE_STATE, 0, 0, &p->zErrmsg); - } + otaMPrintfExec(p, p->dbOta, OTA_CREATE_STATE, p->zStateDb); if( p->rc==SQLITE_OK ){ pState = otaLoadState(p); @@ -2756,6 +2775,28 @@ sqlite3ota *sqlite3ota_open(const char *zTarget, const char *zOta){ return p; } + +/* +** Open and return a new OTA handle. +*/ +sqlite3ota *sqlite3ota_open_v2( + const char *zDb, + const char *zOta, + const char *zState +){ + return otaOpen(zDb, zOta, zState); +} + +/* +** Open and return a new OTA handle. +*/ +sqlite3ota *sqlite3ota_open( + const char *zDb, + const char *zOta +){ + return otaOpen(zDb, zOta, 0); +} + /* ** Return the database handle used by pOta. */ diff --git a/ext/ota/sqlite3ota.h b/ext/ota/sqlite3ota.h index c558be51fb..2b55fb2795 100644 --- a/ext/ota/sqlite3ota.h +++ b/ext/ota/sqlite3ota.h @@ -272,6 +272,37 @@ typedef struct sqlite3ota sqlite3ota; */ sqlite3ota *sqlite3ota_open(const char *zTarget, const char *zOta); +/* +** Open an OTA handle with an auxiliary state file. +** +** This API is similar to sqlite3ota_open(), except that it allows the user +** to specify a separate SQLite database in which to store the OTA update +** state. +** +** While executing, the OTA extension usually stores the current state +** of the update (how many rows have been updated, which indexes are yet +** to be updated etc.) within the OTA database itself. This can be +** convenient, as it means that the OTA application does not need to +** organize removing a separate state file after the update is concluded. +** However, it can also be inconvenient - for example if the OTA update +** database is sto be stored on a read-only media. +** +** If an OTA update started using a handle opened with this function is +** suspended, the application must use this function to resume it, and +** must pass the same zState argument each time the update is resumed. +** Attempting to resume an sqlite3ota_open_v2() update using sqlite3ota_open(), +** or with a call to sqlite3ota_open_v2() specifying a different zState +** argument leads to undefined behaviour. +** +** Once the OTA update is finished, the OTA extension does not +** automatically remove the zState database file, even if it created it. +*/ +sqlite3ota *sqlite3ota_open_v2( + const char *zTarget, + const char *zOta, + const char *zState +); + /* ** Internally, each OTA connection uses a separate SQLite database ** connection to access the target and ota update databases. This diff --git a/ext/ota/test_ota.c b/ext/ota/test_ota.c index 601453e129..f2370ad9a7 100644 --- a/ext/ota/test_ota.c +++ b/ext/ota/test_ota.c @@ -112,7 +112,7 @@ static int test_sqlite3ota_cmd( } /* -** Tclcmd: sqlite3ota CMD +** Tclcmd: sqlite3ota CMD ?? */ static int test_sqlite3ota( ClientData clientData, @@ -125,15 +125,20 @@ static int test_sqlite3ota( const char *zTarget; const char *zOta; - if( objc!=4 ){ - Tcl_WrongNumArgs(interp, 1, objv, "NAME TARGET-DB OTA-DB"); + if( objc!=4 && objc!=5 ){ + Tcl_WrongNumArgs(interp, 1, objv, "NAME TARGET-DB OTA-DB ?STATE-DB?"); return TCL_ERROR; } zCmd = Tcl_GetString(objv[1]); zTarget = Tcl_GetString(objv[2]); zOta = Tcl_GetString(objv[3]); - pOta = sqlite3ota_open(zTarget, zOta); + if( objc==4 ){ + pOta = sqlite3ota_open(zTarget, zOta); + }else{ + const char *zStateDb = Tcl_GetString(objv[4]); + pOta = sqlite3ota_open_v2(zTarget, zOta, zStateDb); + } Tcl_CreateObjCommand(interp, zCmd, test_sqlite3ota_cmd, (ClientData)pOta, 0); Tcl_SetObjResult(interp, objv[1]); return TCL_OK; diff --git a/manifest b/manifest index cb3bd699a7..6558af36a3 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\slatest\strunk\schanges\swith\sthis\sbranch. -D 2015-05-19T14:14:57.988 +C Allow\sOTA\supdate\sstate\sdata\sto\sbe\sstored\sin\sa\sdatabase\sseparate\sfrom\sthe\sOTA\supdate\sdatabase. +D 2015-05-19T16:22:58.978 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 0a6ae26396ec696221021780dffbb894ff3cead7 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -124,7 +124,7 @@ F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95 F ext/misc/vtshim.c babb0dc2bf116029e3e7c9a618b8a1377045303e F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212 F ext/ota/ota.c c47352838b967384a81eda5de75c352922a0dd6e -F ext/ota/ota1.test 960418e4171a989426f8b1ad8ee31770e0f94fb8 +F ext/ota/ota1.test abdcbe746db4c7f7b51e842b576cacb33eef28f5 F ext/ota/ota10.test 85e0f6e7964db5007590c1b299e75211ed4240d4 F ext/ota/ota11.test 2f606cd2b4af260a86b549e91b9f395450fc75cb F ext/ota/ota12.test 0dff44474de448fb4b0b28c20da63273a4149abb @@ -139,9 +139,9 @@ F ext/ota/otaA.test ab67f7f53670b81c750dcc946c5b704f51c429a4 F ext/ota/otacrash.test 8346192b2d46cbe7787d5d65904d81d3262a3cbf F ext/ota/otafault.test 8c43586c2b96ca16bbce00b5d7e7d67316126db8 F ext/ota/otafault2.test fa202a98ca221faec318f3e5c5f39485b1256561 -F ext/ota/sqlite3ota.c 5bfd677bd956d0ea9f0022b010ac70409e8e9bf6 -F ext/ota/sqlite3ota.h 7faa45e080b9c136e666c383187ff6e39d88135b -F ext/ota/test_ota.c e34c801c665d64b4b9e00b71f1acf8c652404b2b +F ext/ota/sqlite3ota.c 89530008cff5825072ef455eb45cf04d497d6399 +F ext/ota/sqlite3ota.h ebde09505ccfff78def3c67b02cfebe27f830925 +F ext/ota/test_ota.c ba5d936190713d15919502d6ee6f287cada279ae F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761 F ext/rtree/rtree.c 0c207fd8b814a35537d96681cbf57436e200b75e F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e @@ -1278,7 +1278,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 16ab9cafd00ea5df7e6f75d6a6740237828b888d 5df4056448fee1c766f8f79c735ed12abdce5101 -R ca0828efa06faab3b9677740d6592df2 +P 6055a6725cb24469c10de9a04f3614dcc79193c6 +R 2b87c978f295a3b27fab33174a5bc3b2 U dan -Z 5e45a187c4752901348a44d76452f100 +Z 5cb8f8d4aed58cbcc0b1d560014c2a0f diff --git a/manifest.uuid b/manifest.uuid index 5f328a8639..cc6e9c40db 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -6055a6725cb24469c10de9a04f3614dcc79193c6 \ No newline at end of file +5af8db56af457d60ea030d84666ca7fffb6821fe \ No newline at end of file