From: drh Date: Fri, 4 Aug 2017 20:15:08 +0000 (+0000) Subject: Add the optional non-found-callback to the swarm-vtab. X-Git-Tag: version-3.21.0~188^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a5aed4b10b42af40958580f02e70e62c84064e5f;p=thirdparty%2Fsqlite.git Add the optional non-found-callback to the swarm-vtab. FossilOrigin-Name: a94e2f600bc766fb459418e674b842628ba21e27cf9942c00cd533507d7b35fe --- diff --git a/ext/misc/unionvtab.c b/ext/misc/unionvtab.c index c4e3fff039..bad82295c1 100644 --- a/ext/misc/unionvtab.c +++ b/ext/misc/unionvtab.c @@ -36,7 +36,7 @@ ** ** A "unionvtab" virtual table is created as follows: ** -** CREATE VIRTUAL TABLE USING unionvtab(); +** CREATE VIRTUAL TABLE USING unionvtab(); ** ** The implementation evalutes whenever a unionvtab virtual ** table is created or opened. It should return one row for each source @@ -58,12 +58,15 @@ ** ** A "swarmvtab" virtual table is created similarly to a unionvtab table: ** -** CREATE VIRTUAL TABLE USING swarmvtab(); +** CREATE VIRTUAL TABLE +** USING swarmvtab(, ); ** ** The difference is that for a swarmvtab table, the first column returned ** by the 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 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 && iSrcnSrc ); 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. */ diff --git a/manifest b/manifest index 720dbcbf68..680db4e31a 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index 05e3670fa1..271c4299f7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -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 index 0000000000..2b37fd2ac8 --- /dev/null +++ b/test/swarmvtab2.test @@ -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