Add the optional non-found-callback to the swarm-vtab.

FossilOrigin-Name: a94e2f600bc766fb459418e674b842628ba21e27cf9942c00cd533507d7b35fe
This commit is contained in:
drh 2017-08-04 20:15:08 +00:00
parent d83e082524
commit a5aed4b10b
4 changed files with 132 additions and 18 deletions

View 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
@ -58,12 +58,15 @@
**
** 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. */

View File

@ -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

View File

@ -1 +1 @@
0f82d3b9dd5bd2e34a984c78e4a4a87921cf3e15b01b611133378c0ea9901010
a94e2f600bc766fb459418e674b842628ba21e27cf9942c00cd533507d7b35fe

74
test/swarmvtab2.test Normal file
View File

@ -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