]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Allow RBU tables to be named "data[0-9]*_<target>" instead of strictly "data_<target...
authordan <dan@noemail.net>
Thu, 30 Jul 2015 11:38:19 +0000 (11:38 +0000)
committerdan <dan@noemail.net>
Thu, 30 Jul 2015 11:38:19 +0000 (11:38 +0000)
FossilOrigin-Name: 287aa30601506f168d355c35176a3383474444ca

ext/rbu/rbufts.test [new file with mode: 0644]
ext/rbu/sqlite3rbu.c
ext/rbu/sqlite3rbu.h
manifest
manifest.uuid

diff --git a/ext/rbu/rbufts.test b/ext/rbu/rbufts.test
new file mode 100644 (file)
index 0000000..e2526a0
--- /dev/null
@@ -0,0 +1,80 @@
+# 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.
+#
+#***********************************************************************
+#
+# This file contains tests for the RBU module. More specifically, it
+# contains tests to ensure that RBU works with FTS tables.
+#
+
+if {![info exists testdir]} {
+  set testdir [file join [file dirname [info script]] .. .. test]
+}
+source $testdir/tester.tcl
+set ::testprefix rbufts
+
+ifcapable !fts3 {
+  finish_test
+  return
+}
+
+proc step_rbu {target rbu} {
+  while 1 {
+    sqlite3rbu rbu $target $rbu
+    set rc [rbu step]
+    rbu close
+    if {$rc != "SQLITE_OK"} break
+  }
+  set rc
+}
+
+do_execsql_test 1.0 {
+  CREATE TABLE t1(i INTEGER PRIMARY KEY, a, b);
+  CREATE VIRTUAL TABLE xx USING fts4(content=t1, a, b);
+  INSERT INTO t1(rowid, a, b) VALUES(10, 'a b c', 'c b a');
+  INSERT INTO t1(rowid, a, b) VALUES(20, 'a b c', 'd e f');
+  INSERT INTO t1(rowid, a, b) VALUES(30, 'd e f', 'a b c');
+  INSERT INTO t1(rowid, a, b) VALUES(40, 'd e f', 'd e f');
+}
+
+do_execsql_test 1.1 {
+  INSERT INTO xx(xx) VALUES('rebuild');
+  INSERT INTO xx(xx) VALUES('integrity-check');
+}
+
+forcedelete rbu.db
+do_test 2.0 {
+  sqlite3 dbrbu rbu.db
+  dbrbu eval {
+    CREATE TABLE data_t1(i, a, b, rbu_control);
+    INSERT INTO data_t1 VALUES(20, NULL, NULL, 1);        -- delete
+    INSERT INTO data_t1 VALUES(30, 'x y z', NULL, '.x.'); -- update
+    INSERT INTO data_t1 VALUES(50, '1 2 3', 'x y z', 0);  -- insert
+
+    CREATE VIEW data0_xx AS 
+    SELECT i AS rbu_rowid, a, b, 
+    CASE WHEN rbu_control IN (0, 1) 
+    THEN rbu_control ELSE substr(rbu_control, 2) END AS rbu_control
+    FROM data_t1;
+
+  }
+  dbrbu close
+
+  step_rbu test.db rbu.db
+} {SQLITE_DONE}
+
+do_execsql_test 2.1 {
+  INSERT INTO xx(xx) VALUES('integrity-check');
+}
+
+
+
+
+finish_test
+
index 491313ad9a3b3b20a5194f7c5a731932dc804ccc..1598ed315ecd1a1cf1376d76743998693035a675 100644 (file)
@@ -239,6 +239,7 @@ struct RbuObjIter {
   /* Output variables. zTbl==0 implies EOF. */
   int bCleanup;                   /* True in "cleanup" state */
   const char *zTbl;               /* Name of target db table */
+  const char *zDataTbl;           /* Name of rbu db table (or null) */
   const char *zIdx;               /* Name of target db index (or null) */
   int iTnum;                      /* Root page of current object */
   int iPkTnum;                    /* If eType==EXTERNAL, root of PK index */
@@ -249,7 +250,7 @@ struct RbuObjIter {
   sqlite3_stmt *pSelect;          /* Source data */
   sqlite3_stmt *pInsert;          /* Statement for INSERT operations */
   sqlite3_stmt *pDelete;          /* Statement for DELETE ops */
-  sqlite3_stmt *pTmpInsert;       /* Insert into rbu_tmp_$zTbl */
+  sqlite3_stmt *pTmpInsert;       /* Insert into rbu_tmp_$zDataTbl */
 
   /* Last UPDATE used (for PK b-tree updates only), or NULL. */
   RbuUpdateStmt *pRbuUpdate;
@@ -526,7 +527,8 @@ static int rbuObjIterNext(sqlite3rbu *p, RbuObjIter *pIter){
           pIter->zTbl = 0;
         }else{
           pIter->zTbl = (const char*)sqlite3_column_text(pIter->pTblIter, 0);
-          rc = pIter->zTbl ? SQLITE_OK : SQLITE_NOMEM;
+          pIter->zDataTbl = (const char*)sqlite3_column_text(pIter->pTblIter,1);
+          rc = (pIter->zDataTbl && pIter->zTbl) ? SQLITE_OK : SQLITE_NOMEM;
         }
       }else{
         if( pIter->zIdx==0 ){
@@ -557,6 +559,40 @@ static int rbuObjIterNext(sqlite3rbu *p, RbuObjIter *pIter){
   return rc;
 }
 
+
+/*
+** The implementation of the rbu_target_name() SQL function. This function
+** accepts one argument - the name of a table in the RBU database. If the
+** table name matches the pattern:
+**
+**     data[0-9]_<name>
+**
+** where <name> is any sequence of 1 or more characters, <name> is returned.
+** Otherwise, if the only argument does not match the above pattern, an SQL
+** NULL is returned.
+**
+**     "data_t1"     -> "t1"
+**     "data0123_t2" -> "t2"
+**     "dataAB_t3"   -> NULL
+*/
+static void rbuTargetNameFunc(
+  sqlite3_context *context,
+  int argc,
+  sqlite3_value **argv
+){
+  const char *zIn;
+  assert( argc==1 );
+
+  zIn = (const char*)sqlite3_value_text(argv[0]);
+  if( zIn && strlen(zIn)>4 && memcmp("data", zIn, 4)==0 ){
+    int i;
+    for(i=4; zIn[i]>='0' && zIn[i]<='9'; i++);
+    if( zIn[i]=='_' && zIn[i+1] ){
+      sqlite3_result_text(context, &zIn[i+1], -1, SQLITE_STATIC);
+    }
+  }
+}
+
 /*
 ** Initialize the iterator structure passed as the second argument.
 **
@@ -570,8 +606,9 @@ static int rbuObjIterFirst(sqlite3rbu *p, RbuObjIter *pIter){
   memset(pIter, 0, sizeof(RbuObjIter));
 
   rc = prepareAndCollectError(p->dbRbu, &pIter->pTblIter, &p->zErrmsg, 
-      "SELECT substr(name, 6) FROM sqlite_master "
-      "WHERE type IN ('table', 'view') AND name LIKE 'data_%'"
+      "SELECT rbu_target_name(name) AS target, name FROM sqlite_master "
+      "WHERE type IN ('table', 'view') AND target IS NOT NULL "
+      "ORDER BY name"
   );
 
   if( rc==SQLITE_OK ){
@@ -917,7 +954,7 @@ static int rbuObjIterCacheTableInfo(sqlite3rbu *p, RbuObjIter *pIter){
     ** of the input table. Ignore any input table columns that begin with
     ** "rbu_".  */
     p->rc = prepareFreeAndCollectError(p->dbRbu, &pStmt, &p->zErrmsg, 
-        sqlite3_mprintf("SELECT * FROM 'data_%q'", pIter->zTbl)
+        sqlite3_mprintf("SELECT * FROM '%q'", pIter->zDataTbl)
     );
     if( p->rc==SQLITE_OK ){
       nCol = sqlite3_column_count(pStmt);
@@ -942,7 +979,7 @@ static int rbuObjIterCacheTableInfo(sqlite3rbu *p, RbuObjIter *pIter){
     ){
       p->rc = SQLITE_ERROR;
       p->zErrmsg = sqlite3_mprintf(
-          "table data_%q %s rbu_rowid column", pIter->zTbl,
+          "table %q %s rbu_rowid column", pIter->zDataTbl,
           (bRbuRowid ? "may not have" : "requires")
       );
     }
@@ -963,8 +1000,8 @@ static int rbuObjIterCacheTableInfo(sqlite3rbu *p, RbuObjIter *pIter){
       }
       if( i==pIter->nTblCol ){
         p->rc = SQLITE_ERROR;
-        p->zErrmsg = sqlite3_mprintf("column missing from data_%q: %s",
-            pIter->zTbl, zName
+        p->zErrmsg = sqlite3_mprintf("column missing from %q: %s",
+            pIter->zDataTbl, zName
         );
       }else{
         int iPk = sqlite3_column_int(pStmt, 5);
@@ -1519,7 +1556,7 @@ static void rbuObjIterPrepareTmpInsert(
     p->rc = prepareFreeAndCollectError(
         p->dbRbu, &pIter->pTmpInsert, &p->zErrmsg, sqlite3_mprintf(
           "INSERT INTO %s.'rbu_tmp_%q'(rbu_control,%s%s) VALUES(%z)", 
-          p->zStateDb, pIter->zTbl, zCollist, zRbuRowid, zBind
+          p->zStateDb, pIter->zDataTbl, zCollist, zRbuRowid, zBind
     ));
   }
 }
@@ -1615,18 +1652,18 @@ static int rbuObjIterPrepareAll(
         if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){
           zSql = sqlite3_mprintf(
               "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' ORDER BY %s%s",
-              zCollist, p->zStateDb, pIter->zTbl,
+              zCollist, p->zStateDb, pIter->zDataTbl,
               zCollist, zLimit
           );
         }else{
           zSql = sqlite3_mprintf(
-              "SELECT %s, rbu_control FROM 'data_%q' "
+              "SELECT %s, rbu_control FROM '%q' "
               "WHERE typeof(rbu_control)='integer' AND rbu_control!=1 "
               "UNION ALL "
               "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' "
               "ORDER BY %s%s",
-              zCollist, pIter->zTbl, 
-              zCollist, p->zStateDb, pIter->zTbl, 
+              zCollist, pIter->zDataTbl, 
+              zCollist, p->zStateDb, pIter->zDataTbl, 
               zCollist, zLimit
           );
         }
@@ -1654,8 +1691,9 @@ static int rbuObjIterPrepareAll(
       if( p->rc==SQLITE_OK ){
         p->rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pSelect, pz,
             sqlite3_mprintf(
-              "SELECT %s, rbu_control%s FROM 'data_%q'%s", 
-              zCollist, (bRbuRowid ? ", rbu_rowid" : ""), zTbl, zLimit
+              "SELECT %s, rbu_control%s FROM '%q'%s", 
+              zCollist, (bRbuRowid ? ", rbu_rowid" : ""), 
+              pIter->zDataTbl, zLimit
             )
         );
       }
@@ -1693,10 +1731,10 @@ static int rbuObjIterPrepareAll(
         /* Create the rbu_tmp_xxx table and the triggers to populate it. */
         rbuMPrintfExec(p, p->dbRbu,
             "CREATE TABLE IF NOT EXISTS %s.'rbu_tmp_%q' AS "
-            "SELECT *%s FROM 'data_%q' WHERE 0;"
-            , p->zStateDb
-            , zTbl, (pIter->eType==RBU_PK_EXTERNAL ? ", 0 AS rbu_rowid" : "")
-            , zTbl
+            "SELECT *%s FROM '%q' WHERE 0;"
+            , p->zStateDb, pIter->zDataTbl
+            , (pIter->eType==RBU_PK_EXTERNAL ? ", 0 AS rbu_rowid" : "")
+            , pIter->zDataTbl
         );
 
         rbuMPrintfExec(p, p->dbMain,
@@ -1862,6 +1900,12 @@ static void rbuOpenDatabase(sqlite3rbu *p){
     );
   }
 
+  if( p->rc==SQLITE_OK ){
+    p->rc = sqlite3_create_function(p->dbRbu, 
+        "rbu_target_name", 1, SQLITE_UTF8, (void*)p, rbuTargetNameFunc, 0, 0
+    );
+  }
+
   if( p->rc==SQLITE_OK ){
     p->rc = sqlite3_file_control(p->dbMain, "main", SQLITE_FCNTL_RBU, (void*)p);
   }
@@ -2403,7 +2447,7 @@ int sqlite3rbu_step(sqlite3rbu *p){
             ** But the contents can be deleted.  */
             if( pIter->abIndexed ){
               rbuMPrintfExec(p, p->dbRbu, 
-                  "DELETE FROM %s.'rbu_tmp_%q'", p->zStateDb, pIter->zTbl
+                  "DELETE FROM %s.'rbu_tmp_%q'", p->zStateDb, pIter->zDataTbl
               );
             }
           }else{
index c3e32f941007482d21a57be702c54a1533ddf6f2..d80e1824246ec66256c878aa8c11a698ef7eb285 100644 (file)
 **
 ** The order of the columns in the data_% table does not matter.
 **
+** Instead of a regular table, the RBU database may also contain virtual
+** tables or view named using the data_<target> naming scheme. 
+**
+** Instead of the plain data_<target> naming scheme, RBU database tables 
+** may also be named data<integer>_<target>, where <integer> is any sequence
+** of zero or more numeric characters (0-9). This can be significant because
+** tables within the RBU database are always processed in order sorted by 
+** name. By judicious selection of the the <integer> portion of the names
+** of the RBU tables the user can therefore control the order in which they
+** are processed. This can be useful, for example, to ensure that "external
+** content" FTS4 tables are updated before their underlying content tables.
+**
 ** If the target database table is a virtual table or a table that has no
 ** PRIMARY KEY declaration, the data_% table must also contain a column 
 ** named "rbu_rowid". This column is mapped to the tables implicit primary 
index de30096e247d4a9d91a1bddeaf0e1c12ef33a873..922ed843dc18d956117778e37278749e440b26af 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Return\san\serror\smessage\s(instead\sof\ssegfaulting)\sif\stest\sfunction\sfts5_expr()\sis\sinvoked\swith\sno\sarguments.
-D 2015-07-30T11:26:10.463
+C Allow\sRBU\stables\sto\sbe\snamed\s"data[0-9]*_<target>"\sinstead\sof\sstrictly\s"data_<target>".\sAlso\supdate\sRBU\sso\sthat\sit\salways\sprocesses\sdata\stables\sin\sorder\ssorted\sby\sname.
+D 2015-07-30T11:38:19.207
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 4de3ef40c8b3b75c0c55ff4242a43c8ce1ad90ee
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -217,8 +217,9 @@ F ext/rbu/rbuA.test c1a7b3e2d926b8f8448bb3b4ae787e314ee4b2b3
 F ext/rbu/rbucrash.test 8d2ed5d4b05fef6c00c2a6b5f7ead71fa172a695
 F ext/rbu/rbufault.test cc0be8d5d392d98b0c2d6a51be377ea989250a89
 F ext/rbu/rbufault2.test 9a7f19edd6ea35c4c9f807d8a3db0a03a5670c06
-F ext/rbu/sqlite3rbu.c dbd7e4b31821398dcdeb21492970401ff1027881
-F ext/rbu/sqlite3rbu.h 6a280298e9eeb8ef59841a620f07f4f844651545
+F ext/rbu/rbufts.test 17db7d968b3d073788bcba044c498d09e830726b
+F ext/rbu/sqlite3rbu.c 75409b33f20ef6acd3689206a707de79e17a8628
+F ext/rbu/sqlite3rbu.h 67977687c3acf995295f5f461809cac030098afd
 F ext/rbu/test_rbu.c f99698956cc9158d6bf865e461e2d15876538ac1
 F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761
 F ext/rtree/rtree.c 0f9b595bd0debcbedf1d7a63d0e0678d619e6c9c
@@ -1366,7 +1367,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 96559adbf18bbbf25ff4e1579ee3ff2afef4c4d7
-R 47e11f7f15f9372decc5bbb9c166f3bf
+P 8e818b8985c0196cd9671a6491796faaeebeb16e
+R d2e717e7df793b66cba7be5b1c5545b9
 U dan
-Z e4fcfe99ec97c4ebc5a25850df719a0d
+Z b15734d6bf397de4fcabb387cb6f82da
index 881a91aac64d2203f9554fa9ce79f4755ee27235..edf94751640d46a2496a66d24432f19954e8f068 100644 (file)
@@ -1 +1 @@
-8e818b8985c0196cd9671a6491796faaeebeb16e
\ No newline at end of file
+287aa30601506f168d355c35176a3383474444ca
\ No newline at end of file