From: dan Date: Mon, 17 Jul 2017 20:25:21 +0000 (+0000) Subject: Enhance error detection and fix other issues in unionvtab code. X-Git-Tag: version-3.20.0~31^2~1 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=bcd303ac7d62a7ba2df2d3a8a73f9e7031b61f71;p=thirdparty%2Fsqlite.git Enhance error detection and fix other issues in unionvtab code. FossilOrigin-Name: 9c3f1b9a82e500e015deb0cc669fbb32e7f0cdc69f926ceff383ab946f8d8d18 --- diff --git a/ext/misc/unionvtab.c b/ext/misc/unionvtab.c index c60f148d86..8fb0c966cb 100644 --- a/ext/misc/unionvtab.c +++ b/ext/misc/unionvtab.c @@ -10,8 +10,42 @@ ** ************************************************************************* ** +** This file contains the implementation of the "unionvtab" virtual +** table. This module provides read-only access to multiple tables, +** possibly in multiple database files, via a single database object. +** The source tables must have the following characteristics: +** +** * They must all be rowid tables (not VIRTUAL or WITHOUT ROWID +** tables or views). +** +** * Each table must have the same set of columns, declared in +** the same order and with the same declared types. +** +** * The tables must not feature a user-defined column named "_rowid_". +** +** * Each table must contain a distinct range of rowid values. +** +** A "unionvtab" virtual table is created as follows: +** +** 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 +** 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 +** databases should be searched for the table in the usual fashion. +** +** 2. The name of the database table. +** +** 3. The smallest rowid in the range of rowids that may be stored in the +** database table (an integer). +** +** 4. The largest rowid in the range of rowids that may be stored in the +** database table (an integer). +** */ -#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_UNIONVTAB) #include "sqlite3ext.h" SQLITE_EXTENSION_INIT1 @@ -20,6 +54,17 @@ SQLITE_EXTENSION_INIT1 #ifndef SQLITE_OMIT_VIRTUALTABLE +/* +** Largest and smallest possible 64-bit signed integers. These macros +** copied from sqliteInt.h. +*/ +#ifndef LARGEST_INT64 +# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32)) +#endif +#ifndef SMALLEST_INT64 +# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64) +#endif + typedef struct UnionCsr UnionCsr; typedef struct UnionTab UnionTab; typedef struct UnionSrc UnionSrc; @@ -36,14 +81,6 @@ struct UnionSrc { 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. */ @@ -54,6 +91,14 @@ struct UnionTab { UnionSrc *aSrc; /* Array of source tables, sorted by 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 */ +}; + /* ** If *pRc is other than SQLITE_OK when this function is called, it ** always returns NULL. Otherwise, it attempts to allocate and return @@ -134,11 +179,24 @@ static void unionDequote(char *z){ } } +/* +** This function is a no-op if *pRc is set to other than SQLITE_OK when it +** is called. NULL is returned in this case. +** +** Otherwise, the SQL statement passed as the third argument is prepared +** against the database handle passed as the second. If the statement is +** successfully prepared, a pointer to the new statement handle is +** returned. It is the responsibility of the caller to eventually free the +** statement by calling sqlite3_finalize(). Alternatively, if statement +** compilation fails, NULL is returned, *pRc is set to an SQLite error +** code and *pzErr may be set to an error message buffer allocated by +** sqlite3_malloc(). +*/ static sqlite3_stmt *unionPrepare( - int *pRc, - sqlite3 *db, - const char *zSql, - char **pzErr + int *pRc, /* IN/OUT: Error code */ + sqlite3 *db, /* Database handle */ + const char *zSql, /* SQL statement to prepare */ + char **pzErr /* OUT: Error message */ ){ sqlite3_stmt *pRet = 0; if( *pRc==SQLITE_OK ){ @@ -151,6 +209,13 @@ static sqlite3_stmt *unionPrepare( return pRet; } +/* +** Call sqlite3_reset() on SQL statement pStmt. If *pRc is set to +** SQLITE_OK when this function is called, then it is set to the +** value returned by sqlite3_reset() before this function exits. +** In this case, *pzErr may be set to point to an error message +** buffer allocated by sqlite3_malloc(). +*/ static void unionReset(int *pRc, sqlite3_stmt *pStmt, char **pzErr){ int rc = sqlite3_reset(pStmt); if( *pRc==SQLITE_OK ){ @@ -161,6 +226,11 @@ static void unionReset(int *pRc, sqlite3_stmt *pStmt, char **pzErr){ } } +/* +** Call sqlite3_finalize() on SQL statement pStmt. If *pRc is set to +** SQLITE_OK when this function is called, then it is set to the +** value returned by sqlite3_finalize() before this function exits. +*/ static void unionFinalize(int *pRc, sqlite3_stmt *pStmt){ int rc = sqlite3_finalize(pStmt); if( *pRc==SQLITE_OK ) *pRc = rc; @@ -183,32 +253,72 @@ static int unionDisconnect(sqlite3_vtab *pVtab){ return SQLITE_OK; } +/* +** This function is a no-op if *pRc is other than SQLITE_OK when it is +** called. In this case it returns NULL. +** +** Otherwise, this function checks that the source table passed as the +** second argument (a) exists, (b) is not a view and (c) has a column +** named "_rowid_" of type "integer" that is the primary key. +** If this is not the case, *pRc is set to SQLITE_ERROR and NULL is +** returned. +** +** Finally, if the source table passes the checks above, a nul-terminated +** string describing the column names and types belonging to the source +** table is returned. Tables with the same set of column names and types +** cause this function to return identical strings. Is is the responsibility +** of the caller to free the returned string using sqlite3_free() when +** it is no longer required. +*/ static char *unionSourceToStr( - int *pRc, - UnionSrc *pSrc, + int *pRc, /* IN/OUT: Error code */ + sqlite3 *db, /* Database handle */ + UnionSrc *pSrc, /* Source table to test */ sqlite3_stmt *pStmt, - char **pzErr + char **pzErr /* OUT: Error message */ ){ 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", + int bPk = 0; + const char *zType = 0; + + int rc = sqlite3_table_column_metadata( + db, pSrc->zDb, pSrc->zTab, "_rowid_", &zType, 0, 0, &bPk, 0 + ); + if( rc==SQLITE_ERROR + || (rc==SQLITE_OK && (!bPk || sqlite3_stricmp("integer", zType))) + ){ + rc = SQLITE_ERROR; + *pzErr = sqlite3_mprintf("no such rowid table: %s%s%s", (pSrc->zDb ? pSrc->zDb : ""), (pSrc->zDb ? "." : ""), pSrc->zTab ); } + + if( rc==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(&rc, (const char*)sqlite3_column_text(pStmt, 0)); + } + unionReset(&rc, pStmt, pzErr); + } + + *pRc = rc; } + return zRet; } +/* +** Check that all configured source tables exist and have the same column +** names and datatypes. If this is not the case, or if some other error +** occurs, return an SQLite error code. In this case *pzErr may be set +** to point to an error message buffer allocated by sqlite3_mprintf(). +** Or, if no problems regarding the source tables are detected and no +** other error occurs, SQLITE_OK is returned. +*/ static int unionSourceCheck(UnionTab *pTab, char **pzErr){ const char *zSql = "SELECT group_concat(quote(name) || '.' || quote(type)) " @@ -225,10 +335,10 @@ static int unionSourceCheck(UnionTab *pTab, char **pzErr){ pStmt = unionPrepare(&rc, pTab->db, zSql, pzErr); if( rc==SQLITE_OK ){ - z0 = unionSourceToStr(&rc, &pTab->aSrc[0], pStmt, pzErr); + z0 = unionSourceToStr(&rc, pTab->db, &pTab->aSrc[0], pStmt, pzErr); } for(i=1; inSrc; i++){ - char *z = unionSourceToStr(&rc, &pTab->aSrc[i], pStmt, pzErr); + char *z = unionSourceToStr(&rc, pTab->db, &pTab->aSrc[i], pStmt, pzErr); if( rc==SQLITE_OK && sqlite3_stricmp(z, z0) ){ *pzErr = sqlite3_mprintf("source table schema mismatch"); rc = SQLITE_ERROR; @@ -272,21 +382,29 @@ static int unionConnect( }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; + char *zSql = 0; /* SQL statement */ + char *zArg = unionStrdup(&rc, argv[3]); /* Copy of argument to CVT */ + + /* Prepare the SQL statement. Instead of executing it directly, sort + ** the results by the "minimum rowid" field. This makes it easier to + ** check that there are no rowid range overlaps between source tables + ** and that the UnionTab.aSrc[] array is always sorted by rowid. */ + if( zArg ){ + unionDequote(zArg); + zSql = sqlite3_mprintf("SELECT * FROM (%s) ORDER BY 3", zArg); + sqlite3_free(zArg); + zArg = 0; } - if( zSql2==0 ){ + if( zSql==0 ){ rc = SQLITE_NOMEM; } + pStmt = unionPrepare(&rc, db, zSql, pzErr); + + /* Allocate the UnionTab structure */ pTab = unionMalloc(&rc, sizeof(UnionTab)); - pStmt = unionPrepare(&rc, db, zSql2, pzErr); + /* Iterate through the rows returned by the SQL statement specified + ** as an argument to the CREATE VIRTUAL TABLE statement. */ 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); @@ -294,6 +412,7 @@ static int unionConnect( sqlite3_int64 iMax = sqlite3_column_int64(pStmt, 3); UnionSrc *pSrc; + /* Grow the pTab->aSrc[] array if required. */ if( nAlloc<=pTab->nSrc ){ int nNew = nAlloc ? nAlloc*2 : 8; UnionSrc *aNew = (UnionSrc*)sqlite3_realloc( @@ -309,6 +428,7 @@ static int unionConnect( } } + /* Check for problems with the specified range of rowids */ if( iMaxnSrc>0 && iMin<=pTab->aSrc[pTab->nSrc-1].iMax) ){ *pzErr = sqlite3_mprintf("rowid range mismatch error"); rc = SQLITE_ERROR; @@ -322,10 +442,10 @@ static int unionConnect( } unionFinalize(&rc, pStmt); pStmt = 0; - sqlite3_free(zSql1); - sqlite3_free(zSql2); - zSql1 = 0; - zSql2 = 0; + sqlite3_free(zArg); + sqlite3_free(zSql); + zArg = 0; + zSql = 0; /* Verify that all source tables exist and have compatible schemas. */ if( rc==SQLITE_OK ){ @@ -335,16 +455,16 @@ static int unionConnect( /* Compose a CREATE TABLE statement and pass it to declare_vtab() */ if( rc==SQLITE_OK ){ - zSql1 = sqlite3_mprintf("SELECT " + zSql = 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; + if( zSql==0 ) rc = SQLITE_NOMEM; } - pStmt = unionPrepare(&rc, db, zSql1, pzErr); + pStmt = unionPrepare(&rc, db, zSql, pzErr); if( rc==SQLITE_OK ){ sqlite3_bind_text(pStmt, 1, pTab->aSrc[0].zDb, -1, SQLITE_STATIC); if( SQLITE_ROW==sqlite3_step(pStmt) ){ @@ -354,7 +474,7 @@ static int unionConnect( } unionFinalize(&rc, pStmt); - sqlite3_free(zSql1); + sqlite3_free(zSql); } if( rc!=SQLITE_OK ){ @@ -448,36 +568,59 @@ static int unionFilter( int rc = SQLITE_OK; int i; char *zSql = 0; + int bZero = 0; - int bMinValid = 0; - int bMaxValid = 0; - sqlite3_int64 iMin; - sqlite3_int64 iMax; + sqlite3_int64 iMin = SMALLEST_INT64; + sqlite3_int64 iMax = LARGEST_INT64; + + assert( idxNum==0 + || idxNum==SQLITE_INDEX_CONSTRAINT_EQ + || idxNum==SQLITE_INDEX_CONSTRAINT_LE + || idxNum==SQLITE_INDEX_CONSTRAINT_GE + || idxNum==SQLITE_INDEX_CONSTRAINT_LT + || idxNum==SQLITE_INDEX_CONSTRAINT_GT + || idxNum==(SQLITE_INDEX_CONSTRAINT_GE|SQLITE_INDEX_CONSTRAINT_LE) + ); 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 ){ + + if( idxNum & (SQLITE_INDEX_CONSTRAINT_LE|SQLITE_INDEX_CONSTRAINT_LT) ){ assert( argc>=1 ); iMax = sqlite3_value_int64(argv[0]); - bMaxValid = 1; + if( idxNum & SQLITE_INDEX_CONSTRAINT_LT ){ + if( iMax==SMALLEST_INT64 ){ + bZero = 1; + }else{ + iMax--; + } + } } - if( idxNum & SQLITE_INDEX_CONSTRAINT_GE ){ + + if( idxNum & (SQLITE_INDEX_CONSTRAINT_GE|SQLITE_INDEX_CONSTRAINT_GT) ){ assert( argc>=1 ); iMin = sqlite3_value_int64(argv[argc-1]); - bMinValid = 1; + if( idxNum & SQLITE_INDEX_CONSTRAINT_GT ){ + if( iMin==LARGEST_INT64 ){ + bZero = 1; + }else{ + iMin++; + } + } } } - sqlite3_finalize(pCsr->pStmt); pCsr->pStmt = 0; + if( bZero ){ + return SQLITE_OK; + } for(i=0; inSrc; i++){ UnionSrc *pSrc = &pTab->aSrc[i]; - if( (bMinValid && iMin>pSrc->iMax) || (bMaxValid && iMaxiMin) ){ + if( iMin>pSrc->iMax || iMaxiMin ){ continue; } @@ -495,15 +638,15 @@ static int unionFilter( } if( zSql ){ - if( bMinValid && bMaxValid && iMin==iMax ){ + if( iMin==iMax ){ zSql = sqlite3_mprintf("%z WHERE rowid=%lld", zSql, iMin); }else{ const char *zWhere = "WHERE"; - if( bMinValid && iMin>pSrc->iMin ){ + if( iMin!=SMALLEST_INT64 && iMin>pSrc->iMin ){ zSql = sqlite3_mprintf("%z WHERE rowid>=%lld", zSql, iMin); zWhere = "AND"; } - if( bMaxValid && iMaxiMax ){ + if( iMax!=LARGEST_INT64 && iMaxiMax ){ zSql = sqlite3_mprintf("%z %s rowid<=%lld", zSql, zWhere, iMax); } } @@ -521,6 +664,21 @@ static int unionFilter( /* ** xBestIndex. +** +** This implementation searches for constraints on the rowid field. EQ, +** LE, LT, GE and GT are handled. +** +** If there is an EQ comparison, then idxNum is set to INDEX_CONSTRAINT_EQ. +** In this case the only argument passed to xFilter is the rhs of the == +** operator. +** +** Otherwise, if an LE or LT constraint is found, then the INDEX_CONSTRAINT_LE +** or INDEX_CONSTRAINT_LT (but not both) bit is set in idxNum. The first +** argument to xFilter is the rhs of the <= or < operator. Similarly, if +** an GE or GT constraint is found, then the INDEX_CONSTRAINT_GE or +** INDEX_CONSTRAINT_GT bit is set in idxNum. The rhs of the >= or > operator +** is passed as either the first or second argument to xFilter, depending +** on whether or not there is also a LT|LE constraint. */ static int unionBestIndex( sqlite3_vtab *tab, @@ -556,6 +714,7 @@ static int unionBestIndex( pIdxInfo->estimatedCost = 3.0; pIdxInfo->idxNum = SQLITE_INDEX_CONSTRAINT_EQ; pIdxInfo->aConstraintUsage[iEq].argvIndex = 1; + pIdxInfo->aConstraintUsage[iEq].omit = 1; }else{ int iCons = 1; int idxNum = 0; @@ -563,12 +722,14 @@ static int unionBestIndex( if( iLt>=0 ){ nRow = nRow / 2; pIdxInfo->aConstraintUsage[iLt].argvIndex = iCons++; - idxNum |= SQLITE_INDEX_CONSTRAINT_LE; + pIdxInfo->aConstraintUsage[iLt].omit = 1; + idxNum |= pIdxInfo->aConstraint[iLt].op; } if( iGt>=0 ){ nRow = nRow / 2; pIdxInfo->aConstraintUsage[iGt].argvIndex = iCons++; - idxNum |= SQLITE_INDEX_CONSTRAINT_GE; + pIdxInfo->aConstraintUsage[iGt].omit = 1; + idxNum |= pIdxInfo->aConstraint[iGt].op; } pIdxInfo->estimatedRows = nRow; pIdxInfo->estimatedCost = 3.0 * (double)nRow; @@ -578,6 +739,9 @@ static int unionBestIndex( return SQLITE_OK; } +/* +** Register the unionvtab virtual table module with database handle db. +*/ static int createUnionVtab(sqlite3 *db){ static sqlite3_module unionModule = { 0, /* iVersion */ @@ -623,4 +787,3 @@ int sqlite3_unionvtab_init( return rc; } -#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_UNIONVTAB) */ diff --git a/manifest b/manifest index 51a952a0f8..cb797c6b83 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\s"unionvtab"\svirtual\stable\sextension\sin\sext/misc/unionvtab.c. -D 2017-07-15T20:48:30.280 +C Enhance\serror\sdetection\sand\sfix\sother\sissues\sin\sunionvtab\scode. +D 2017-07-17T20:25:21.632 F Makefile.in d9873c9925917cca9990ee24be17eb9613a668012c85a343aef7e5536ae266e8 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 20850e3e8d4d4791e0531955852d768eb06f24138214870d543abb1a47346fba @@ -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 594b8cebd11297c45d87fed5985548fa141d917f08e672aadea06c14f18bcf42 +F ext/misc/unionvtab.c e5b0f62563839da45952be404b80ba592a1183fae8c6976036177edc6c713ef7 F ext/misc/vfslog.c fe40fab5c077a40477f7e5eba994309ecac6cc95 F ext/misc/vfsstat.c bf10ef0bc51e1ad6756629e1edb142f7a8db1178 F ext/misc/vtshim.c 1976e6dd68dd0d64508c91a6dfab8e75f8aaf6cd @@ -1432,7 +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/unionvtab.test 41b5d8c843ed4968aa20ec5f25ab501256a81515952846ed6e1d48e4ece09f7e F test/unique.test 93f8b2ef5ea51b9495f8d6493429b1fd0f465264 F test/unique2.test 3674e9f2a3f1fbbfd4772ac74b7a97090d0f77d2 F test/unixexcl.test d936ba2b06794018e136418addd59a2354eeae97 @@ -1635,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 253945d480b052bfe311888022b5eb0be91c8c80cda05036e58207d57520262c -R ee0a9dc5b1464f766485894fa2069cae +P 62a86aa6c0519cf1fa232169122d3d6ae8d2f66b20530fb934a82a15712bd2f0 +R 84247a701b2728604bb146e760fbf4dd U dan -Z d195e18cbc4e4f34f8cf4b9b0b0c8aaa +Z fe159fd3b9fb252212619f29108c42d6 diff --git a/manifest.uuid b/manifest.uuid index 58f352fc6e..5a6f337c5f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -62a86aa6c0519cf1fa232169122d3d6ae8d2f66b20530fb934a82a15712bd2f0 \ No newline at end of file +9c3f1b9a82e500e015deb0cc669fbb32e7f0cdc69f926ceff383ab946f8d8d18 \ No newline at end of file diff --git a/test/unionvtab.test b/test/unionvtab.test index 29403afd83..c72b8e6e4c 100644 --- a/test/unionvtab.test +++ b/test/unionvtab.test @@ -98,13 +98,13 @@ do_catchsql_test 2.1.3 { do_catchsql_test 2.2.1 { CREATE VIRTUAL TABLE temp.u1 USING unionvtab("VALUES(NULL, 't555', 1, 100)"); -} {1 {no such table: t555}} +} {1 {no such rowid 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}} +} {1 {no such rowid 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'}} +} {1 {no such rowid table: xua.t555}} do_execsql_test 2.4.0 { CREATE TABLE x1(a BLOB, b);