--- /dev/null
+# 2015 February 16
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+# Test an OTA update that features lots of different ota_control strings
+# for UPDATE statements. This tests OTA's internal UPDATE statement cache.
+#
+
+if {![info exists testdir]} {
+ set testdir [file join [file dirname [info script]] .. .. test]
+}
+source $testdir/tester.tcl
+source $testdir/lock_common.tcl
+set ::testprefix ota13
+
+do_execsql_test 1.0 {
+ CREATE TABLE t1(a PRIMARY KEY, b, c, d, e, f, g, h);
+ WITH ii(i) AS (SELECT 0 UNION ALL SELECT i+1 FROM ii WHERE i<127)
+ INSERT INTO t1 SELECT i, 0, 0, 0, 0, 0, 0, 0 FROM ii;
+}
+
+forcedelete ota.db
+do_execsql_test 1.1 {
+ ATTACH 'ota.db' AS ota;
+ CREATE TABLE ota.data_t1(a, b, c, d, e, f, g, h, ota_control);
+}
+
+do_test 1.2 {
+ for {set i 0} {$i<128} {incr i} {
+ set control "."
+ for {set bit 6} {$bit>=0} {incr bit -1} {
+ if { $i & (1<<$bit) } {
+ append control "x"
+ } else {
+ append control "."
+ }
+ }
+ execsql { INSERT INTO data_t1 VALUES($i, 1, 1, 1, 1, 1, 1, 1, $control) }
+ }
+} {}
+
+do_test 1.3 {
+ sqlite3ota ota test.db ota.db
+ while 1 {
+ set rc [ota step]
+ if {$rc!="SQLITE_OK"} break
+ }
+ ota close
+} {SQLITE_DONE}
+
+do_execsql_test 1.4 {
+ SELECT count(*) FROM t1 WHERE
+ a == ( (b<<6) + (c<<5) + (d<<4) + (e<<3) + (f<<2) + (g<<1) + (h<<0) )
+} {128}
+
+
+finish_test
+
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_OTA)
#include "sqlite3ota.h"
+/* Maximum number of prepared UPDATE statements held by this module */
+#define SQLITE_OTA_UPDATE_CACHESIZE 16
+
/*
** Swap two objects of type TYPE.
*/
typedef struct OtaState OtaState;
typedef struct ota_vfs ota_vfs;
typedef struct ota_file ota_file;
+typedef struct OtaUpdateStmt OtaUpdateStmt;
#if !defined(SQLITE_AMALGAMATION)
typedef unsigned int u32;
i64 iOalSz;
};
+struct OtaUpdateStmt {
+ char *zMask; /* Copy of update mask used with pUpdate */
+ sqlite3_stmt *pUpdate; /* Last update statement (or NULL) */
+ OtaUpdateStmt *pNext;
+};
+
/*
** An iterator of this type is used to iterate through all objects in
** the target database that require updating. For each such table, the
sqlite3_stmt *pTmpInsert; /* Insert into ota_tmp_$zTbl */
/* Last UPDATE used (for PK b-tree updates only), or NULL. */
- char *zMask; /* Copy of update mask used with pUpdate */
- sqlite3_stmt *pUpdate; /* Last update statement (or NULL) */
+ OtaUpdateStmt *pOtaUpdate;
};
/*
pIter->abTblPk = 0;
pIter->abNotNull = 0;
pIter->nTblCol = 0;
- sqlite3_free(pIter->zMask);
- pIter->zMask = 0;
pIter->eType = 0; /* Invalid value */
}
** the current object (table/index pair).
*/
static void otaObjIterClearStatements(OtaObjIter *pIter){
+ OtaUpdateStmt *pUp;
+
sqlite3_finalize(pIter->pSelect);
sqlite3_finalize(pIter->pInsert);
sqlite3_finalize(pIter->pDelete);
- sqlite3_finalize(pIter->pUpdate);
sqlite3_finalize(pIter->pTmpInsert);
+ pUp = pIter->pOtaUpdate;
+ while( pUp ){
+ OtaUpdateStmt *pTmp = pUp->pNext;
+ sqlite3_finalize(pUp->pUpdate);
+ sqlite3_free(pUp);
+ pUp = pTmp;
+ }
+
pIter->pSelect = 0;
pIter->pInsert = 0;
pIter->pDelete = 0;
- pIter->pUpdate = 0;
+ pIter->pOtaUpdate = 0;
pIter->pTmpInsert = 0;
pIter->nCol = 0;
}
otaObjIterPrepareTmpInsert(p, pIter, zCollist, zOtaRowid);
}
- /* Allocate space required for the zMask field. */
- pIter->zMask = (char*)otaMalloc(p, pIter->nTblCol+1);
-
sqlite3_free(zWhere);
sqlite3_free(zOldlist);
sqlite3_free(zNewlist);
** be used to update the imposter table for the main table b-tree of the
** table object that pIter currently points to, assuming that the
** ota_control column of the data_xyz table contains zMask.
+**
+** If the zMask string does not specify any columns to update, then this
+** is not an error. Output variable *ppStmt is set to NULL in this case.
*/
static int otaGetUpdateStmt(
sqlite3ota *p, /* OTA handle */
const char *zMask, /* ota_control value ('x.x.') */
sqlite3_stmt **ppStmt /* OUT: UPDATE statement handle */
){
- if( pIter->pUpdate && strcmp(zMask, pIter->zMask)==0 ){
- *ppStmt = pIter->pUpdate;
+ OtaUpdateStmt **pp;
+ OtaUpdateStmt *pUp = 0;
+ int nUp = 0;
+
+ /* In case an error occurs */
+ *ppStmt = 0;
+
+ /* Search for an existing statement. If one is found, shift it to the front
+ ** of the LRU queue and return immediately. Otherwise, leave nUp pointing
+ ** to the number of statements currently in the cache and pUp to the
+ ** last object in the list. */
+ for(pp=&pIter->pOtaUpdate; *pp; pp=&((*pp)->pNext)){
+ pUp = *pp;
+ if( strcmp(pUp->zMask, zMask)==0 ){
+ *pp = pUp->pNext;
+ pUp->pNext = pIter->pOtaUpdate;
+ pIter->pOtaUpdate = pUp;
+ *ppStmt = pUp->pUpdate;
+ return SQLITE_OK;
+ }
+ nUp++;
+ }
+ assert( pUp==0 || pUp->pNext==0 );
+
+ if( nUp>=SQLITE_OTA_UPDATE_CACHESIZE ){
+ for(pp=&pIter->pOtaUpdate; *pp!=pUp; pp=&((*pp)->pNext));
+ *pp = 0;
+ sqlite3_finalize(pUp->pUpdate);
+ pUp->pUpdate = 0;
}else{
+ pUp = (OtaUpdateStmt*)otaMalloc(p, sizeof(OtaUpdateStmt)+pIter->nTblCol+1);
+ }
+
+ if( pUp ){
char *zWhere = otaObjIterGetWhere(p, pIter);
char *zSet = otaObjIterGetSetlist(p, pIter, zMask);
char *zUpdate = 0;
- sqlite3_finalize(pIter->pUpdate);
- pIter->pUpdate = 0;
- if( p->rc==SQLITE_OK ){
+
+ pUp->zMask = (char*)&pUp[1];
+ memcpy(pUp->zMask, zMask, pIter->nTblCol);
+ pUp->pNext = pIter->pOtaUpdate;
+ pIter->pOtaUpdate = pUp;
+
+ if( zSet ){
const char *zPrefix = "";
if( pIter->eType!=OTA_PK_VTAB ) zPrefix = "ota_imp_";
zPrefix, pIter->zTbl, zSet, zWhere
);
p->rc = prepareFreeAndCollectError(
- p->dbMain, &pIter->pUpdate, &p->zErrmsg, zUpdate
+ p->dbMain, &pUp->pUpdate, &p->zErrmsg, zUpdate
);
- *ppStmt = pIter->pUpdate;
- }
- if( p->rc==SQLITE_OK ){
- memcpy(pIter->zMask, zMask, pIter->nTblCol);
+ *ppStmt = pUp->pUpdate;
}
sqlite3_free(zWhere);
sqlite3_free(zSet);
}
+
return p->rc;
}
** blob starting at byte offset 40.
*/
static i64 otaShmChecksum(sqlite3ota *p){
- i64 iRet;
+ i64 iRet = 0;
if( p->rc==SQLITE_OK ){
sqlite3_file *pDb = p->pTargetFd->pReal;
u32 volatile *ptr;
-C Add\scomments\srelated\sto\sdatabase\slocking\sto\ssqlite3ota.h.
-D 2015-04-22T11:34:38.145
+C Have\sOTA\smaintain\sa\ssmall\sLRU\scache\sof\sUPDATE\sstatements.\sThis\sreduces\sthe\samount\sof\stime\sit\sspends\scompiling\sUPDATE\sif\sa\ssingle\sdata_xxx\stable\scontains\smany\sdifferent\sota_control\sstrings.
+D 2015-04-23T18:14:21.590
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 79b306896135a2305cfb7e6d88990fc4820fb917
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
F ext/ota/ota10.test 85e0f6e7964db5007590c1b299e75211ed4240d4
F ext/ota/ota11.test 2f606cd2b4af260a86b549e91b9f395450fc75cb
F ext/ota/ota12.test 0dff44474de448fb4b0b28c20da63273a4149abb
+F ext/ota/ota13.test f7a3d73fa5d3fabf2755b569f125fce7390a874c
F ext/ota/ota3.test 3fe3521fbdce32d0e4e116a60999c3cba47712c5
F ext/ota/ota5.test ad0799daf8923ddebffe75ae8c5504ca90b7fadb
F ext/ota/ota6.test 3bde7f69a894748b27206b6753462ec3b75b6bb6
F ext/ota/otacrash.test 8346192b2d46cbe7787d5d65904d81d3262a3cbf
F ext/ota/otafault.test 8c43586c2b96ca16bbce00b5d7e7d67316126db8
F ext/ota/otafault2.test fa202a98ca221faec318f3e5c5f39485b1256561
-F ext/ota/sqlite3ota.c b45d73607b78eba89ca224fb0d10e10d1b32b6b0
+F ext/ota/sqlite3ota.c 3b53197166b08bae980ea9b3b668f7eb0580fe8c
F ext/ota/sqlite3ota.h c0d32e46aa52cce3b16899399639deaff1f27b2f
F ext/ota/test_ota.c e34c801c665d64b4b9e00b71f1acf8c652404b2b
F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 352fa09efd8240567b8f9487944e8c3ade4e664a
-R 7514208e376f1404350994ff3e1120f2
+P 77242965e77446313a8f0a65a39fccb67fe4cabf
+R a8ed618fd0d0e5a18750e29ca6db9e19
U dan
-Z aab2f59acbc2b3195f57173b44acb17c
+Z 00cb71982fa5d2b70e9c222c1f403ddb