]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Update ota so that the hidden columns of virtual tables may be written.
authordan <dan@noemail.net>
Thu, 27 Nov 2014 18:09:46 +0000 (18:09 +0000)
committerdan <dan@noemail.net>
Thu, 27 Nov 2014 18:09:46 +0000 (18:09 +0000)
FossilOrigin-Name: ccee999649d0fa1d48e53847542f4cbe05e3d694

ext/ota/ota10.test [new file with mode: 0644]
ext/ota/ota3.test
ext/ota/sqlite3ota.c
ext/ota/sqlite3ota.h
manifest
manifest.uuid

diff --git a/ext/ota/ota10.test b/ext/ota/ota10.test
new file mode 100644 (file)
index 0000000..bca34d6
--- /dev/null
@@ -0,0 +1,122 @@
+# 2014 August 30
+#
+# 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.
+#
+#***********************************************************************
+#
+
+if {![info exists testdir]} {
+  set testdir [file join [file dirname [info script]] .. .. test]
+}
+source $testdir/tester.tcl
+set ::testprefix ota10
+
+
+#--------------------------------------------------------------------
+# Test that UPDATE commands work even if the input columns are in a 
+# different order to the output columns. 
+#
+do_execsql_test 1.0 {
+  CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
+  INSERT INTO t1 VALUES(1, 'b', 'c');
+}
+
+proc apply_ota {sql} {
+  forcedelete ota.db
+  sqlite3 db2 ota.db
+  db2 eval $sql
+  db2 close
+  sqlite3ota ota test.db ota.db
+  while { [ota step]=="SQLITE_OK" } {}
+  ota close
+}
+
+do_test 1.1 {
+  apply_ota {
+    CREATE TABLE data_t1(a, c, b, ota_control);
+    INSERT INTO data_t1 VALUES(1, 'xxx', NULL, '.x.');
+  }
+  db eval { SELECT * FROM t1 }
+} {1 b xxx}
+
+#--------------------------------------------------------------------
+# Test that the hidden languageid column of an fts4 table can be 
+# written.
+#
+ifcapable fts3 {
+  do_execsql_test 2.0 {
+    CREATE VIRTUAL TABLE ft USING fts4(a, b, languageid='langid');
+  }
+  do_test 2.1 {
+    apply_ota {
+      CREATE TABLE data_ft(a, b, ota_rowid, langid, ota_control);
+      INSERT INTO data_ft VALUES('a', 'b', 22, 1, 0);    -- insert
+      INSERT INTO data_ft VALUES('a', 'b', 23, 10, 0);   -- insert
+      INSERT INTO data_ft VALUES('a', 'b', 24, 100, 0);  -- insert
+    }
+    db eval { SELECT a, b, rowid, langid FROM ft }
+  } [list {*}{
+    a b 22 1
+    a b 23 10
+    a b 24 100
+  }]
+  
+  # Or not - this data_xxx table has no langid column, so langid 
+  # defaults to 0.
+  #
+  do_test 2.2 {
+    apply_ota {
+      CREATE TABLE data_ft(a, b, ota_rowid, ota_control);
+      INSERT INTO data_ft VALUES('a', 'b', 25, 0);    -- insert
+    }
+    db eval { SELECT a, b, rowid, langid FROM ft }
+  } [list {*}{
+    a b 22 1
+    a b 23 10
+    a b 24 100
+    a b 25 0
+  }]
+  
+  # Update langid.
+  #
+  do_test 2.3 {
+    apply_ota {
+      CREATE TABLE data_ft(a, b, ota_rowid, langid, ota_control);
+      INSERT INTO data_ft VALUES(NULL, NULL, 23, 50, '..x');
+      INSERT INTO data_ft VALUES(NULL, NULL, 25, 500, '..x');
+    }
+    db eval { SELECT a, b, rowid, langid FROM ft }
+  } [list {*}{
+    a b 22 1
+    a b 23 50
+    a b 24 100
+    a b 25 500
+  }]
+}
+
+#--------------------------------------------------------------------
+# Test that if writing a hidden virtual table column is an error, 
+# attempting to do so via ota is also an error.
+#
+ifcapable fts3 {
+  do_execsql_test 3.0 {
+    CREATE VIRTUAL TABLE xt USING fts4(a);
+  }
+  do_test 3.1 {
+    list [catch {
+      apply_ota {
+        CREATE TABLE data_xt(a, xt, ota_rowid, ota_control);
+        INSERT INTO data_xt VALUES('a', 'b', 1, 0);
+      }
+    } msg] $msg
+  } {1 {SQLITE_ERROR - SQL logic error or missing database}}
+}
+
+
+
+finish_test
index 09d1bab083ef87a7f571bcd6d7827b31b54ee172..8921c12b359596644c03ca695909486d64506530 100644 (file)
@@ -96,7 +96,7 @@ do_test 2.1 {
   }
   db2 close
   list [catch { run_ota test.db ota.db } msg] $msg
-} {1 {SQLITE_ERROR - no such column: c}}
+} {1 {SQLITE_ERROR - column missing from data_x1: c}}
 
 do_execsql_test 2.2 {
   PRAGMA integrity_check;
index 6c3c99434e6e39f29e528a7d36ea2ee9b30ebf0c..b1bc1d10cc14f04d36564d1c403f3b265f47f89e 100644 (file)
@@ -223,7 +223,6 @@ static void otaObjIterFreeCols(OtaObjIter *pIter){
     sqlite3_free(pIter->azTblCol[i]);
   }
   sqlite3_free(pIter->azTblCol);
-  sqlite3_free(pIter->abTblPk);
   pIter->azTblCol = 0;
   pIter->abTblPk = 0;
   pIter->nTblCol = 0;
@@ -369,6 +368,24 @@ static char *otaQuoteName(const char *zName){
   return zRet;
 }
 
+/*
+** Argument zName points to a column name. Argument zQuoted also points
+** to a column name, but one that has been quoted using otaQuoteName().
+** Return true if the column names are the same, or false otherwise.
+*/
+static int otaMatchName(const char *zName, const char *zQuoted){
+  const char *p = zName;
+  const char *q = &zQuoted[1];
+  while( 1 ){
+    if( *q=='`' ) q++;
+    if( sqlite3_strnicmp(q, p, 1) ) return 0;
+    if( !*q ) break;
+    p++;
+    q++;
+  }
+  return 1;
+}
+
 /*
 ** Argument zFmt is a sqlite3_mprintf() style format string. The trailing
 ** arguments are the usual subsitution values. This function performs
@@ -396,21 +413,22 @@ static int otaMPrintfExec(sqlite3ota *p, const char *zFmt, ...){
 }
 
 /*
-** Increase the size of the pIter->azTblCol[] and abTblPk[] arrays so that
+** Allocate and zero the pIter->azTblCol[] and abTblPk[] arrays so that
 ** there is room for at least nCol elements. If an OOM occurs, store an
 ** error code in the OTA handle passed as the first argument.
 */
-static void otaExtendIterArrays(sqlite3ota *p, OtaObjIter *pIter, int nCol){
+static void otaAllocateIterArrays(sqlite3ota *p, OtaObjIter *pIter, int nCol){
+  int nByte = sizeof(char*) * nCol + sizeof(unsigned char*) * nCol;
+  char **azNew;
+
   assert( p->rc==SQLITE_OK );
-  if( (nCol % 8)==0 ){
-    unsigned char *abNew;
-    int nByte = sizeof(char*) * (nCol+8);
-    char **azNew = (char**)sqlite3_realloc(pIter->azTblCol, nByte);
-    abNew = (unsigned char*)sqlite3_realloc(pIter->abTblPk, nCol+8);
-
-    if( azNew ) pIter->azTblCol = azNew;
-    if( abNew ) pIter->abTblPk = abNew;
-    if( azNew==0 || abNew==0 ) p->rc = SQLITE_NOMEM;
+  azNew = (char**)sqlite3_malloc(nByte);
+  if( azNew ){
+    memset(azNew, 0, nByte);
+    pIter->azTblCol = azNew;
+    pIter->abTblPk = (unsigned char*)&pIter->azTblCol[nCol];
+  }else{
+    p->rc = SQLITE_NOMEM;
   }
 }
 
@@ -451,30 +469,60 @@ static int otaIsVtab(sqlite3ota *p, const char *zTab){
 */
 static int otaObjIterGetCols(sqlite3ota *p, OtaObjIter *pIter){
   if( pIter->azTblCol==0 ){
-    sqlite3_stmt *pStmt;
-    char *zSql;
+    sqlite3_stmt *pStmt = 0;
     int nCol = 0;
     int bSeenPk = 0;
+    int i;                        /* for() loop iterator variable */
     int rc2;                      /* sqlite3_finalize() return value */
 
     assert( pIter->bRowid==0 && pIter->bVtab==0 );
 
-    zSql = sqlite3_mprintf("PRAGMA main.table_info(%Q)", pIter->zTbl);
-    p->rc = prepareFreeAndCollectError(p->db, &pStmt, &p->zErrmsg, zSql);
+    /* Populate the azTblCol[] and nTblCol variables based on the columns
+    ** of the input table. Ignore any input table columns that begin with
+    ** "ota_".  */
+    p->rc = prepareFreeAndCollectError(p->db, &pStmt, &p->zErrmsg, 
+        sqlite3_mprintf("SELECT * FROM 'data_%q'", pIter->zTbl)
+    );
+    if( p->rc==SQLITE_OK ){
+      nCol = sqlite3_column_count(pStmt);
+      otaAllocateIterArrays(p, pIter, nCol);
+    }
+    for(i=0; p->rc==SQLITE_OK && i<nCol; i++){
+      const char *zName = (const char*)sqlite3_column_name(pStmt, i);
+      if( sqlite3_strnicmp("ota_", zName, 4) ){
+        char *zCopy = otaQuoteName(zName);
+        pIter->azTblCol[pIter->nTblCol++] = zCopy;
+        if( zCopy==0 ) p->rc = SQLITE_NOMEM;
+      }
+    }
+    sqlite3_finalize(pStmt);
+    pStmt = 0;
+
+    /* Check that all non-HIDDEN columns in the destination table are also
+    ** present in the input table. Populate the abTblPk[] array at the
+    ** same time.  */
+    if( p->rc==SQLITE_OK ){
+      p->rc = prepareFreeAndCollectError(p->db, &pStmt, &p->zErrmsg, 
+          sqlite3_mprintf("PRAGMA main.table_info(%Q)", pIter->zTbl)
+      );
+    }
     while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
-      otaExtendIterArrays(p, pIter, nCol);
-      if( p->rc==SQLITE_OK ){
-        const char *zName = (const char*)sqlite3_column_text(pStmt, 1);
+      const char *zName = (const char*)sqlite3_column_text(pStmt, 1);
+      for(i=0; i<pIter->nTblCol; i++){
+        if( otaMatchName(zName, pIter->azTblCol[i]) ) break;
+      }
+      if( i==pIter->nTblCol ){
+        p->rc = SQLITE_ERROR;
+        p->zErrmsg = sqlite3_mprintf("column missing from data_%q: %s",
+            pIter->zTbl, zName
+        );
+      }else{
         int iPk = sqlite3_column_int(pStmt, 5);
-        pIter->abTblPk[nCol] = (iPk!=0);
+        pIter->abTblPk[i] = (iPk!=0);
         if( iPk ) bSeenPk = 1;
         if( iPk<0 ) pIter->bRowid = 1;
-        pIter->azTblCol[nCol] = otaQuoteName(zName);
-        if( pIter->azTblCol[nCol]==0 ) p->rc = SQLITE_NOMEM;
-        nCol++;
       }
     }
-    pIter->nTblCol = nCol;
     rc2 = sqlite3_finalize(pStmt);
     if( p->rc==SQLITE_OK ) p->rc = rc2;
 
index 8a201f25bd34da569ccca63e6fedeb7c1fb2f722..a652316ca2120607273f88abe8d2fa0d17fd5c0f 100644 (file)
 **
 **   CREATE TABLE data_ft1(a, b, ota_rowid, ota_control);
 **
+** All non-hidden columns (i.e. all columns matched by "SELECT *") of the
+** target table must be present in the input table. For virtual tables,
+** hidden columns are optional - they are updated by OTA if present in
+** the input table, or not otherwise. For example, to write to an fts4
+** table with a hidden languageid column such as:
+**
+**   CREATE VIRTUAL TABLE ft1 USING fts4(a, b, languageid='langid');
+**
+** Either of the following input table schemas may be used:
+**
+**   CREATE TABLE data_ft1(a, b, langid, ota_rowid, ota_control);
+**   CREATE TABLE data_ft1(a, b, ota_rowid, ota_control);
+**
 ** For each row to INSERT into the target database as part of the OTA 
 ** update, the corresponding data_% table should contain a single record
 ** with the "ota_control" column set to contain integer value 0. The
index 5d142cd5c1f0d600012cfec1be169733c26a4723..00072e021c696b56a57cd7906799aa5105573b86 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sSQLITE_ENABLE_OTA\spre-processor\sdirectives\sso\sthat\sthis\sbranch\smay\sbe\scompiled\swith\sor\swithout\sOTA.
-D 2014-11-22T09:09:50.320
+C Update\sota\sso\sthat\sthe\shidden\scolumns\sof\svirtual\stables\smay\sbe\swritten.
+D 2014-11-27T18:09:46.630
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in a226317fdf3f4c895fb3cfedc355b4d0868ce1fb
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -126,8 +126,9 @@ 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/ota10.test ab815dff9cef7248c504f06b888627d236f25e9c
 F ext/ota/ota2.test 4568c2671d19dbde789fb9091d727a2e94880128
-F ext/ota/ota3.test 215dd4a8e238567e0f890a5139b6fdf5494ef311
+F ext/ota/ota3.test 71bd8cc0cf8d7e7d9bb11a1fcc238320a5a9d8c8
 F ext/ota/ota4.test 60f897f329a6782ef2f24862640acf3c52e48077
 F ext/ota/ota5.test ad0799daf8923ddebffe75ae8c5504ca90b7fadb
 F ext/ota/ota6.test 82f1f757ec9b2ad07d6de4060b8e3ba8e44dfdd3
@@ -135,8 +136,8 @@ F ext/ota/ota7.test 1fe2c5761705374530e29f70c39693076028221a
 F ext/ota/ota8.test cd70e63a0c29c45c0906692827deafa34638feda
 F ext/ota/ota9.test d9ad30ccb4e08f878e382876fe67752309538af9
 F ext/ota/otafault.test be02466863015a583cc0ceb6aca871a5e6f7a71b
-F ext/ota/sqlite3ota.c accfada2ab182dc52225e7345f656520a4e8db22
-F ext/ota/sqlite3ota.h 04577b00c456aacb99be9c8b55572a6e3ca9aa27
+F ext/ota/sqlite3ota.c f1e930690ec2bcb72138301ebf05ea2ffd4450de
+F ext/ota/sqlite3ota.h 8dc9c812e61f47f497336a45f820bee88b33a2c5
 F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
 F ext/rtree/rtree.c 57bec53e1a677ab74217fe1f20a58c3a47261d6b
 F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e
@@ -1236,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 7ef44c5b5bd30bcc4ef59ed172b9ce9ac6a843f6
-R a969a1e9314cd2ef179f3839efd0b8ec
+P 600cefdd4d29c1de4d107fa7ddeb76a18edce4f5
+R 19cf180a791a327f1f4fb68723a586ef
 U dan
-Z bba2f4cd69d571c58d439710133b8bb2
+Z 7646a22500cce7c7ef211b057e34ff27
index 3c2a061073e6326cc958209e164e0bcb70550adc..0c798bdfcc68e6bdb984c1e2744f48935fdb4440 100644 (file)
@@ -1 +1 @@
-600cefdd4d29c1de4d107fa7ddeb76a18edce4f5
\ No newline at end of file
+ccee999649d0fa1d48e53847542f4cbe05e3d694
\ No newline at end of file