Allow OTA update state data to be stored in a database separate from the OTA update database.

FossilOrigin-Name: 5af8db56af457d60ea030d84666ca7fffb6821fe
This commit is contained in:
dan 2015-05-19 16:22:58 +00:00
parent b387e63707
commit a6fb464878
6 changed files with 195 additions and 53 deletions

View File

@ -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

View File

@ -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.
*/

View File

@ -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

View File

@ -112,7 +112,7 @@ static int test_sqlite3ota_cmd(
}
/*
** Tclcmd: sqlite3ota CMD <target-db> <ota-db>
** Tclcmd: sqlite3ota CMD <target-db> <ota-db> ?<state-db>?
*/
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;

View File

@ -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

View File

@ -1 +1 @@
6055a6725cb24469c10de9a04f3614dcc79193c6
5af8db56af457d60ea030d84666ca7fffb6821fe