]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add support for update statements to sqlite3ota.c.
authordan <dan@noemail.net>
Mon, 8 Sep 2014 17:50:35 +0000 (17:50 +0000)
committerdan <dan@noemail.net>
Mon, 8 Sep 2014 17:50:35 +0000 (17:50 +0000)
FossilOrigin-Name: e109b27e4d66b83e1a804e7556d9c91aa37fea28

ext/ota/ota1.test
ext/ota/sqlite3ota.c
manifest
manifest.uuid

index 22b8fa66cdc6118c5031b75e745bbb314dc66ee4..917d1d2c9e2259ccbe1a0ad07d562a170a5671ee 100644 (file)
@@ -36,6 +36,8 @@ proc create_ota1 {filename} {
 #
 #   CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
 #
+# This OTA includes both insert and delete operations.
+#
 proc create_ota4 {filename} {
   forcedelete $filename
   sqlite3 ota1 $filename  
@@ -50,6 +52,25 @@ proc create_ota4 {filename} {
   return $filename
 }
 
+# Create a simple OTA database. That expects to write to a table:
+#
+#   CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d);
+#
+# This OTA includes update statements.
+#
+proc create_ota5 {filename} {
+  forcedelete $filename
+  sqlite3 ota5 $filename  
+  ota5 eval {
+    CREATE TABLE data_t1(a, b, c, d, ota_control);
+    INSERT INTO data_t1 VALUES(1, NULL, NULL, 5, '...x');  -- SET d = 5
+    INSERT INTO data_t1 VALUES(2, NULL, 10, 5, '..xx');    -- SET c=10, d = 5
+    INSERT INTO data_t1 VALUES(3, 11, NULL, NULL, '.x..'); -- SET b=11
+  }
+  ota5 close
+  return $filename
+}
+
 # Run the OTA in file $ota on target database $target until completion.
 #
 proc run_ota {target ota} {
@@ -224,6 +245,47 @@ foreach {tn2 cmd} {1 run_ota 2 step_ota} {
   }
 }
 
+#-------------------------------------------------------------------------
+#
+foreach {tn2 cmd} {1 run_ota 2 step_ota} {
+  foreach {tn schema} {
+    1 {
+      CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d);
+    }
+    2 {
+      CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c, d);
+      CREATE INDEX i1 ON t1(d);
+      CREATE INDEX i2 ON t1(d, c);
+      CREATE INDEX i3 ON t1(d, c, b);
+      CREATE INDEX i4 ON t1(b);
+      CREATE INDEX i5 ON t1(c);
+      CREATE INDEX i6 ON t1(c, b);
+    }
+  } {
+    reset_db
+    execsql $schema
+    execsql {
+      INSERT INTO t1 VALUES(1, 2, 3, 4);
+      INSERT INTO t1 VALUES(2, 5, 6, 7);
+      INSERT INTO t1 VALUES(3, 8, 9, 10);
+    }
+  
+    do_test 5.$tn2.$tn.1 {
+      create_ota5 ota.db
+      $cmd test.db ota.db
+    } {SQLITE_DONE}
+    
+    do_execsql_test 5.$tn2.$tn.2 {
+      SELECT * FROM t1 ORDER BY a ASC;
+    } {
+      1 2 3 5
+      2 5 10 5
+      3 11 9 10
+    }
+  
+    do_execsql_test 5.$tn2.$tn.3 { PRAGMA integrity_check } ok
+  }
+}
 
 finish_test
 
index 62b1c4ab3413d30c1641307c982feca3cb38abe4..fcf0c8b9fd6863042292a4ac912443db4e853016 100644 (file)
@@ -78,6 +78,10 @@ struct OtaObjIter {
   sqlite3_stmt *pSelect;          /* Source data */
   sqlite3_stmt *pInsert;          /* Statement for INSERT operations */
   sqlite3_stmt *pDelete;          /* Statement for DELETE ops */
+
+  /* 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) */
 };
 
 /*
@@ -179,6 +183,24 @@ static void otaObjIterFreeCols(OtaObjIter *pIter){
   pIter->azTblCol = 0;
   pIter->abTblPk = 0;
   pIter->nTblCol = 0;
+  sqlite3_free(pIter->zMask);
+  pIter->zMask = 0;
+}
+
+/*
+** Finalize all statements and free all allocations that are specific to
+** the current object (table/index pair).
+*/
+static void otaObjIterClearStatements(OtaObjIter *pIter){
+  sqlite3_finalize(pIter->pSelect);
+  sqlite3_finalize(pIter->pInsert);
+  sqlite3_finalize(pIter->pDelete);
+  sqlite3_finalize(pIter->pUpdate);
+  pIter->pSelect = 0;
+  pIter->pInsert = 0;
+  pIter->pDelete = 0;
+  pIter->pUpdate = 0;
+  pIter->nCol = 0;
 }
 
 /*
@@ -186,11 +208,9 @@ static void otaObjIterFreeCols(OtaObjIter *pIter){
 ** as the only argument.
 */
 static void otaObjIterFinalize(OtaObjIter *pIter){
+  otaObjIterClearStatements(pIter);
   sqlite3_finalize(pIter->pTblIter);
   sqlite3_finalize(pIter->pIdxIter);
-  sqlite3_finalize(pIter->pSelect);
-  sqlite3_finalize(pIter->pInsert);
-  sqlite3_finalize(pIter->pDelete);
   otaObjIterFreeCols(pIter);
   memset(pIter, 0, sizeof(OtaObjIter));
 }
@@ -208,13 +228,7 @@ static int otaObjIterNext(sqlite3ota *p, OtaObjIter *pIter){
   if( rc==SQLITE_OK ){
 
     /* Free any SQLite statements used while processing the previous object */ 
-    sqlite3_finalize(pIter->pSelect);
-    sqlite3_finalize(pIter->pInsert);
-    sqlite3_finalize(pIter->pDelete);
-    pIter->pSelect = 0;
-    pIter->pInsert = 0;
-    pIter->pDelete = 0;
-    pIter->nCol = 0;
+    otaObjIterClearStatements(pIter);
 
     if( pIter->bCleanup ){
       otaObjIterFreeCols(pIter);
@@ -412,15 +426,16 @@ static char *otaObjIterGetCollist(
 
 static char *otaObjIterGetOldlist(
   sqlite3ota *p, 
-  OtaObjIter *pIter
+  OtaObjIter *pIter,
+  const char *zObj
 ){
   char *zList = 0;
   if( p->rc==SQLITE_OK ){
-    const char *zSep = "";
+    const char *zS = "";
     int i;
     for(i=0; i<pIter->nTblCol; i++){
-      zList = sqlite3_mprintf("%z%sold.%s", zList, zSep, pIter->azTblCol[i]);
-      zSep = ", ";
+      zList = sqlite3_mprintf("%z%s%s.%s", zList, zS, zObj, pIter->azTblCol[i]);
+      zS = ", ";
       if( zList==0 ){
         p->rc = SQLITE_NOMEM;
         break;
@@ -440,7 +455,8 @@ static char *otaObjIterGetWhere(
     int i;
     for(i=0; i<pIter->nTblCol; i++){
       if( pIter->abTblPk[i] ){
-        zList = sqlite3_mprintf("%z%s%s=?", zList, zSep, pIter->azTblCol[i]);
+        const char *zCol = pIter->azTblCol[i];
+        zList = sqlite3_mprintf("%z%s%s=?%d", zList, zSep, zCol, i+1);
         zSep = " AND ";
         if( zList==0 ){
           p->rc = SQLITE_NOMEM;
@@ -452,6 +468,44 @@ static char *otaObjIterGetWhere(
   return zList;
 }
 
+/*
+** The SELECT statement iterating through the keys for the current object
+** (p->objiter.pSelect) currently points to a valid row. However, there
+** is something wrong with the ota_control value in the ota_control value
+** stored in the (p->nCol+1)'th column. Set the error code and error message
+** of the OTA handle to something reflecting this.
+*/
+static void otaBadControlError(sqlite3ota *p){
+  p->rc = SQLITE_ERROR;
+  p->zErrmsg = sqlite3_mprintf("Invalid ota_control value");
+}
+
+static char *otaObjIterGetSetlist(
+  sqlite3ota *p,
+  OtaObjIter *pIter,
+  const char *zMask
+){
+  char *zList = 0;
+  if( p->rc==SQLITE_OK ){
+    int i;
+
+    if( strlen(zMask)!=pIter->nTblCol ){
+      otaBadControlError(p);
+    }else{
+      const char *zSep = "";
+      for(i=0; i<pIter->nTblCol; i++){
+        if( zMask[i]=='x' ){
+          zList = sqlite3_mprintf("%z%s%s=?%d", 
+              zList, zSep, pIter->azTblCol[i], i+1
+          );
+          zSep = ", ";
+        }
+      }
+    }
+  }
+  return zList;
+}
+
 static char *otaObjIterGetBindlist(sqlite3ota *p, int nBind){
   char *zRet = 0;
   if( p->rc==SQLITE_OK ){
@@ -513,6 +567,7 @@ static int otaObjIterPrepareAll(
         p->rc = prepareFreeAndCollectError(p->db, &pIter->pSelect, pz,
             sqlite3_mprintf(
               "SELECT %s, ota_control FROM ota.'data_%q' "
+              "WHERE typeof(ota_control)='integer' AND ota_control!=1 "
                 "UNION ALL "
               "SELECT %s, ota_control FROM ota.'ota_tmp_%q' "
               "ORDER BY %s%s",
@@ -525,7 +580,8 @@ static int otaObjIterPrepareAll(
     }else{
       char *zBindings = otaObjIterGetBindlist(p, pIter->nTblCol);
       char *zWhere = otaObjIterGetWhere(p, pIter);
-      char *zOldlist = otaObjIterGetOldlist(p, pIter);
+      char *zOldlist = otaObjIterGetOldlist(p, pIter, "old");
+      char *zNewlist = otaObjIterGetOldlist(p, pIter, "new");
       zCollist = otaObjIterGetCollist(p, pIter, pIter->nTblCol, 0);
       pIter->nCol = pIter->nTblCol;
 
@@ -561,17 +617,43 @@ static int otaObjIterPrepareAll(
         otaMPrintfExec(p, 
             "CREATE TABLE IF NOT EXISTS ota.'ota_tmp_%q' AS "
             "SELECT * FROM ota.'data_%q' WHERE 0;"
+
             "CREATE TEMP TRIGGER ota_delete_%q BEFORE DELETE ON main.%Q "
             "BEGIN "
             "  INSERT INTO 'ota_tmp_%q'(ota_control, %s) VALUES(2, %s);"
             "END;"
-            , pIter->zTbl, pIter->zTbl, pIter->zTbl, pIter->zTbl, pIter->zTbl,
-            zCollist, zOldlist
+
+            "CREATE TEMP TRIGGER ota_update1_%q BEFORE UPDATE ON main.%Q "
+            "BEGIN "
+            "  INSERT INTO 'ota_tmp_%q'(ota_control, %s) VALUES(2, %s);"
+            "END;"
+
+            "CREATE TEMP TRIGGER ota_update2_%q AFTER UPDATE ON main.%Q "
+            "BEGIN "
+            "  INSERT INTO 'ota_tmp_%q'(ota_control, %s) VALUES(3, %s);"
+            "END;"
+
+            , pIter->zTbl, pIter->zTbl, 
+            pIter->zTbl, pIter->zTbl, pIter->zTbl, zCollist, zOldlist, 
+            pIter->zTbl, pIter->zTbl, pIter->zTbl, zCollist, zOldlist, 
+            pIter->zTbl, pIter->zTbl, pIter->zTbl, zCollist, zNewlist
         );
       }
 
+      /* Allocate space required for the zMask field. */
+      if( p->rc==SQLITE_OK ){
+        int nMask = pIter->nTblCol+1;
+        pIter->zMask = (char*)sqlite3_malloc(nMask);
+        if( pIter->zMask==0 ){
+          p->rc = SQLITE_NOMEM;
+        }else{
+          memset(pIter->zMask, 0, nMask);
+        }
+      }
+
       sqlite3_free(zWhere);
       sqlite3_free(zOldlist);
+      sqlite3_free(zNewlist);
       sqlite3_free(zBindings);
     }
     sqlite3_free(zCollist);
@@ -584,18 +666,39 @@ static int otaObjIterPrepareAll(
 #define OTA_INSERT     1
 #define OTA_DELETE     2
 #define OTA_IDX_DELETE 3
-#define OTA_UPDATE     4
+#define OTA_IDX_INSERT 4
+#define OTA_UPDATE     5
 
-/*
-** The SELECT statement iterating through the keys for the current object
-** (p->objiter.pSelect) currently points to a valid row. However, there
-** is something wrong with the ota_control value in the ota_control value
-** stored in the (p->nCol+1)'th column. Set the error code and error message
-** of the OTA handle to something reflecting this.
-*/
-static void otaBadControlError(sqlite3ota *p){
-  p->rc = SQLITE_ERROR;
-  p->zErrmsg = sqlite3_mprintf("Invalid ota_control value");
+static int otaGetUpdateStmt(
+  sqlite3ota *p, 
+  OtaObjIter *pIter, 
+  const char *zMask,
+  sqlite3_stmt **ppStmt
+){
+  if( pIter->pUpdate && strcmp(zMask, pIter->zMask)==0 ){
+    *ppStmt = pIter->pUpdate;
+  }else{
+    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 ){
+      zUpdate = sqlite3_mprintf("UPDATE %Q SET %s WHERE %s", 
+          pIter->zTbl, zSet, zWhere
+      );
+      p->rc = prepareFreeAndCollectError(
+          p->db, &pIter->pUpdate, &p->zErrmsg, zUpdate
+      );
+      *ppStmt = pIter->pUpdate;
+    }
+    if( p->rc==SQLITE_OK ){
+      memcpy(pIter->zMask, zMask, pIter->nTblCol);
+    }
+    sqlite3_free(zWhere);
+    sqlite3_free(zSet);
+  }
+  return p->rc;
 }
 
 /*
@@ -628,6 +731,8 @@ static int otaStepType(sqlite3ota *p, const char **pzMask){
         res = OTA_DELETE;
       }else if( iVal==2 ){
         res = OTA_IDX_DELETE;
+      }else if( iVal==3 ){
+        res = OTA_IDX_INSERT;
       }
       break;
     }
@@ -670,33 +775,43 @@ static int otaStep(sqlite3ota *p){
     if( pIter->zIdx==0 && eType==OTA_IDX_DELETE ){
       otaBadControlError(p);
     }
-    else if( eType==OTA_INSERT || eType==OTA_IDX_DELETE ){
+    else if( 
+        eType==OTA_INSERT 
+     || eType==OTA_DELETE
+     || eType==OTA_IDX_DELETE 
+     || eType==OTA_IDX_INSERT
+    ){
       sqlite3_stmt *pWriter;
+
       assert( eType!=OTA_UPDATE );
+      assert( eType!=OTA_DELETE || pIter->zIdx==0 );
+
+      if( eType==OTA_IDX_DELETE || eType==OTA_DELETE ){
+        pWriter = pIter->pDelete;
+      }else{
+        pWriter = pIter->pInsert;
+      }
 
-      pWriter = (eType==OTA_INSERT)?pIter->pInsert:pIter->pDelete;
       for(i=0; i<pIter->nCol; i++){
+        if( eType==SQLITE_DELETE && pIter->zIdx==0 && pIter->abTblPk[i]==0 ){
+          continue;
+        }
         sqlite3_value *pVal = sqlite3_column_value(pIter->pSelect, i);
         sqlite3_bind_value(pWriter, i+1, pVal);
       }
       sqlite3_step(pWriter);
       p->rc = resetAndCollectError(pWriter, &p->zErrmsg);
-    }
-    else if( eType==OTA_DELETE && pIter->zIdx==0 ){
-      int iVar = 1;
-      assert( pIter->zIdx==0 );
-      assert( pIter->nCol==pIter->nTblCol );
-      for(i=0; i<pIter->nCol; i++){
-        if( pIter->abTblPk[i] ){
+    }else if( eType==OTA_UPDATE ){
+      sqlite3_stmt *pUpdate = 0;
+      otaGetUpdateStmt(p, pIter, zMask, &pUpdate);
+      if( pUpdate ){
+        for(i=0; i<pIter->nCol; i++){
           sqlite3_value *pVal = sqlite3_column_value(pIter->pSelect, i);
-          sqlite3_bind_value(pIter->pDelete, iVar++, pVal);
+          sqlite3_bind_value(pUpdate, i+1, pVal);
         }
+        sqlite3_step(pUpdate);
+        p->rc = resetAndCollectError(pUpdate, &p->zErrmsg);
       }
-      sqlite3_step(pIter->pDelete);
-      p->rc = resetAndCollectError(pIter->pDelete, &p->zErrmsg);
-    }else if( eType==OTA_UPDATE ){
-      p->rc = SQLITE_ERROR;
-      p->zErrmsg = sqlite3_mprintf("not yet");
     }else{
       /* no-op */
       assert( eType==OTA_DELETE && pIter->zIdx );
@@ -718,7 +833,7 @@ int sqlite3ota_step(sqlite3ota *p){
         /* Clean up the ota_tmp_xxx table for the previous table. It 
         ** cannot be dropped as there are currently active SQL statements.
         ** But the contents can be deleted.  */
-        otaMPrintfExec(p, "DELETE FROM ota.'ota_tmp_%q'", pIter->zTbl);
+        // otaMPrintfExec(p, "DELETE FROM ota.'ota_tmp_%q'", pIter->zTbl);
       }else{
         otaObjIterPrepareAll(p, pIter, 0);
         
index 46bc695b1a9f022f2d2436fbe7a865518949863b..35c6c48a27a3bba598b507f16bf8e2073a043efa 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\ssupport\sfor\sdelete\soperations\sto\sthe\sota\sextension.
-D 2014-09-06T20:19:38.006
+C Add\ssupport\sfor\supdate\sstatements\sto\ssqlite3ota.c.
+D 2014-09-08T17:50:35.158
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in cf57f673d77606ab0f2d9627ca52a9ba1464146a
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -122,9 +122,9 @@ F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95
 F ext/misc/vtshim.c babb0dc2bf116029e3e7c9a618b8a1377045303e
 F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212
 F ext/ota/ota.c d37097e92a005d3915883adefbb93019ea6f8841
-F ext/ota/ota1.test 47317179125b5e65289a9f59753c9f895186e6d5
+F ext/ota/ota1.test fe0bb8acf0caef6c19937b84c6547b788342610d
 F ext/ota/ota2.test 13f76922446c62ed96192e938b8e625ebf0142fa
-F ext/ota/sqlite3ota.c ceb0f77dc6a958d299f532319f6477e5599dc59d
+F ext/ota/sqlite3ota.c 3697f6db2d51d5f7c0f0306fe94514785361e521
 F ext/ota/sqlite3ota.h 545f0008b5f02f2595899cb9841caddada5c17c0
 F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
 F ext/rtree/rtree.c 57bec53e1a677ab74217fe1f20a58c3a47261d6b
@@ -1198,7 +1198,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 3c2f4a078132992e33cc675173c84f8385af9cb5
-R eeedfe1c92df64758f3e97de2d55d43b
+P f988234ba54d7c667f7deef1d04beed4e7fe6182
+R c23b09bc3993f39690d272fa85636507
 U dan
-Z 3317be7135040205f21e9375db1283ea
+Z 28e28dd83293020abb0c0fb47ae5ab2c
index 457a56e402fe53ac2baa6eb81f13b4afa513e513..09501cef431981c3f8d2aa864f368c85a40e3dde 100644 (file)
@@ -1 +1 @@
-f988234ba54d7c667f7deef1d04beed4e7fe6182
\ No newline at end of file
+e109b27e4d66b83e1a804e7556d9c91aa37fea28
\ No newline at end of file