]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add the optional non-found-callback to the swarm-vtab. union-vtab
authordrh <drh@noemail.net>
Fri, 4 Aug 2017 20:15:08 +0000 (20:15 +0000)
committerdrh <drh@noemail.net>
Fri, 4 Aug 2017 20:15:08 +0000 (20:15 +0000)
FossilOrigin-Name: a94e2f600bc766fb459418e674b842628ba21e27cf9942c00cd533507d7b35fe

ext/misc/unionvtab.c
manifest
manifest.uuid
test/swarmvtab2.test [new file with mode: 0644]

index c4e3fff039a7bf3a5216ca768b34d02ecdf9e039..bad82295c151bebad7078c53f749f839253dd3f0 100644 (file)
@@ -36,7 +36,7 @@
 **
 **   A "unionvtab" virtual table is created as follows:
 **
-**     CREATE VIRTUAL TABLE <name> USING unionvtab(<sql statement>);
+**     CREATE VIRTUAL TABLE <name> USING unionvtab(<sql-statement>);
 **
 **   The implementation evalutes <sql statement> whenever a unionvtab virtual
 **   table is created or opened. It should return one row for each source
 **
 **   A "swarmvtab" virtual table is created similarly to a unionvtab table:
 **
-**     CREATE VIRTUAL TABLE <name> USING swarmvtab(<sql statement>);
+**     CREATE VIRTUAL TABLE <name>
+**      USING swarmvtab(<sql-statement>, <callback>);
 **
 **   The difference is that for a swarmvtab table, the first column returned
 **   by the <sql statement> must return a path or URI that can be used to open
-**   the database file containing the source table.
-**
+**   the database file containing the source table.  The <callback> option
+**   is optional.  If included, it is the name of an application-defined
+**   SQL function that is invoked with the URI of the file, if the file
+**   does not already exist on disk.
 */
 
 #include "sqlite3ext.h"
@@ -143,6 +146,7 @@ struct UnionTab {
 
   /* Used by swarmvtab only */
   char *zSourceStr;               /* Expected unionSourceToStr() value */
+  char *zNotFoundCallback;        /* UDF to invoke if file not found on open */
   UnionSrc *pClosable;            /* First in list of closable sources */
   int nOpen;                      /* Current number of open sources */
   int nMaxOpen;                   /* Maximum number of open sources */
@@ -379,6 +383,7 @@ static int unionDisconnect(sqlite3_vtab *pVtab){
       sqlite3_close(pSrc->db);
     }
     sqlite3_free(pTab->zSourceStr);
+    sqlite3_free(pTab->zNotFoundCallback);
     sqlite3_free(pTab->aSrc);
     sqlite3_free(pTab);
   }
@@ -491,6 +496,35 @@ static int unionSourceCheck(UnionTab *pTab, char **pzErr){
   return rc;
 }
 
+
+/*
+** Try to open the swarmvtab database.  If initially unable, invoke the
+** not-found callback UDF and then try again.
+*/
+static int unionOpenDatabaseInner(UnionTab *pTab, UnionSrc *pSrc, char **pzErr){
+  int rc = SQLITE_OK;
+  static const int openFlags = 
+       SQLITE_OPEN_READONLY | SQLITE_OPEN_URI;
+  rc = sqlite3_open_v2(pSrc->zFile, &pSrc->db, openFlags, 0);
+  if( rc==SQLITE_OK ) return rc;
+  if( pTab->zNotFoundCallback ){
+    char *zSql = sqlite3_mprintf("SELECT \"%w\"(%Q);",
+                    pTab->zNotFoundCallback, pSrc->zFile);
+    if( zSql==0 ){
+      *pzErr = sqlite3_mprintf("out of memory");
+      return SQLITE_NOMEM;
+    }
+    rc = sqlite3_exec(pTab->db, zSql, 0, 0, pzErr);
+    sqlite3_free(zSql);
+    if( rc ) return rc;
+    rc = sqlite3_open_v2(pSrc->zFile, &pSrc->db, openFlags, 0);
+  }
+  if( rc!=SQLITE_OK ){
+    *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(pSrc->db));
+  }
+  return rc;
+}
+
 /*
 ** This function may only be called for swarmvtab tables. The results of
 ** calling it on a unionvtab table are undefined.
@@ -513,10 +547,8 @@ static int unionOpenDatabase(UnionTab *pTab, int iSrc, char **pzErr){
   assert( pTab->bSwarm && iSrc<pTab->nSrc );
   if( pSrc->db==0 ){
     unionCloseSources(pTab, pTab->nMaxOpen-1);
-    rc = sqlite3_open_v2(pSrc->zFile, &pSrc->db, SQLITE_OPEN_READONLY, 0);
-    if( rc!=SQLITE_OK ){
-      *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(pSrc->db));
-    }else{
+    rc = unionOpenDatabaseInner(pTab, pSrc, pzErr);
+    if( rc==SQLITE_OK ){
       char *z = unionSourceToStr(&rc, pTab, pSrc, pzErr);
       if( rc==SQLITE_OK ){
         if( pTab->zSourceStr==0 ){
@@ -598,10 +630,11 @@ static int unionFinalizeCsrStmt(UnionCsr *pCsr){
 **
 ** The argv[] array contains the following:
 **
-**   argv[0]   -> module name  ("unionvtab")
+**   argv[0]   -> module name  ("unionvtab" or "swarmvtab")
 **   argv[1]   -> database name
 **   argv[2]   -> table name
 **   argv[3]   -> SQL statement
+**   argv[4]   -> not-found callback UDF name
 */
 static int unionConnect(
   sqlite3 *db,
@@ -619,7 +652,7 @@ static int unionConnect(
     /* unionvtab tables may only be created in the temp schema */
     *pzErr = sqlite3_mprintf("%s tables must be created in TEMP schema", zVtab);
     rc = SQLITE_ERROR;
-  }else if( argc!=4 ){
+  }else if( argc!=4 && argc!=5 ){
     *pzErr = sqlite3_mprintf("wrong number of arguments for %s", zVtab);
     rc = SQLITE_ERROR;
   }else{
@@ -685,6 +718,12 @@ static int unionConnect(
     unionFinalize(&rc, pStmt, pzErr);
     pStmt = 0;
 
+    /* Capture the not-found callback UDF name */
+    if( argc>=5 ){
+      pTab->zNotFoundCallback = unionStrdup(&rc, argv[4]);
+      unionDequote(pTab->zNotFoundCallback);
+    }
+
     /* It is an error if the SELECT statement returned zero rows. If only
     ** because there is no way to determine the schema of the virtual 
     ** table in this case.  */
index 720dbcbf68f3cbbf00d83fb7836a43ba4e8f7a3c..680db4e31a17a274d909c59871ae3329939db5af 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sfurther\stest\scases\sfor\sswarmvtab.\sAnd\sminor\scode\schanges.
-D 2017-08-04T17:39:13.161
+C Add\sthe\soptional\snon-found-callback\sto\sthe\sswarm-vtab.
+D 2017-08-04T20:15:08.335
 F Makefile.in d9873c9925917cca9990ee24be17eb9613a668012c85a343aef7e5536ae266e8
 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
 F Makefile.msc 02b469e9dcd5b7ee63fc1fb05babc174260ee4cfa4e0ef2e48c3c6801567a016
@@ -281,7 +281,7 @@ F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
 F ext/misc/spellfix.c a4723b6aff748a417b5091b68a46443265c40f0d
 F ext/misc/stmt.c 6f16443abb3551e3f5813bb13ba19a30e7032830015b0f92fe0c0453045c0a11
 F ext/misc/totype.c 4a167594e791abeed95e0a8db028822b5e8fe512
-F ext/misc/unionvtab.c c30398ece1cfe07d97334acdf3bd0f082782b493af254557f389c8fc1635aa7f
+F ext/misc/unionvtab.c cd472d02a7a0c77a3cecdd3cc995cd37fa902be845cd6b1c36a66da4ce0db99a
 F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95
 F ext/misc/vfsstat.c bf10ef0bc51e1ad6756629e1edb142f7a8db1178
 F ext/misc/vtshim.c 1976e6dd68dd0d64508c91a6dfab8e75f8aaf6cd
@@ -1232,6 +1232,7 @@ F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a
 F test/subtype1.test 7fe09496352f97053af1437150751be2d0a0cae8
 F test/superlock.test ec94f0556b6488d97f71c79f9061ae08d9ab8f12
 F test/swarmvtab.test fbb2415797477588337a54e3bc0ff8e1981d325d22b9e75a527438e79d926a6a
+F test/swarmvtab2.test 038ef9bcad6fd2fb9e395196080cf23e223ddb1219015049a61540c161bc577d
 F test/symlink.test c9ebe7330d228249e447038276bfc8a7b22f4849
 F test/sync.test 2f84bdbc2b2df1fcb0220575b4b9f8cea94b7529
 F test/sync2.test 6be8ed007fa063b147773c1982b5bdba97a32badc536bdc6077eff5cf8710ece
@@ -1641,7 +1642,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 7ae20eac83fc053dc1bbc42501dd41f77445a6b9a33cfa42b899fc7a18c637ab
-R f110030d9eaed7a6ec593149c64799ea
-U dan
-Z 1047b1cef8ad6de0923837acad2fcea1
+P 0f82d3b9dd5bd2e34a984c78e4a4a87921cf3e15b01b611133378c0ea9901010
+R 77c16cf4cab7b7344ed73948b800d412
+U drh
+Z 7560f1ef26bd2571437b3895f1c10f52
index 05e3670fa17e0ef23d2ac90529638cb345a0550f..271c4299f7c0dd5b6ecf0ae1e4d67bb2b666a26a 100644 (file)
@@ -1 +1 @@
-0f82d3b9dd5bd2e34a984c78e4a4a87921cf3e15b01b611133378c0ea9901010
\ No newline at end of file
+a94e2f600bc766fb459418e674b842628ba21e27cf9942c00cd533507d7b35fe
\ No newline at end of file
diff --git a/test/swarmvtab2.test b/test/swarmvtab2.test
new file mode 100644 (file)
index 0000000..2b37fd2
--- /dev/null
@@ -0,0 +1,74 @@
+# 2017-07-15
+#
+# 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 implements regression tests for SQLite library.  The
+# focus of this file is the "swarmvtab" extension
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix swarmvtab
+
+ifcapable !vtab {
+  finish_test
+  return
+}
+
+
+db close
+foreach name [glob -nocomplain test*.db] {
+  forcedelete $name
+}
+sqlite3 db test.db
+load_static_extension db unionvtab
+proc create_database {filename} {
+  sqlite3 dbx $filename
+  set num [regsub -all {[^0-9]+} $filename {}]
+  set num [string trimleft $num 0]
+  set start [expr {$num*1000}]
+  set end [expr {$start+999}]
+  dbx eval {
+    CREATE TABLE t2(a INTEGER PRIMARY KEY,b);
+    WITH RECURSIVE c(x) AS (
+      VALUES($start) UNION ALL SELECT x+1 FROM c WHERE x<$end
+    )
+    INSERT INTO t2(a,b) SELECT x, printf('**%05d**',x) FROM c;
+  }
+  dbx close
+}
+db func create_database create_database
+do_execsql_test 100 {
+  CREATE TABLE t1(filename, tablename, istart, iend);
+  WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<99)
+  INSERT INTO t1 SELECT printf('test%03d.db',x),'t2',x*1000,x*1000+999 FROM c;
+  CREATE VIRTUAL TABLE temp.v1 USING swarmvtab(
+    'SELECT * FROM t1', 'create_database'
+  );
+} {}
+do_execsql_test 110 {
+  SELECT b FROM v1 WHERE a=3875;
+} {**03875**}
+do_test 120 {
+  lsort [glob -nocomplain test?*.db]
+} {test001.db test003.db}
+do_execsql_test 130 {
+  SELECT b FROM v1 WHERE a BETWEEN 3999 AND 4000 ORDER BY a;
+} {**03999** **04000**}
+do_test 140 {
+  lsort [glob -nocomplain test?*.db]
+} {test001.db test003.db test004.db}
+do_execsql_test 150 {
+  SELECT b FROM v1 WHERE a>=99998;
+} {**99998** **99999**}
+do_test 160 {
+  lsort -dictionary [glob -nocomplain test?*.db]
+} {test001.db test003.db test004.db test099.db}
+
+finish_test