**
** 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"
/* 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 */
sqlite3_close(pSrc->db);
}
sqlite3_free(pTab->zSourceStr);
+ sqlite3_free(pTab->zNotFoundCallback);
sqlite3_free(pTab->aSrc);
sqlite3_free(pTab);
}
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.
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 ){
**
** 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,
/* 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{
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. */
-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
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
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
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
--- /dev/null
+# 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