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 {
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);
} {
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}
}
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
+ }
}
}
#-------------------------------------------------------------------------
#
- 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);
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}
}
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);
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
}
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);
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}
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
+ }
}
}
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]
}
# 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}
#
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);
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
#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;
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 */
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
));
}
}
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{
"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
);
}
/* 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
);
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
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), "
"(%d, %lld), "
"(%d, %lld), "
"(%d, %lld) ",
+ p->zStateDb,
OTA_STATE_STAGE, eStage,
OTA_STATE_TBL, p->objiter.zTbl,
OTA_STATE_IDX, p->objiter.zIdx,
** 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);
** 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;
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:
}
}
-/*
-** 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;
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);
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.
*/
*/
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
}
/*
-** Tclcmd: sqlite3ota CMD <target-db> <ota-db>
+** Tclcmd: sqlite3ota CMD <target-db> <ota-db> ?<state-db>?
*/
static int test_sqlite3ota(
ClientData clientData,
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;
-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
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
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
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
-6055a6725cb24469c10de9a04f3614dcc79193c6
\ No newline at end of file
+5af8db56af457d60ea030d84666ca7fffb6821fe
\ No newline at end of file