]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Allow the ota extension to write to tables with no PRIMARY KEY declaration.
authordan <dan@noemail.net>
Sat, 6 Dec 2014 19:30:41 +0000 (19:30 +0000)
committerdan <dan@noemail.net>
Sat, 6 Dec 2014 19:30:41 +0000 (19:30 +0000)
FossilOrigin-Name: ba59a7e2ba97244492cbca9247456df0f3f19248

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

index e6b50c59065d5e409486fbc74c90d6d45dc12ae5..74600ff6ecf77cef2741204670c9e36d81feb93f 100644 (file)
@@ -177,6 +177,9 @@ foreach {tn2 cmd} {1 run_ota 2 step_ota 3 step_ota_uri} {
 # relaxed so that external PRIMARY KEYs on tables with automatic rowids
 # are now allowed.
 #
+# UPDATE 2: Tables without any PRIMARY KEY declaration are now allowed.
+# However the input table must feature an "ota_rowid" column.
+#
 reset_db
 create_ota1 ota.db
 do_execsql_test 2.1 { CREATE TABLE t1(a, b, c) }
@@ -186,7 +189,7 @@ do_test 2.2 {
 } {SQLITE_ERROR}
 do_test 2.3 {
   list [catch { ota close } msg] $msg
-} {1 {SQLITE_ERROR - table t1 has no PRIMARY KEY}}
+} {1 {SQLITE_ERROR - table data_t1 requires ota_rowid column}}
 reset_db
 do_execsql_test 2.4 { CREATE TABLE t1(a PRIMARY KEY, b, c) }
 do_test 2.5 {
index 18791b479eff55db033e008313eacaae8c83c2dc..e746d363f16877494feeb9f20d3d0a635bcdf829 100644 (file)
@@ -9,7 +9,7 @@
 #
 #***********************************************************************
 #
-# Test OTA with virtual tables
+# Test OTA with virtual tables. And tables with no PRIMARY KEY declarations.
 #
 
 if {![info exists testdir]} {
@@ -60,6 +60,68 @@ do_execsql_test 1.2.2 { SELECT rowid, * FROM f1 } {
 do_execsql_test 1.2.3 { INSERT INTO f1(f1) VALUES('integrity-check') }
 integrity_check 1.2.4
 
+#-------------------------------------------------------------------------
+# Tables with no PK declaration.
+#
+
+# Run the OTA in file $ota on target database $target until completion.
+#
+proc run_ota {target ota} {
+  sqlite3ota ota $target $ota
+  while { [ota step]=="SQLITE_OK" } {}
+  ota close
+}
+
+foreach {tn idx} {
+  1 { }
+  2 { 
+    CREATE INDEX i1 ON t1(a);
+  }
+  3 { 
+    CREATE INDEX i1 ON t1(b, c);
+    CREATE INDEX i2 ON t1(c, b);
+    CREATE INDEX i3 ON t1(a, a, a, b, b, b, c, c, c);
+  }
+} {
+
+  reset_db
+  do_execsql_test 2.$tn.1 {
+    CREATE TABLE t1(a, b, c);
+    INSERT INTO t1 VALUES(1, 2, 3);
+    INSERT INTO t1 VALUES(4, 5, 6);
+    INSERT INTO t1(rowid, a, b, c) VALUES(-1, 'a', 'b', 'c');
+    INSERT INTO t1(rowid, a, b, c) VALUES(-2, 'd', 'e', 'f');
+  }
+
+  db eval $idx
+  
+  do_test 2.$tn.2 {
+    forcedelete ota.db
+    sqlite3 db2 ota.db
+    db2 eval {
+      CREATE TABLE data_t1(ota_rowid, a, b, c, ota_control);
+      INSERT INTO data_t1 VALUES(3, 'x', 'y', 'z', 0);
+      INSERT INTO data_t1 VALUES(NULL, 'X', 'Y', 'Z', 0);
+      INSERT INTO data_t1 VALUES('1', NULL, NULL, NULL, 1);
+      INSERT INTO data_t1 VALUES(-2, NULL, NULL, 'fff', '..x');
+    }
+    db2 close
+  } {}
+  
+  run_ota test.db ota.db
+  
+  do_execsql_test 2.$tn.3 {
+    SELECT rowid, a, b, c FROM t1 ORDER BY rowid;
+  } {
+    -2 d e fff
+    -1 a b c
+     2 4 5 6
+     3 x y z
+     4 X Y Z
+  }
+  
+  integrity_check 2.$tn.4
+}
 
 
 finish_test
index b1bc1d10cc14f04d36564d1c403f3b265f47f89e..a3d73ae67b69a04d45b2a5da363d9400ce672386 100644 (file)
@@ -104,8 +104,11 @@ struct OtaObjIter {
   int nTblCol;                    /* Size of azTblCol[] array */
   char **azTblCol;                /* Array of quoted column names */
   unsigned char *abTblPk;         /* Array of flags - true for PK columns */
+  int eType;
+#if 0
   unsigned char bRowid;           /* True for implicit IPK tables */
   unsigned char bVtab;            /* True for a virtual table */
+#endif
 
   /* Output variables. zTbl==0 implies EOF. */
   int bCleanup;                   /* True in "cleanup" state */
@@ -124,6 +127,14 @@ struct OtaObjIter {
   sqlite3_stmt *pUpdate;          /* Last update statement (or NULL) */
 };
 
+/*
+** Values for OtaObjIter.eType
+*/
+#define OTA_PK_REAL     1         /* Table has a real primary key */
+#define OTA_PK_EXTERNAL 2         /* Table has an external primary key index */
+#define OTA_PK_NONE     3         /* Table has no PK (use rowid) */
+#define OTA_PK_VTAB     4         /* Table is a virtual table (use rowid) */
+
 /*
 ** OTA handle.
 */
@@ -228,8 +239,7 @@ static void otaObjIterFreeCols(OtaObjIter *pIter){
   pIter->nTblCol = 0;
   sqlite3_free(pIter->zMask);
   pIter->zMask = 0;
-  pIter->bRowid = 0;
-  pIter->bVtab = 0;
+  pIter->eType = 0;               /* Invalid value */
 }
 
 /*
@@ -471,11 +481,11 @@ static int otaObjIterGetCols(sqlite3ota *p, OtaObjIter *pIter){
   if( pIter->azTblCol==0 ){
     sqlite3_stmt *pStmt = 0;
     int nCol = 0;
-    int bSeenPk = 0;
     int i;                        /* for() loop iterator variable */
     int rc2;                      /* sqlite3_finalize() return value */
+    int bOtaRowid = 0;            /* If input table has column "ota_rowid" */
 
-    assert( pIter->bRowid==0 && pIter->bVtab==0 );
+    assert( pIter->eType==0 );
 
     /* Populate the azTblCol[] and nTblCol variables based on the columns
     ** of the input table. Ignore any input table columns that begin with
@@ -494,6 +504,9 @@ static int otaObjIterGetCols(sqlite3ota *p, OtaObjIter *pIter){
         pIter->azTblCol[pIter->nTblCol++] = zCopy;
         if( zCopy==0 ) p->rc = SQLITE_NOMEM;
       }
+      else if( 0==sqlite3_stricmp("ota_rowid", zName) ){
+        bOtaRowid = 1;
+      }
     }
     sqlite3_finalize(pStmt);
     pStmt = 0;
@@ -519,20 +532,33 @@ static int otaObjIterGetCols(sqlite3ota *p, OtaObjIter *pIter){
       }else{
         int iPk = sqlite3_column_int(pStmt, 5);
         pIter->abTblPk[i] = (iPk!=0);
-        if( iPk ) bSeenPk = 1;
-        if( iPk<0 ) pIter->bRowid = 1;
+        if( iPk ){
+          pIter->eType = (iPk<0) ? OTA_PK_EXTERNAL : OTA_PK_REAL;
+        }
       }
     }
     rc2 = sqlite3_finalize(pStmt);
     if( p->rc==SQLITE_OK ) p->rc = rc2;
 
-    if( p->rc==SQLITE_OK && bSeenPk==0 ){
-      const char *zTab = pIter->zTbl;
-      if( otaIsVtab(p, zTab) ){
-        pIter->bVtab = 1;
-      }else{
-        p->zErrmsg = sqlite3_mprintf("table %s has no PRIMARY KEY", zTab);
+    if( p->rc==SQLITE_OK ){
+      if( pIter->eType==0 ){
+        /* This must either be a virtual table, or a regular table with no
+        ** PRIMARY KEY declaration whatsoever.  */
+        if( bOtaRowid==0 ){
+          p->rc = SQLITE_ERROR;
+          p->zErrmsg = sqlite3_mprintf(
+              "table data_%q requires ota_rowid column", pIter->zTbl
+          );
+        }else if( otaIsVtab(p, pIter->zTbl) ){
+          pIter->eType = OTA_PK_VTAB;
+        }else{
+          pIter->eType = OTA_PK_NONE;
+        }
+      }else if( bOtaRowid ){
         p->rc = SQLITE_ERROR;
+        p->zErrmsg = sqlite3_mprintf(
+            "table data_%q may not have ota_rowid column", pIter->zTbl
+        );
       }
     }
   }
@@ -618,6 +644,17 @@ static char *otaObjIterGetCollist(
   return zList;
 }
 
+/*
+** Assuming the current table columns are "a", "b" and "c", and the zObj
+** paramter is passed "old", return a string of the form:
+**
+**     "old.a, old.b, old.b"
+**
+** With the column names escaped.
+**
+** For tables with implicit rowids - OTA_PK_EXTERNAL and OTA_PK_NONE, append
+** the text ", old._rowid_" to the returned value.
+*/
 static char *otaObjIterGetOldlist(
   sqlite3ota *p, 
   OtaObjIter *pIter,
@@ -637,21 +674,31 @@ static char *otaObjIterGetOldlist(
     }
 
     /* For a table with implicit rowids, append "old._rowid_" to the list. */
-    if( pIter->bRowid ){
+    if( pIter->eType==OTA_PK_EXTERNAL || pIter->eType==OTA_PK_NONE ){
       zList = sqlite3_mprintf("%z, %s._rowid_", zList, zObj);
     }
   }
   return zList;
 }
 
+/*
+** Return an expression that can be used in a WHERE clause to match the
+** primary key of the current table. For example, if the table is:
+**
+**   CREATE TABLE t1(a, b, c, PRIMARY KEY(b, c));
+**
+** Return the string:
+**
+**   "b = ?1 AND c = ?2"
+*/
 static char *otaObjIterGetWhere(
   sqlite3ota *p, 
   OtaObjIter *pIter
 ){
   char *zList = 0;
   if( p->rc==SQLITE_OK ){
-    if( pIter->bVtab ){
-      zList = otaMPrintfAndCollectError(p, "rowid = ?%d", pIter->nTblCol+1);
+    if( pIter->eType==OTA_PK_VTAB || pIter->eType==OTA_PK_NONE ){
+      zList = otaMPrintfAndCollectError(p, "_rowid_ = ?%d", pIter->nTblCol+1);
     }else{
       const char *zSep = "";
       int i;
@@ -759,7 +806,7 @@ static int otaObjIterPrepareAll(
       int *aiCol;                 /* Column map */
       const char **azColl;        /* Collation sequences */
 
-      assert( pIter->bVtab==0 );
+      assert( pIter->eType!=OTA_PK_VTAB );
 
       /* Create the index writers */
       if( p->rc==SQLITE_OK ){
@@ -777,10 +824,10 @@ static int otaObjIterPrepareAll(
       zCollist = otaObjIterGetCollist(p, pIter, pIter->nCol, aiCol, azColl);
       if( p->rc==SQLITE_OK ){
         char *zSql;
-        if( pIter->bRowid ){
+        if( pIter->eType==OTA_PK_EXTERNAL || pIter->eType==OTA_PK_NONE ){
           zSql = sqlite3_mprintf(
               "SELECT %s, ota_control FROM ota.'ota_tmp_%q' ORDER BY %s%s",
-              zCollist, pIter->zTbl, 
+              zCollist, pIter->zTbl,
               zCollist, zLimit
           );
         }else{
@@ -798,11 +845,13 @@ static int otaObjIterPrepareAll(
         p->rc = prepareFreeAndCollectError(p->db, &pIter->pSelect, pz, zSql);
       }
     }else{
+      int bOtaRowid = (pIter->eType==OTA_PK_VTAB || pIter->eType==OTA_PK_NONE);
       const char *zTbl = pIter->zTbl;
-      char *zBindings = otaObjIterGetBindlist(p, pIter->nTblCol+pIter->bVtab);
       char *zWhere = otaObjIterGetWhere(p, pIter);
       char *zOldlist = otaObjIterGetOldlist(p, pIter, "old");
       char *zNewlist = otaObjIterGetOldlist(p, pIter, "new");
+      char *zBindings = otaObjIterGetBindlist(p, pIter->nTblCol + bOtaRowid);
+
       zCollist = otaObjIterGetCollist(p, pIter, pIter->nTblCol, 0, 0);
       pIter->nCol = pIter->nTblCol;
 
@@ -811,7 +860,7 @@ static int otaObjIterPrepareAll(
         p->rc = prepareFreeAndCollectError(p->db, &pIter->pSelect, pz,
             sqlite3_mprintf(
               "SELECT %s, ota_control%s FROM ota.'data_%q'%s", 
-              zCollist, (pIter->bVtab ? ", ota_rowid" : ""), zTbl, zLimit
+              zCollist, (bOtaRowid ? ", ota_rowid" : ""), zTbl, zLimit
             )
         );
       }
@@ -821,7 +870,7 @@ static int otaObjIterPrepareAll(
         p->rc = prepareFreeAndCollectError(p->db, &pIter->pInsert, pz,
             sqlite3_mprintf(
               "INSERT INTO main.%Q(%s%s) VALUES(%s)", 
-              zTbl, zCollist, (pIter->bVtab ? ", rowid" : ""), zBindings
+              zTbl, zCollist, (bOtaRowid ? ", _rowid_" : ""), zBindings
             )
         );
       }
@@ -835,8 +884,11 @@ static int otaObjIterPrepareAll(
         );
       }
 
-      if( pIter->bVtab==0 ){
-        const char *zOtaRowid = (pIter->bRowid ? ", ota_rowid" : "");
+      if( pIter->eType!=OTA_PK_VTAB ){
+        const char *zOtaRowid = "";
+        if( pIter->eType==OTA_PK_EXTERNAL || pIter->eType==OTA_PK_NONE ){
+          zOtaRowid = ", ota_rowid";
+        }
 
         /* Create the ota_tmp_xxx table and the triggers to populate it. */
         otaMPrintfExec(p, 
@@ -858,12 +910,13 @@ static int otaObjIterPrepareAll(
             "BEGIN "
             "  INSERT INTO 'ota_tmp_%q'(ota_control, %s%s) VALUES(3, %s);"
             "END;"
-            , zTbl, (pIter->bRowid ? ", 0 AS ota_rowid" : ""), zTbl, 
+            , zTbl, (pIter->eType==OTA_PK_EXTERNAL ? ", 0 AS ota_rowid" : "")
+            , zTbl, 
             zTbl, zTbl, zTbl, zCollist, zOtaRowid, zOldlist,
             zTbl, zTbl, zTbl, zCollist, zOtaRowid, zOldlist,
             zTbl, zTbl, zTbl, zCollist, zOtaRowid, zNewlist
         );
-        if( pIter->bRowid ){
+        if( pIter->eType==OTA_PK_EXTERNAL || pIter->eType==OTA_PK_NONE ){
           otaMPrintfExec(p, 
               "CREATE TEMP TRIGGER ota_insert_%q AFTER INSERT ON main.%Q "
               "BEGIN "
@@ -1074,6 +1127,15 @@ static int otaStepType(sqlite3ota *p, const char **pzMask){
   return res;
 }
 
+#ifdef SQLITE_DEBUG
+static void assertColumnName(sqlite3_stmt *pStmt, int iCol, const char *zName){
+  const char *zCol = sqlite3_column_name(pStmt, iCol);
+  assert( 0==sqlite3_stricmp(zName, zCol) );
+}
+#else
+# define assertColumnName(x,y,z)
+#endif
+
 /*
 ** This function does the work for an sqlite3ota_step() call.
 **
@@ -1122,13 +1184,17 @@ static int otaStep(sqlite3ota *p){
         pVal = sqlite3_column_value(pIter->pSelect, i);
         sqlite3_bind_value(pWriter, i+1, pVal);
       }
-      if( pIter->bVtab ){
-        /* For a virtual table, the SELECT statement is:
+      if( pIter->zIdx==0
+       && (pIter->eType==OTA_PK_VTAB || pIter->eType==OTA_PK_NONE) 
+      ){
+        /* For a virtual table, or a table with no primary key, the 
+        ** SELECT statement is:
         **
         **   SELECT <cols>, ota_control, ota_rowid FROM ....
         **
         ** Hence column_value(pIter->nCol+1).
         */
+        assertColumnName(pIter->pSelect, pIter->nCol+1, "ota_rowid");
         pVal = sqlite3_column_value(pIter->pSelect, pIter->nCol+1);
         sqlite3_bind_value(pWriter, pIter->nCol+1, pVal);
       }
@@ -1143,7 +1209,9 @@ static int otaStep(sqlite3ota *p){
           pVal = sqlite3_column_value(pIter->pSelect, i);
           sqlite3_bind_value(pUpdate, i+1, pVal);
         }
-        if( pIter->bVtab ){
+        if( pIter->eType==OTA_PK_VTAB || pIter->eType==OTA_PK_NONE ){
+          /* Bind the ota_rowid value to column _rowid_ */
+          assertColumnName(pIter->pSelect, pIter->nCol+1, "ota_rowid");
           pVal = sqlite3_column_value(pIter->pSelect, pIter->nCol+1);
           sqlite3_bind_value(pUpdate, pIter->nCol+1, pVal);
         }
@@ -1195,7 +1263,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.  */
-            if( pIter->bVtab==0 ){
+            if( pIter->eType!=OTA_PK_VTAB ){
               otaMPrintfExec(p, "DELETE FROM ota.'ota_tmp_%q'", pIter->zTbl);
             }
           }else{
index 00072e021c696b56a57cd7906799aa5105573b86..9e9bafcb13a929608f6d476195a29d30a88af3c3 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Update\sota\sso\sthat\sthe\shidden\scolumns\sof\svirtual\stables\smay\sbe\swritten.
-D 2014-11-27T18:09:46.630
+C Allow\sthe\sota\sextension\sto\swrite\sto\stables\swith\sno\sPRIMARY\sKEY\sdeclaration.
+D 2014-12-06T19:30:41.673
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in a226317fdf3f4c895fb3cfedc355b4d0868ce1fb
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -125,7 +125,7 @@ F ext/misc/vtshim.c babb0dc2bf116029e3e7c9a618b8a1377045303e
 F ext/misc/wholenumber.c 784b12543d60702ebdd47da936e278aa03076212
 F ext/ota/README.txt 78d4a9f78f567d4bf826cf0f02df6254902562ca
 F ext/ota/ota.c c11a85af71dccc45976622fe7a51169a481caa91
-F ext/ota/ota1.test a8f9d89c9b2d381a663bcedaa5dd5952cdbd1231
+F ext/ota/ota1.test 64770d76d3dc00c24f9a78ac69e4448708bde985
 F ext/ota/ota10.test ab815dff9cef7248c504f06b888627d236f25e9c
 F ext/ota/ota2.test 4568c2671d19dbde789fb9091d727a2e94880128
 F ext/ota/ota3.test 71bd8cc0cf8d7e7d9bb11a1fcc238320a5a9d8c8
@@ -134,9 +134,9 @@ F ext/ota/ota5.test ad0799daf8923ddebffe75ae8c5504ca90b7fadb
 F ext/ota/ota6.test 82f1f757ec9b2ad07d6de4060b8e3ba8e44dfdd3
 F ext/ota/ota7.test 1fe2c5761705374530e29f70c39693076028221a
 F ext/ota/ota8.test cd70e63a0c29c45c0906692827deafa34638feda
-F ext/ota/ota9.test d9ad30ccb4e08f878e382876fe67752309538af9
+F ext/ota/ota9.test d3eee95dd836824d07a22e5efcdb7bf6e869358b
 F ext/ota/otafault.test be02466863015a583cc0ceb6aca871a5e6f7a71b
-F ext/ota/sqlite3ota.c f1e930690ec2bcb72138301ebf05ea2ffd4450de
+F ext/ota/sqlite3ota.c 84cab0f965144772068ec0183252ae5e5278f0be
 F ext/ota/sqlite3ota.h 8dc9c812e61f47f497336a45f820bee88b33a2c5
 F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
 F ext/rtree/rtree.c 57bec53e1a677ab74217fe1f20a58c3a47261d6b
@@ -1237,7 +1237,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 600cefdd4d29c1de4d107fa7ddeb76a18edce4f5
-R 19cf180a791a327f1f4fb68723a586ef
+P ccee999649d0fa1d48e53847542f4cbe05e3d694
+R 23b9af0814440584d410f9d611aafb5a
 U dan
-Z 7646a22500cce7c7ef211b057e34ff27
+Z b3bc69908e00e34016dd9eb2dbbe05c2
index 0c798bdfcc68e6bdb984c1e2744f48935fdb4440..b63e4f6272def73351049d4a9a53d35895ca8cab 100644 (file)
@@ -1 +1 @@
-ccee999649d0fa1d48e53847542f4cbe05e3d694
\ No newline at end of file
+ba59a7e2ba97244492cbca9247456df0f3f19248
\ No newline at end of file