From: dan Date: Sat, 15 Jul 2017 20:48:30 +0000 (+0000) Subject: Add the "unionvtab" virtual table extension in ext/misc/unionvtab.c. X-Git-Tag: version-3.20.0~31^2~2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d8ecefa5abc18071810b24382dd9bc3e954c4556;p=thirdparty%2Fsqlite.git Add the "unionvtab" virtual table extension in ext/misc/unionvtab.c. FossilOrigin-Name: 62a86aa6c0519cf1fa232169122d3d6ae8d2f66b20530fb934a82a15712bd2f0 --- diff --git a/Makefile.in b/Makefile.in index e71cbd8091..bce57b717b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -435,6 +435,7 @@ TESTSRC += \ $(TOP)/ext/misc/series.c \ $(TOP)/ext/misc/spellfix.c \ $(TOP)/ext/misc/totype.c \ + $(TOP)/ext/misc/unionvtab.c \ $(TOP)/ext/misc/wholenumber.c # Source code to the library files needed by the test fixture diff --git a/ext/misc/unionvtab.c b/ext/misc/unionvtab.c new file mode 100644 index 0000000000..c60f148d86 --- /dev/null +++ b/ext/misc/unionvtab.c @@ -0,0 +1,626 @@ +/* +** 2017 July 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. +** +************************************************************************* +** +*/ +#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_UNIONVTAB) + +#include "sqlite3ext.h" +SQLITE_EXTENSION_INIT1 +#include +#include + +#ifndef SQLITE_OMIT_VIRTUALTABLE + +typedef struct UnionCsr UnionCsr; +typedef struct UnionTab UnionTab; +typedef struct UnionSrc UnionSrc; + +/* +** Each source table (row returned by the initialization query) is +** represented by an instance of the following structure stored in the +** UnionTab.aSrc[] array. +*/ +struct UnionSrc { + char *zDb; /* Database containing source table */ + char *zTab; /* Source table name */ + sqlite3_int64 iMin; /* Minimum rowid */ + sqlite3_int64 iMax; /* Maximum rowid */ +}; + +/* +** Virtual table cursor type for union vtab. +*/ +struct UnionCsr { + sqlite3_vtab_cursor base; /* Base class - must be first */ + sqlite3_stmt *pStmt; /* SQL statement to run */ +}; + +/* +** Virtual table type for union vtab. +*/ +struct UnionTab { + sqlite3_vtab base; /* Base class - must be first */ + sqlite3 *db; /* Database handle */ + int nSrc; /* Number of elements in the aSrc[] array */ + UnionSrc *aSrc; /* Array of source tables, sorted by rowid */ +}; + +/* +** If *pRc is other than SQLITE_OK when this function is called, it +** always returns NULL. Otherwise, it attempts to allocate and return +** a pointer to nByte bytes of zeroed memory. If the memory allocation +** is attempted but fails, NULL is returned and *pRc is set to +** SQLITE_NOMEM. +*/ +static void *unionMalloc(int *pRc, int nByte){ + void *pRet; + assert( nByte>0 ); + if( *pRc==SQLITE_OK ){ + pRet = sqlite3_malloc(nByte); + if( pRet ){ + memset(pRet, 0, nByte); + }else{ + *pRc = SQLITE_NOMEM; + } + }else{ + pRet = 0; + } + return pRet; +} + +/* +** If *pRc is other than SQLITE_OK when this function is called, it +** always returns NULL. Otherwise, it attempts to allocate and return +** a copy of the nul-terminated string passed as the second argument. +** If the allocation is attempted but fails, NULL is returned and *pRc is +** set to SQLITE_NOMEM. +*/ +static char *unionStrdup(int *pRc, const char *zIn){ + char *zRet = 0; + if( zIn ){ + int nByte = strlen(zIn) + 1; + zRet = unionMalloc(pRc, nByte); + if( zRet ){ + memcpy(zRet, zIn, nByte); + } + } + return zRet; +} + +/* +** If the first character of the string passed as the only argument to this +** function is one of the 4 that may be used as an open quote character +** in SQL, this function assumes that the input is a well-formed quoted SQL +** string. In this case the string is dequoted in place. +** +** If the first character of the input is not an open quote, then this +** function is a no-op. +*/ +static void unionDequote(char *z){ + char q = z[0]; + + /* Set stack variable q to the close-quote character */ + if( q=='[' || q=='\'' || q=='"' || q=='`' ){ + int iIn = 1; + int iOut = 0; + if( q=='[' ) q = ']'; + while( z[iIn] ){ + if( z[iIn]==q ){ + if( z[iIn+1]!=q ){ + /* Character iIn was the close quote. */ + iIn++; + break; + }else{ + /* Character iIn and iIn+1 form an escaped quote character. Skip + ** the input cursor past both and copy a single quote character + ** to the output buffer. */ + iIn += 2; + z[iOut++] = q; + } + }else{ + z[iOut++] = z[iIn++]; + } + } + z[iOut] = '\0'; + } +} + +static sqlite3_stmt *unionPrepare( + int *pRc, + sqlite3 *db, + const char *zSql, + char **pzErr +){ + sqlite3_stmt *pRet = 0; + if( *pRc==SQLITE_OK ){ + int rc = sqlite3_prepare_v2(db, zSql, -1, &pRet, 0); + if( rc!=SQLITE_OK ){ + *pzErr = sqlite3_mprintf("sql error: %s", sqlite3_errmsg(db)); + *pRc = rc; + } + } + return pRet; +} + +static void unionReset(int *pRc, sqlite3_stmt *pStmt, char **pzErr){ + int rc = sqlite3_reset(pStmt); + if( *pRc==SQLITE_OK ){ + *pRc = rc; + if( rc && pzErr ){ + *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(sqlite3_db_handle(pStmt))); + } + } +} + +static void unionFinalize(int *pRc, sqlite3_stmt *pStmt){ + int rc = sqlite3_finalize(pStmt); + if( *pRc==SQLITE_OK ) *pRc = rc; +} + +/* +** xDisconnect method. +*/ +static int unionDisconnect(sqlite3_vtab *pVtab){ + if( pVtab ){ + UnionTab *pTab = (UnionTab*)pVtab; + int i; + for(i=0; inSrc; i++){ + sqlite3_free(pTab->aSrc[i].zDb); + sqlite3_free(pTab->aSrc[i].zTab); + } + sqlite3_free(pTab->aSrc); + sqlite3_free(pTab); + } + return SQLITE_OK; +} + +static char *unionSourceToStr( + int *pRc, + UnionSrc *pSrc, + sqlite3_stmt *pStmt, + char **pzErr +){ + char *zRet = 0; + if( *pRc==SQLITE_OK ){ + sqlite3_bind_text(pStmt, 1, pSrc->zTab, -1, SQLITE_STATIC); + sqlite3_bind_text(pStmt, 2, pSrc->zDb, -1, SQLITE_STATIC); + if( SQLITE_ROW==sqlite3_step(pStmt) ){ + zRet = unionStrdup(pRc, (const char*)sqlite3_column_text(pStmt, 0)); + } + unionReset(pRc, pStmt, pzErr); + if( *pRc==SQLITE_OK && zRet==0 ){ + *pRc = SQLITE_ERROR; + *pzErr = sqlite3_mprintf("no such table: %s%s%s", + (pSrc->zDb ? pSrc->zDb : ""), + (pSrc->zDb ? "." : ""), + pSrc->zTab + ); + } + } + return zRet; +} + +static int unionSourceCheck(UnionTab *pTab, char **pzErr){ + const char *zSql = + "SELECT group_concat(quote(name) || '.' || quote(type)) " + "FROM pragma_table_info(?, ?)"; + int rc = SQLITE_OK; + + if( pTab->nSrc==0 ){ + *pzErr = sqlite3_mprintf("no source tables configured"); + rc = SQLITE_ERROR; + }else{ + sqlite3_stmt *pStmt = 0; + char *z0 = 0; + int i; + + pStmt = unionPrepare(&rc, pTab->db, zSql, pzErr); + if( rc==SQLITE_OK ){ + z0 = unionSourceToStr(&rc, &pTab->aSrc[0], pStmt, pzErr); + } + for(i=1; inSrc; i++){ + char *z = unionSourceToStr(&rc, &pTab->aSrc[i], pStmt, pzErr); + if( rc==SQLITE_OK && sqlite3_stricmp(z, z0) ){ + *pzErr = sqlite3_mprintf("source table schema mismatch"); + rc = SQLITE_ERROR; + } + sqlite3_free(z); + } + + unionFinalize(&rc, pStmt); + sqlite3_free(z0); + } + return rc; +} + +/* +** xConnect/xCreate method. +** +** The argv[] array contains the following: +** +** argv[0] -> module name ("unionvtab") +** argv[1] -> database name +** argv[2] -> table name +** argv[3] -> SQL statement +*/ +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; + + if( sqlite3_stricmp("temp", argv[1]) ){ + /* unionvtab tables may only be created in the temp schema */ + *pzErr = sqlite3_mprintf("unionvtab tables must be created in TEMP schema"); + rc = SQLITE_ERROR; + }else if( argc!=4 ){ + *pzErr = sqlite3_mprintf("wrong number of arguments for unionvtab"); + rc = SQLITE_ERROR; + }else{ + int nAlloc = 0; /* Allocated size of pTab->aSrc[] */ + sqlite3_stmt *pStmt = 0; /* Argument statement */ + char *zSql1 = unionStrdup(&rc, argv[3]); + char *zSql2 = 0; + + if( zSql1 ){ + unionDequote(zSql1); + zSql2 = sqlite3_mprintf("SELECT * FROM (%s) ORDER BY 3", zSql1); + sqlite3_free(zSql1); + zSql1 = 0; + } + if( zSql2==0 ){ + rc = SQLITE_NOMEM; + } + pTab = unionMalloc(&rc, sizeof(UnionTab)); + pStmt = unionPrepare(&rc, db, zSql2, pzErr); + + while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ + const char *zDb = (const char*)sqlite3_column_text(pStmt, 0); + const char *zTab = (const char*)sqlite3_column_text(pStmt, 1); + sqlite3_int64 iMin = sqlite3_column_int64(pStmt, 2); + sqlite3_int64 iMax = sqlite3_column_int64(pStmt, 3); + UnionSrc *pSrc; + + if( nAlloc<=pTab->nSrc ){ + int nNew = nAlloc ? nAlloc*2 : 8; + UnionSrc *aNew = (UnionSrc*)sqlite3_realloc( + pTab->aSrc, nNew*sizeof(UnionSrc) + ); + if( aNew==0 ){ + rc = SQLITE_NOMEM; + break; + }else{ + memset(&aNew[pTab->nSrc], 0, (nNew-pTab->nSrc)*sizeof(UnionSrc)); + pTab->aSrc = aNew; + nAlloc = nNew; + } + } + + if( iMaxnSrc>0 && iMin<=pTab->aSrc[pTab->nSrc-1].iMax) ){ + *pzErr = sqlite3_mprintf("rowid range mismatch error"); + rc = SQLITE_ERROR; + } + + pSrc = &pTab->aSrc[pTab->nSrc++]; + pSrc->zDb = unionStrdup(&rc, zDb); + pSrc->zTab = unionStrdup(&rc, zTab); + pSrc->iMin = iMin; + pSrc->iMax = iMax; + } + unionFinalize(&rc, pStmt); + pStmt = 0; + sqlite3_free(zSql1); + sqlite3_free(zSql2); + zSql1 = 0; + zSql2 = 0; + + /* Verify that all source tables exist and have compatible schemas. */ + if( rc==SQLITE_OK ){ + pTab->db = db; + rc = unionSourceCheck(pTab, pzErr); + } + + /* Compose a CREATE TABLE statement and pass it to declare_vtab() */ + if( rc==SQLITE_OK ){ + zSql1 = sqlite3_mprintf("SELECT " + "'CREATE TABLE xyz('" + " || group_concat(quote(name) || ' ' || type, ', ')" + " || ')'" + "FROM pragma_table_info(%Q, ?)", + pTab->aSrc[0].zTab + ); + if( zSql1==0 ) rc = SQLITE_NOMEM; + } + pStmt = unionPrepare(&rc, db, zSql1, pzErr); + if( rc==SQLITE_OK ){ + sqlite3_bind_text(pStmt, 1, pTab->aSrc[0].zDb, -1, SQLITE_STATIC); + if( SQLITE_ROW==sqlite3_step(pStmt) ){ + const char *zDecl = (const char*)sqlite3_column_text(pStmt, 0); + rc = sqlite3_declare_vtab(db, zDecl); + } + } + + unionFinalize(&rc, pStmt); + sqlite3_free(zSql1); + } + + if( rc!=SQLITE_OK ){ + unionDisconnect((sqlite3_vtab*)pTab); + pTab = 0; + } + + *ppVtab = (sqlite3_vtab*)pTab; + return rc; +} + + +/* +** xOpen +*/ +static int unionOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){ + UnionCsr *pCsr; + int rc = SQLITE_OK; + pCsr = (UnionCsr*)unionMalloc(&rc, sizeof(UnionCsr)); + *ppCursor = &pCsr->base; + return rc; +} + +/* +** xClose +*/ +static int unionClose(sqlite3_vtab_cursor *cur){ + UnionCsr *pCsr = (UnionCsr*)cur; + sqlite3_finalize(pCsr->pStmt); + sqlite3_free(pCsr); + return SQLITE_OK; +} + + +/* +** xNext +*/ +static int unionNext(sqlite3_vtab_cursor *cur){ + UnionCsr *pCsr = (UnionCsr*)cur; + int rc; + assert( pCsr->pStmt ); + if( sqlite3_step(pCsr->pStmt)!=SQLITE_ROW ){ + rc = sqlite3_finalize(pCsr->pStmt); + pCsr->pStmt = 0; + }else{ + rc = SQLITE_OK; + } + return rc; +} + +/* +** xColumn +*/ +static int unionColumn( + sqlite3_vtab_cursor *cur, + sqlite3_context *ctx, + int i +){ + UnionCsr *pCsr = (UnionCsr*)cur; + sqlite3_result_value(ctx, sqlite3_column_value(pCsr->pStmt, i+1)); + return SQLITE_OK; +} + +/* +** xRowid +*/ +static int unionRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ + UnionCsr *pCsr = (UnionCsr*)cur; + *pRowid = sqlite3_column_int64(pCsr->pStmt, 0); + return SQLITE_OK; +} + +/* +** xEof +*/ +static int unionEof(sqlite3_vtab_cursor *cur){ + UnionCsr *pCsr = (UnionCsr*)cur; + return pCsr->pStmt==0; +} + +/* +** xFilter +*/ +static int unionFilter( + sqlite3_vtab_cursor *pVtabCursor, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv +){ + UnionTab *pTab = (UnionTab*)(pVtabCursor->pVtab); + UnionCsr *pCsr = (UnionCsr*)pVtabCursor; + int rc = SQLITE_OK; + int i; + char *zSql = 0; + + int bMinValid = 0; + int bMaxValid = 0; + sqlite3_int64 iMin; + sqlite3_int64 iMax; + + if( idxNum==SQLITE_INDEX_CONSTRAINT_EQ ){ + assert( argc==1 ); + iMin = iMax = sqlite3_value_int64(argv[0]); + bMinValid = bMaxValid = 1; + }else{ + if( idxNum & SQLITE_INDEX_CONSTRAINT_LE ){ + assert( argc>=1 ); + iMax = sqlite3_value_int64(argv[0]); + bMaxValid = 1; + } + if( idxNum & SQLITE_INDEX_CONSTRAINT_GE ){ + assert( argc>=1 ); + iMin = sqlite3_value_int64(argv[argc-1]); + bMinValid = 1; + } + } + + + sqlite3_finalize(pCsr->pStmt); + pCsr->pStmt = 0; + + for(i=0; inSrc; i++){ + UnionSrc *pSrc = &pTab->aSrc[i]; + if( (bMinValid && iMin>pSrc->iMax) || (bMaxValid && iMaxiMin) ){ + continue; + } + + zSql = sqlite3_mprintf("%z%sSELECT rowid, * FROM %s%q%s%Q" + , zSql + , (zSql ? " UNION ALL " : "") + , (pSrc->zDb ? "'" : "") + , (pSrc->zDb ? pSrc->zDb : "") + , (pSrc->zDb ? "'." : "") + , pSrc->zTab + ); + if( zSql==0 ){ + rc = SQLITE_NOMEM; + break; + } + + if( zSql ){ + if( bMinValid && bMaxValid && iMin==iMax ){ + zSql = sqlite3_mprintf("%z WHERE rowid=%lld", zSql, iMin); + }else{ + const char *zWhere = "WHERE"; + if( bMinValid && iMin>pSrc->iMin ){ + zSql = sqlite3_mprintf("%z WHERE rowid>=%lld", zSql, iMin); + zWhere = "AND"; + } + if( bMaxValid && iMaxiMax ){ + zSql = sqlite3_mprintf("%z %s rowid<=%lld", zSql, zWhere, iMax); + } + } + } + } + + if( rc==SQLITE_OK ){ + pCsr->pStmt = unionPrepare(&rc, pTab->db, zSql, &pTab->base.zErrMsg); + } + sqlite3_free(zSql); + + if( rc!=SQLITE_OK ) return rc; + return unionNext(pVtabCursor); +} + +/* +** xBestIndex. +*/ +static int unionBestIndex( + sqlite3_vtab *tab, + sqlite3_index_info *pIdxInfo +){ + int iEq = -1; + int iLt = -1; + int iGt = -1; + int i; + + for(i=0; inConstraint; i++){ + struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[i]; + if( p->usable && p->iColumn<0 ){ + switch( p->op ){ + case SQLITE_INDEX_CONSTRAINT_EQ: + if( iEq<0 ) iEq = i; + break; + case SQLITE_INDEX_CONSTRAINT_LE: + case SQLITE_INDEX_CONSTRAINT_LT: + if( iLt<0 ) iLt = i; + break; + case SQLITE_INDEX_CONSTRAINT_GE: + case SQLITE_INDEX_CONSTRAINT_GT: + if( iGt<0 ) iGt = i; + break; + } + } + } + + if( iEq>=0 ){ + pIdxInfo->estimatedRows = 1; + pIdxInfo->idxFlags = SQLITE_INDEX_SCAN_UNIQUE; + pIdxInfo->estimatedCost = 3.0; + pIdxInfo->idxNum = SQLITE_INDEX_CONSTRAINT_EQ; + pIdxInfo->aConstraintUsage[iEq].argvIndex = 1; + }else{ + int iCons = 1; + int idxNum = 0; + sqlite3_int64 nRow = 1000000; + if( iLt>=0 ){ + nRow = nRow / 2; + pIdxInfo->aConstraintUsage[iLt].argvIndex = iCons++; + idxNum |= SQLITE_INDEX_CONSTRAINT_LE; + } + if( iGt>=0 ){ + nRow = nRow / 2; + pIdxInfo->aConstraintUsage[iGt].argvIndex = iCons++; + idxNum |= SQLITE_INDEX_CONSTRAINT_GE; + } + pIdxInfo->estimatedRows = nRow; + pIdxInfo->estimatedCost = 3.0 * (double)nRow; + pIdxInfo->idxNum = idxNum; + } + + return SQLITE_OK; +} + +static int createUnionVtab(sqlite3 *db){ + static sqlite3_module unionModule = { + 0, /* iVersion */ + unionConnect, + unionConnect, + unionBestIndex, /* xBestIndex - query planner */ + unionDisconnect, + unionDisconnect, + unionOpen, /* xOpen - open a cursor */ + unionClose, /* xClose - close a cursor */ + unionFilter, /* xFilter - configure scan constraints */ + unionNext, /* xNext - advance a cursor */ + unionEof, /* xEof - check for end of scan */ + unionColumn, /* xColumn - read data */ + unionRowid, /* xRowid - read data */ + 0, /* xUpdate */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindMethod */ + 0, /* xRename */ + }; + + return sqlite3_create_module(db, "unionvtab", &unionModule, 0); +} + +#endif /* SQLITE_OMIT_VIRTUALTABLE */ + +#ifdef _WIN32 +__declspec(dllexport) +#endif +int sqlite3_unionvtab_init( + sqlite3 *db, + char **pzErrMsg, + const sqlite3_api_routines *pApi +){ + int rc = SQLITE_OK; + SQLITE_EXTENSION_INIT2(pApi); +#ifndef SQLITE_OMIT_VIRTUALTABLE + rc = createUnionVtab(db); +#endif + return rc; +} + +#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_UNIONVTAB) */ diff --git a/main.mk b/main.mk index 4bd2f54ce4..d3450a63cb 100644 --- a/main.mk +++ b/main.mk @@ -341,6 +341,7 @@ TESTSRC += \ $(TOP)/ext/misc/series.c \ $(TOP)/ext/misc/spellfix.c \ $(TOP)/ext/misc/totype.c \ + $(TOP)/ext/misc/unionvtab.c \ $(TOP)/ext/misc/wholenumber.c \ $(TOP)/ext/misc/vfslog.c \ $(TOP)/ext/fts5/fts5_tcl.c \ diff --git a/manifest b/manifest index a0044545dc..51a952a0f8 100644 --- a/manifest +++ b/manifest @@ -1,6 +1,6 @@ -C Fix\sa\sregister\sallocation\sproblem\sin\sPRAGMA\sintegrity_check\sthat\scaused\nthe\ssame\sregister\sto\sbe\sused\sfor\stwo\sdifferent\spurposes\son\sthe\sfirst\nATTACHed\sdatabase\sif\sthe\sschema\sfor\sthe\sATTACHed\sdatabase\swas\snoticable\nmore\scomplex\sthan\sthe\sschema\sfor\sthe\sfirst\sdatabase.\nFix\sfor\sticket\s[a4e06e75a9ab61a1]. -D 2017-07-15T20:33:19.899 -F Makefile.in eda8bedf08c4c93e2137ef1218b3d3302488c68c2774918de0335a1133aab157 +C Add\sthe\s"unionvtab"\svirtual\stable\sextension\sin\sext/misc/unionvtab.c. +D 2017-07-15T20:48:30.280 +F Makefile.in d9873c9925917cca9990ee24be17eb9613a668012c85a343aef7e5536ae266e8 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 20850e3e8d4d4791e0531955852d768eb06f24138214870d543abb1a47346fba F README.md f5c87359573c4d255425e588a56554b50fdcc2afba4e017a2e02a43701456afd @@ -281,6 +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 594b8cebd11297c45d87fed5985548fa141d917f08e672aadea06c14f18bcf42 F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95 F ext/misc/vfsstat.c bf10ef0bc51e1ad6756629e1edb142f7a8db1178 F ext/misc/vtshim.c 1976e6dd68dd0d64508c91a6dfab8e75f8aaf6cd @@ -375,7 +376,7 @@ F ext/userauth/userauth.c 3410be31283abba70255d71fd24734e017a4497f F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60 -F main.mk 0288de0e6b44c2fa709b354d4fab5bce423259cc1653c35305c6e50d23ec561a +F main.mk 5b7d72ab03dd70aa1401f934d31e85aefd6fc542eb58094d7a95d6921390b2d0 F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83 F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271 F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504 @@ -444,7 +445,7 @@ F src/parse.y e384cb73f99e1b074085c974b37f4d830e885359e4b60837e30f7d67c16ba65b F src/pcache.c 62835bed959e2914edd26afadfecce29ece0e870 F src/pcache.h 521bb9610d38ef17a3cc9b5ddafd4546c2ea67fa3d0e464823d73c2a28d50e11 F src/pcache1.c 1195a21fe28e223e024f900b2011e80df53793f0356a24caace4188b098540dc -F src/pragma.c 2ae4088e9c3ca0e63ffc3ada7f2d2d66e91f0b3db50c7f7ddb2f56e9e37fd638 +F src/pragma.c d42b6b5af353bcd763f1901accb63e07855bb8d74c81d246be12488e424e6804 F src/pragma.h bb83728944b42f6d409c77f5838a8edbdb0fe83046c5496ffc9602b40340a324 F src/prepare.c dd250f904739b1dc449c131ac527c35e3424d94082dd111321bd83f80c6bb0fe F src/printf.c 8757834f1b54dae512fb25eb1acc8e94a0d15dd2290b58f2563f65973265adb2 @@ -462,7 +463,7 @@ F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6 F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1 F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34 F src/tclsqlite.c 2c29b0b76e91edfd1b43bf135c32c8674710089197327682b6b7e6af88062c3d -F src/test1.c 1c0726cdf7389ed053a9b9aa0dc3c63f3b9bbc607a25decae6549682008510b3 +F src/test1.c cfb78b728b37ae3a2b14fe1b3a6c766e0da41370eda112594e698c94011b622e F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5 F src/test3.c b8434949dfb8aff8dfa082c8b592109e77844c2135ed3c492113839b6956255b F src/test4.c 18ec393bb4d0ad1de729f0b94da7267270f3d8e6 @@ -1431,6 +1432,7 @@ F test/tt3_vacuum.c 1753f45917699c9c1f66b64c717a717c9379f776 F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff F test/types2.test 1aeb81976841a91eef292723649b5c4fe3bc3cac F test/types3.test 99e009491a54f4dc02c06bdbc0c5eea56ae3e25a +F test/unionvtab.test c206279d350f473da61daeeaa58637d31a691ad7e7ba1d8826d5f5ea7545676d F test/unique.test 93f8b2ef5ea51b9495f8d6493429b1fd0f465264 F test/unique2.test 3674e9f2a3f1fbbfd4772ac74b7a97090d0f77d2 F test/unixexcl.test d936ba2b06794018e136418addd59a2354eeae97 @@ -1633,7 +1635,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 687bd478710eb827e041533eea67115464f5a0de767bb6cfdbe36a0d3c597fa1 -R 522613d16f7d47bea7a7f05e6d39255f -U drh -Z 4ddd385f7f3a9276d818cfc9da4dbe0b +P 253945d480b052bfe311888022b5eb0be91c8c80cda05036e58207d57520262c +R ee0a9dc5b1464f766485894fa2069cae +U dan +Z d195e18cbc4e4f34f8cf4b9b0b0c8aaa diff --git a/manifest.uuid b/manifest.uuid index cc1f259bcc..58f352fc6e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -253945d480b052bfe311888022b5eb0be91c8c80cda05036e58207d57520262c \ No newline at end of file +62a86aa6c0519cf1fa232169122d3d6ae8d2f66b20530fb934a82a15712bd2f0 \ No newline at end of file diff --git a/src/pragma.c b/src/pragma.c index 45809bcb14..0e13efa7d2 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -2371,10 +2371,14 @@ static int pragmaVtabFilter( pragmaVtabCursorClear(pCsr); j = (pTab->pName->mPragFlg & PragFlg_Result1)!=0 ? 0 : 1; for(i=0; iazArg) ); - pCsr->azArg[j] = sqlite3_mprintf("%s", sqlite3_value_text(argv[i])); - if( pCsr->azArg[j]==0 ){ - return SQLITE_NOMEM; + assert( pCsr->azArg[j]==0 ); + if( zText ){ + pCsr->azArg[j] = sqlite3_mprintf("%s", zText); + if( pCsr->azArg[j]==0 ){ + return SQLITE_NOMEM; + } } } sqlite3StrAccumInit(&acc, 0, 0, 0, pTab->db->aLimit[SQLITE_LIMIT_SQL_LENGTH]); diff --git a/src/test1.c b/src/test1.c index 4b97f2c223..df7685f244 100644 --- a/src/test1.c +++ b/src/test1.c @@ -6920,6 +6920,7 @@ static int SQLITE_TCLAPI tclLoadStaticExtensionCmd( extern int sqlite3_spellfix_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_totype_init(sqlite3*,char**,const sqlite3_api_routines*); extern int sqlite3_wholenumber_init(sqlite3*,char**,const sqlite3_api_routines*); + extern int sqlite3_unionvtab_init(sqlite3*,char**,const sqlite3_api_routines*); static const struct { const char *zExtName; int (*pInit)(sqlite3*,char**,const sqlite3_api_routines*); @@ -6939,6 +6940,7 @@ static int SQLITE_TCLAPI tclLoadStaticExtensionCmd( { "series", sqlite3_series_init }, { "spellfix", sqlite3_spellfix_init }, { "totype", sqlite3_totype_init }, + { "unionvtab", sqlite3_unionvtab_init }, { "wholenumber", sqlite3_wholenumber_init }, }; sqlite3 *db; diff --git a/test/unionvtab.test b/test/unionvtab.test new file mode 100644 index 0000000000..29403afd83 --- /dev/null +++ b/test/unionvtab.test @@ -0,0 +1,147 @@ +# 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 percentile.c extension +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix unionvtab + +load_static_extension db unionvtab + +#------------------------------------------------------------------------- +# Warm body tests. +# +forcedelete test.db2 +do_execsql_test 1.0 { + ATTACH 'test.db2' AS aux; + + CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT); + CREATE TABLE t2(a INTEGER PRIMARY KEY, b TEXT); + CREATE TABLE aux.t3(a INTEGER PRIMARY KEY, b TEXT); + + + INSERT INTO t1 VALUES(1, 'one'), (2, 'two'), (3, 'three'); + INSERT INTO t2 VALUES(10, 'ten'), (11, 'eleven'), (12, 'twelve'); + INSERT INTO t3 VALUES(20, 'twenty'), (21, 'twenty-one'), (22, 'twenty-two'); +} + +do_execsql_test 1.1 { + CREATE VIRTUAL TABLE temp.uuu USING unionvtab( + "VALUES(NULL, 't1', 1, 9), ('main', 't2', 10, 19), ('aux', 't3', 20, 29)" + ); + SELECT * FROM uuu; +} { + 1 one 2 two 3 three + 10 ten 11 eleven 12 twelve + 20 twenty 21 twenty-one 22 twenty-two +} + +do_execsql_test 1.2 { + PRAGMA table_info(uuu); +} { + 0 a INTEGER 0 {} 0 + 1 b TEXT 0 {} 0 +} + +do_execsql_test 1.3 { + SELECT * FROM uuu WHERE rowid = 3; + SELECT * FROM uuu WHERE rowid = 11; +} {3 three 11 eleven} + +do_execsql_test 1.4 { + SELECT * FROM uuu WHERE rowid IN (12, 10, 2); +} {2 two 10 ten 12 twelve} + +do_execsql_test 1.5 { + SELECT * FROM uuu WHERE rowid BETWEEN 3 AND 11; +} {3 three 10 ten 11 eleven} + +do_execsql_test 1.6 { + SELECT * FROM uuu WHERE rowid BETWEEN 11 AND 15; +} {11 eleven 12 twelve} + +do_execsql_test 1.7 { + SELECT * FROM uuu WHERE rowid BETWEEN -46 AND 1500; +} { + 1 one 2 two 3 three + 10 ten 11 eleven 12 twelve + 20 twenty 21 twenty-one 22 twenty-two +} + +#------------------------------------------------------------------------- +# Error conditions. +# +# 2.1.*: Attempt to create a unionvtab table outside of the TEMP schema. +# 2.2.*: Tables that do not exist. +# 2.3.*: Non WITHOUT ROWID tables. +# 2.4.*: Tables with mismatched schemas. +# +do_catchsql_test 2.1.1 { + CREATE VIRTUAL TABLE u1 USING unionvtab("VALUES(NULL, 't1', 1, 100)"); +} {1 {unionvtab tables must be created in TEMP schema}} +do_catchsql_test 2.1.2 { + CREATE VIRTUAL TABLE main.u1 USING unionvtab("VALUES('', 't1', 1, 100)"); +} {1 {unionvtab tables must be created in TEMP schema}} +do_catchsql_test 2.1.3 { + CREATE VIRTUAL TABLE aux.u1 USING unionvtab("VALUES('', 't1', 1, 100)"); +} {1 {unionvtab tables must be created in TEMP schema}} + +do_catchsql_test 2.2.1 { + CREATE VIRTUAL TABLE temp.u1 USING unionvtab("VALUES(NULL, 't555', 1, 100)"); +} {1 {no such table: t555}} +do_catchsql_test 2.2.2 { + CREATE VIRTUAL TABLE temp.u1 USING unionvtab("VALUES('aux', 't555', 1, 100)"); +} {1 {no such table: aux.t555}} +do_catchsql_test 2.2.3 { + CREATE VIRTUAL TABLE temp.u1 USING unionvtab("VALUES('xua', 't555', 1, 100)"); +} {1 {unknown database 'xua'}} + +do_execsql_test 2.4.0 { + CREATE TABLE x1(a BLOB, b); + CREATE TABLE x2(a BLOB, b); + CREATE TEMP TABLE x3(a BLOB, b); + + CREATE TABLE aux.y1(one, two, three INTEGER PRIMARY KEY); + CREATE TEMP TABLE y2(one, two, three INTEGER PRIMARY KEY); + CREATE TABLE y3(one, two, three INTEGER PRIMARY KEY); +} + +foreach {tn dbs res} { + 1 {x1 x2 x3} {0 {}} + 2 {y1 y2 y3} {0 {}} + 3 {x1 y2 y3} {1 {source table schema mismatch}} + 4 {x1 y2 x3} {1 {source table schema mismatch}} + 5 {x1 x2 y3} {1 {source table schema mismatch}} +} { + set L [list] + set iMin 0 + foreach e $dbs { + set E [split $e .] + if {[llength $E]>1} { + lappend L "('[lindex $E 0]', '[lindex $E 1]', $iMin, $iMin)" + } else { + lappend L "(NULL, '$e', $iMin, $iMin)" + } + incr iMin + } + + set sql "CREATE VIRTUAL TABLE temp.a1 USING unionvtab(\"VALUES [join $L ,]\")" + do_catchsql_test 2.4.$tn " + DROP TABLE IF EXISTS temp.a1; + CREATE VIRTUAL TABLE temp.a1 USING unionvtab(\"VALUES [join $L ,]\"); + " $res +} + + +finish_test +