Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Add the optional non-found-callback to the swarm-vtab. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | union-vtab |
Files: | files | file ages | folders |
SHA3-256: |
a94e2f600bc766fb459418e674b84262 |
User & Date: | drh 2017-08-04 20:15:08.335 |
Context
2017-08-04
| ||
20:27 | Add the swarm virtual table to the existing union virtual table module. (check-in: 0f6f6f03de user: drh tags: trunk) | |
20:15 | Add the optional non-found-callback to the swarm-vtab. (Closed-Leaf check-in: a94e2f600b user: drh tags: union-vtab) | |
17:39 | Add further test cases for swarmvtab. And minor code changes. (check-in: 0f82d3b9dd user: dan tags: union-vtab) | |
Changes
Changes to ext/misc/unionvtab.c.
︙ | ︙ | |||
32 33 34 35 36 37 38 | ** implementation takes care of opening and closing database files ** automatically. ** ** UNIONVTAB ** ** A "unionvtab" virtual table is created as follows: ** | | | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | ** implementation takes care of opening and closing database files ** automatically. ** ** UNIONVTAB ** ** A "unionvtab" virtual table is created as follows: ** ** 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 ** database table. The four columns required of each row are: ** ** 1. The name of the database containing the table ("main" or "temp" or ** the name of an attached database). Or NULL to indicate that all |
︙ | ︙ | |||
54 55 56 57 58 59 60 | ** 4. The largest rowid in the range of rowids that may be stored in the ** database table (an integer). ** ** SWARMVTAB ** ** A "swarmvtab" virtual table is created similarly to a unionvtab table: ** | | > | > > | | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | ** 4. The largest rowid in the range of rowids that may be stored in the ** database table (an integer). ** ** SWARMVTAB ** ** A "swarmvtab" virtual table is created similarly to a unionvtab table: ** ** 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 <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" SQLITE_EXTENSION_INIT1 #include <assert.h> #include <string.h> |
︙ | ︙ | |||
139 140 141 142 143 144 145 146 147 148 149 150 151 152 | int bSwarm; /* 1 for "swarmvtab", 0 for "unionvtab" */ int iPK; /* INTEGER PRIMARY KEY column, or -1 */ int nSrc; /* Number of elements in the aSrc[] array */ UnionSrc *aSrc; /* Array of source tables, sorted by rowid */ /* Used by swarmvtab only */ char *zSourceStr; /* Expected unionSourceToStr() value */ UnionSrc *pClosable; /* First in list of closable sources */ int nOpen; /* Current number of open sources */ int nMaxOpen; /* Maximum number of open sources */ }; /* ** Virtual table cursor type for union vtab. | > | 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | int bSwarm; /* 1 for "swarmvtab", 0 for "unionvtab" */ int iPK; /* INTEGER PRIMARY KEY column, or -1 */ int nSrc; /* Number of elements in the aSrc[] array */ UnionSrc *aSrc; /* Array of source tables, sorted by rowid */ /* 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 */ }; /* ** Virtual table cursor type for union vtab. |
︙ | ︙ | |||
375 376 377 378 379 380 381 382 383 384 385 386 387 388 | UnionSrc *pSrc = &pTab->aSrc[i]; sqlite3_free(pSrc->zDb); sqlite3_free(pSrc->zTab); sqlite3_free(pSrc->zFile); sqlite3_close(pSrc->db); } sqlite3_free(pTab->zSourceStr); sqlite3_free(pTab->aSrc); sqlite3_free(pTab); } return SQLITE_OK; } /* | > | 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 | UnionSrc *pSrc = &pTab->aSrc[i]; sqlite3_free(pSrc->zDb); sqlite3_free(pSrc->zTab); sqlite3_free(pSrc->zFile); sqlite3_close(pSrc->db); } sqlite3_free(pTab->zSourceStr); sqlite3_free(pTab->zNotFoundCallback); sqlite3_free(pTab->aSrc); sqlite3_free(pTab); } return SQLITE_OK; } /* |
︙ | ︙ | |||
487 488 489 490 491 492 493 494 495 496 497 498 499 500 | sqlite3_free(z); } sqlite3_free(z0); return rc; } /* ** This function may only be called for swarmvtab tables. The results of ** calling it on a unionvtab table are undefined. ** ** For a swarmvtab table, this function ensures that source database iSrc ** is open. If the database is opened successfully and the schema is as ** expected, or if it is already open when this function is called, SQLITE_OK | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 | sqlite3_free(z); } sqlite3_free(z0); 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. ** ** For a swarmvtab table, this function ensures that source database iSrc ** is open. If the database is opened successfully and the schema is as ** expected, or if it is already open when this function is called, SQLITE_OK |
︙ | ︙ | |||
509 510 511 512 513 514 515 | static int unionOpenDatabase(UnionTab *pTab, int iSrc, char **pzErr){ int rc = SQLITE_OK; UnionSrc *pSrc = &pTab->aSrc[iSrc]; assert( pTab->bSwarm && iSrc<pTab->nSrc ); if( pSrc->db==0 ){ unionCloseSources(pTab, pTab->nMaxOpen-1); | | | < < | 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 | static int unionOpenDatabase(UnionTab *pTab, int iSrc, char **pzErr){ int rc = SQLITE_OK; UnionSrc *pSrc = &pTab->aSrc[iSrc]; assert( pTab->bSwarm && iSrc<pTab->nSrc ); if( pSrc->db==0 ){ unionCloseSources(pTab, pTab->nMaxOpen-1); rc = unionOpenDatabaseInner(pTab, pSrc, pzErr); if( rc==SQLITE_OK ){ char *z = unionSourceToStr(&rc, pTab, pSrc, pzErr); if( rc==SQLITE_OK ){ if( pTab->zSourceStr==0 ){ pTab->zSourceStr = z; }else{ if( sqlite3_stricmp(z, pTab->zSourceStr) ){ *pzErr = sqlite3_mprintf("source table schema mismatch"); |
︙ | ︙ | |||
594 595 596 597 598 599 600 | } /* ** xConnect/xCreate method. ** ** The argv[] array contains the following: ** | | > | | 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 | } /* ** xConnect/xCreate method. ** ** The argv[] array contains the following: ** ** 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, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVtab, char **pzErr ){ UnionTab *pTab = 0; int rc = SQLITE_OK; int bSwarm = (pAux==0 ? 0 : 1); const char *zVtab = (bSwarm ? "swarmvtab" : "unionvtab"); if( sqlite3_stricmp("temp", argv[1]) ){ /* 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 && argc!=5 ){ *pzErr = sqlite3_mprintf("wrong number of arguments for %s", zVtab); rc = SQLITE_ERROR; }else{ int nAlloc = 0; /* Allocated size of pTab->aSrc[] */ sqlite3_stmt *pStmt = 0; /* Argument statement */ char *zArg = unionStrdup(&rc, argv[3]); /* Copy of argument to CVT */ |
︙ | ︙ | |||
680 681 682 683 684 685 686 687 688 689 690 691 692 693 | }else{ pSrc->zDb = unionStrdup(&rc, zDb); } } } unionFinalize(&rc, pStmt, pzErr); pStmt = 0; /* 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. */ if( rc==SQLITE_OK && pTab->nSrc==0 ){ *pzErr = sqlite3_mprintf("no source tables configured"); rc = SQLITE_ERROR; | > > > > > > | 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 | }else{ pSrc->zDb = unionStrdup(&rc, zDb); } } } 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. */ if( rc==SQLITE_OK && pTab->nSrc==0 ){ *pzErr = sqlite3_mprintf("no source tables configured"); rc = SQLITE_ERROR; |
︙ | ︙ |
Added test/swarmvtab2.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 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 |