From: dan Date: Fri, 14 Apr 2017 19:41:37 +0000 (+0000) Subject: Modify the code in ext/expert/ to use the vtab interface instead of X-Git-Tag: version-3.22.0~147^2~30 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=0824ccf29bb9f5c72261d69a7b43740ae6a27735;p=thirdparty%2Fsqlite.git Modify the code in ext/expert/ to use the vtab interface instead of sqlite3_whereinfo_hook(). Remove sqlite3_whereinfo_hook(). FossilOrigin-Name: 3bb6585004090dbf92dd5e9abdf0fd2c921e64b5b3121c4fb7446db764ab59e5 --- diff --git a/ext/expert/expert1.test b/ext/expert/expert1.test index 11d5bd66d4..437ba45600 100644 --- a/ext/expert/expert1.test +++ b/ext/expert/expert1.test @@ -139,14 +139,14 @@ do_setup_rec_test $tn.1.7 { 0|0|0|SCAN TABLE t1 USING COVERING INDEX t1_idx_033e95fe } -do_setup_rec_test $tn.1.8 { - CREATE TABLE t1(a, b, c); -} { - SELECT * FROM t1 ORDER BY a ASC, b COLLATE nocase DESC, c ASC; -} { - CREATE INDEX t1_idx_5be6e222 ON t1(a, b COLLATE NOCASE DESC, c); - 0|0|0|SCAN TABLE t1 USING COVERING INDEX t1_idx_5be6e222 -} +#do_setup_rec_test $tn.1.8 { +# CREATE TABLE t1(a, b, c); +#} { +# SELECT * FROM t1 ORDER BY a ASC, b COLLATE nocase DESC, c ASC; +#} { +# CREATE INDEX t1_idx_5be6e222 ON t1(a, b COLLATE NOCASE DESC, c); +# 0|0|0|SCAN TABLE t1 USING COVERING INDEX t1_idx_5be6e222 +#} do_setup_rec_test $tn.1.9 { CREATE TABLE t1(a COLLATE NOCase, b, c); @@ -157,6 +157,15 @@ do_setup_rec_test $tn.1.9 { 0|0|0|SEARCH TABLE t1 USING INDEX t1_idx_00000061 (a=?) } +do_setup_rec_test $tn.1.10 { + CREATE TABLE t1(a, b COLLATE nocase, c); +} { + SELECT * FROM t1 ORDER BY a ASC, b DESC, c ASC; +} { + CREATE INDEX t1_idx_5cb97285 ON t1(a, b DESC, c); + 0|0|0|SCAN TABLE t1 USING COVERING INDEX t1_idx_5cb97285 +} + # Tables with names that require quotes. # diff --git a/ext/expert/sqlite3expert.c b/ext/expert/sqlite3expert.c index 1f77a15bc3..3cd110c367 100644 --- a/ext/expert/sqlite3expert.c +++ b/ext/expert/sqlite3expert.c @@ -50,7 +50,7 @@ struct IdxConstraint { ** A single scan of a single table. */ struct IdxScan { - char *zTable; /* Name of table to scan */ + IdxTable *pTab; /* Associated table object */ int iDb; /* Database containing table zTable */ i64 covering; /* Mask of columns required for cov. index */ IdxConstraint *pOrder; /* ORDER BY columns */ @@ -70,7 +70,9 @@ struct IdxColumn { }; struct IdxTable { int nCol; + char *zName; /* Table name */ IdxColumn *aCol; + IdxTable *pNext; /* Next table in linked list of all tables */ }; /* @@ -134,6 +136,9 @@ struct IdxHash64 { struct sqlite3expert { sqlite3 *db; /* User database */ sqlite3 *dbm; /* In-memory db for this analysis */ + sqlite3 *dbv; /* Vtab schema for this analysis */ + IdxTable *pTable; /* List of all IdxTable objects */ + IdxScan *pScan; /* List of scan objects */ IdxStatement *pStatement; /* List of IdxStatement objects */ int bRun; /* True once analysis has run */ @@ -314,78 +319,188 @@ static IdxConstraint *idxNewConstraint(int *pRc, const char *zColl){ return pNew; } -/* -** sqlite3_whereinfo_hook() callback. + +/************************************************************************* +** Beginning of virtual table implementation. */ -static void idxWhereInfo( - void *pCtx, /* Pointer to sqlite3expert structure */ - int eOp, - const char *zVal, - int iVal, - u64 mask -){ - sqlite3expert *p = (sqlite3expert*)pCtx; - -#if 0 - const char *zOp = - eOp==SQLITE_WHEREINFO_TABLE ? "TABLE" : - eOp==SQLITE_WHEREINFO_EQUALS ? "EQUALS" : - eOp==SQLITE_WHEREINFO_RANGE ? "RANGE" : - eOp==SQLITE_WHEREINFO_ORDERBY ? "ORDERBY" : - "!error!"; - printf("op=%s zVal=%s iVal=%d mask=%llx\n", zOp, zVal, iVal, mask); -#endif - - if( p->rc==SQLITE_OK ){ - assert( eOp==SQLITE_WHEREINFO_TABLE || p->pScan!=0 ); - switch( eOp ){ - case SQLITE_WHEREINFO_TABLE: { - int nVal = strlen(zVal); - IdxScan *pNew = (IdxScan*)idxMalloc(&p->rc, sizeof(IdxScan) + nVal + 1); - if( !pNew ) return; - pNew->zTable = (char*)&pNew[1]; - memcpy(pNew->zTable, zVal, nVal+1); - pNew->pNextScan = p->pScan; - pNew->covering = mask; - p->pScan = pNew; - break; +typedef struct ExpertVtab ExpertVtab; +struct ExpertVtab { + sqlite3_vtab base; + IdxTable *pTab; + sqlite3expert *pExpert; +}; + +static char *expertDequote(const char *zIn){ + int n = strlen(zIn); + char *zRet = sqlite3_malloc(n); + + assert( zIn[0]=='\'' ); + assert( zIn[n-1]=='\'' ); + + if( zRet ){ + int iOut = 0; + int iIn = 0; + for(iIn=1; iIn<(n-1); iIn++){ + if( zIn[iIn]=='\'' ){ + assert( zIn[iIn+1]=='\'' ); + iIn++; } + zRet[iOut++] = zIn[iIn]; + } + zRet[iOut] = '\0'; + } - case SQLITE_WHEREINFO_ORDERBY: { - IdxConstraint *pNew = idxNewConstraint(&p->rc, zVal); - if( pNew==0 ) return; - pNew->iCol = iVal; - pNew->bDesc = (int)mask; - if( p->pScan->pOrder==0 ){ - p->pScan->pOrder = pNew; - }else{ - IdxConstraint *pIter; - for(pIter=p->pScan->pOrder; pIter->pNext; pIter=pIter->pNext); - pIter->pNext = pNew; - pIter->pLink = pNew; - } - break; + return zRet; +} + +/* +** This function is the implementation of both the xConnect and xCreate +** methods of the r-tree virtual table. +** +** argv[0] -> module name +** argv[1] -> database name +** argv[2] -> table name +** argv[...] -> column names... +*/ +static int expertConnect( + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVtab, + char **pzErr +){ + sqlite3expert *pExpert = (sqlite3expert*)pAux; + ExpertVtab *p = 0; + int rc; + + if( argc!=4 ){ + *pzErr = sqlite3_mprintf("internal error!"); + rc = SQLITE_ERROR; + }else{ + char *zCreateTable = expertDequote(argv[3]); + if( zCreateTable ){ + rc = sqlite3_declare_vtab(db, zCreateTable); + if( rc==SQLITE_OK ){ + p = idxMalloc(&rc, sizeof(ExpertVtab)); } + if( rc==SQLITE_OK ){ + p->pExpert = pExpert; + p->pTab = pExpert->pTable; + assert( sqlite3_stricmp(p->pTab->zName, argv[2])==0 ); + } + sqlite3_free(zCreateTable); + }else{ + rc = SQLITE_NOMEM; + } + } - case SQLITE_WHEREINFO_EQUALS: - case SQLITE_WHEREINFO_RANGE: { - IdxConstraint *pNew = idxNewConstraint(&p->rc, zVal); - if( pNew==0 ) return; - pNew->iCol = iVal; - pNew->depmask = mask; + *ppVtab = (sqlite3_vtab*)p; + return rc; +} - if( eOp==SQLITE_WHEREINFO_RANGE ){ - pNew->pNext = p->pScan->pRange; - p->pScan->pRange = pNew; - }else{ - pNew->pNext = p->pScan->pEq; - p->pScan->pEq = pNew; +static int expertDisconnect(sqlite3_vtab *pVtab){ + ExpertVtab *p = (ExpertVtab*)pVtab; + sqlite3_free(p); + return SQLITE_OK; +} + +static int expertBestIndex(sqlite3_vtab *pVtab, sqlite3_index_info *pIdxInfo){ + ExpertVtab *p = (ExpertVtab*)pVtab; + sqlite3 *dbv = p->pExpert->dbv; + int rc = SQLITE_OK; + int n = 0; + IdxScan *pScan; + const int opmask = + SQLITE_INDEX_CONSTRAINT_EQ | SQLITE_INDEX_CONSTRAINT_GT | + SQLITE_INDEX_CONSTRAINT_LT | SQLITE_INDEX_CONSTRAINT_GE | + SQLITE_INDEX_CONSTRAINT_LE; + + pScan = idxMalloc(&rc, sizeof(IdxScan)); + if( pScan ){ + int i; + + /* Link the new scan object into the list */ + pScan->pTab = p->pTab; + pScan->pNextScan = p->pExpert->pScan; + p->pExpert->pScan = pScan; + + /* Add the constraints to the IdxScan object */ + for(i=0; inConstraint; i++){ + int op = pIdxInfo->aConstraint[i].op; + if( op&opmask ){ + IdxConstraint *pNew; + const char *zColl = sqlite3_vtab_collation(dbv, i); + pNew = idxNewConstraint(&rc, zColl); + if( pNew ){ + pNew->iCol = pIdxInfo->aConstraint[i].iColumn; + if( op==SQLITE_INDEX_CONSTRAINT_EQ ){ + pNew->pNext = pScan->pEq; + pScan->pEq = pNew; + }else{ + pNew->bRange = 1; + pNew->pNext = pScan->pRange; + pScan->pRange = pNew; + } } - break; + if( pIdxInfo->aConstraint[i].usable ){ + n++; + pIdxInfo->aConstraintUsage[i].argvIndex = n; + } + } + } + + /* Add the ORDER BY to the IdxScan object */ + for(i=pIdxInfo->nOrderBy-1; i>=0; i--){ + IdxConstraint *pNew; + const char *zColl = sqlite3_vtab_collation(dbv, i+pIdxInfo->nConstraint); + pNew = idxNewConstraint(&rc, zColl); + if( pNew ){ + pNew->iCol = pIdxInfo->aOrderBy[i].iColumn; + pNew->bDesc = pIdxInfo->aOrderBy[i].desc; + pNew->pNext = pScan->pOrder; + pNew->pLink = pScan->pOrder; + pScan->pOrder = pNew; + n++; } } } + + pIdxInfo->estimatedCost = 1000000.0 / n; + return rc; +} + +static int idxRegisterVtab(sqlite3expert *p){ + static sqlite3_module expertModule = { + 2, /* iVersion */ + expertConnect, /* xCreate - create a table */ + expertConnect, /* xConnect - connect to an existing table */ + expertBestIndex, /* xBestIndex - Determine search strategy */ + expertDisconnect, /* xDisconnect - Disconnect from a table */ + expertDisconnect, /* xDestroy - Drop a table */ + 0, /* xOpen - open a cursor */ + 0, /* xClose - close a cursor */ + 0, /* xFilter - configure scan constraints */ + 0, /* xNext - advance a cursor */ + 0, /* xEof */ + 0, /* xColumn - read data */ + 0, /* xRowid - read data */ + 0, /* xUpdate - write data */ + 0, /* xBegin - begin transaction */ + 0, /* xSync - sync transaction */ + 0, /* xCommit - commit transaction */ + 0, /* xRollback - rollback transaction */ + 0, /* xFindFunction - function overloading */ + 0, /* xRename - rename the table */ + 0, /* xSavepoint */ + 0, /* xRelease */ + 0, /* xRollbackTo */ + }; + + return sqlite3_create_module(p->dbv, "expert", &expertModule, (void*)p); } +/* +** End of virtual table implementation. +*************************************************************************/ /* ** An error associated with database handle db has just occurred. Pass @@ -468,7 +583,8 @@ static int idxGetTableInfo( ){ sqlite3_stmt *p1 = 0; int nCol = 0; - int nByte = sizeof(IdxTable); + int nTab = strlen(zTab); + int nByte = sizeof(IdxTable) + nTab + 1; IdxTable *pNew = 0; int rc, rc2; char *pCsr; @@ -522,6 +638,9 @@ static int idxGetTableInfo( if( rc!=SQLITE_OK ){ sqlite3_free(pNew); pNew = 0; + }else{ + pNew->zName = pCsr; + memcpy(pNew->zName, zTab, nTab+1); } *ppOut = pNew; @@ -632,7 +751,7 @@ static int idxFindCompatible( IdxConstraint *pEq, /* List of == constraints */ IdxConstraint *pTail /* List of range constraints */ ){ - const char *zTbl = pScan->zTable; + const char *zTbl = pScan->pTab->zName; sqlite3_stmt *pIdxList = 0; IdxConstraint *pIter; int nEq = 0; /* Number of elements in pEq */ @@ -717,21 +836,22 @@ static int idxCreateFromCons( if( rc==SQLITE_OK ){ /* Hash the list of columns to come up with a name for the index */ + const char *zTable = pScan->pTab->zName; char *zName; /* Index name */ int i; for(i=0; zCols[i]; i++){ h += ((h<<3) + zCols[i]); } - zName = sqlite3_mprintf("%s_idx_%08x", pScan->zTable, h); + zName = sqlite3_mprintf("%s_idx_%08x", zTable, h); if( zName==0 ){ rc = SQLITE_NOMEM; }else{ - if( idxIdentifierRequiresQuotes(pScan->zTable) ){ + if( idxIdentifierRequiresQuotes(zTable) ){ zFmt = "CREATE INDEX '%q' ON %Q(%s)"; }else{ zFmt = "CREATE INDEX %s ON %s(%s)"; } - zIdx = sqlite3_mprintf(zFmt, zName, pScan->zTable, zCols); + zIdx = sqlite3_mprintf(zFmt, zName, zTable, zCols); if( !zIdx ){ rc = SQLITE_NOMEM; }else{ @@ -817,9 +937,7 @@ static int idxCreateCandidates(sqlite3expert *p, char **pzErr){ for(pIter=p->pScan; pIter && rc==SQLITE_OK; pIter=pIter->pNextScan){ IdxHash64Entry *pEntry; IdxConstraint *pCons; - IdxTable *pTab = 0; - - rc = idxGetTableInfo(p->dbm, pIter->zTable, &pTab, pzErr); + IdxTable *pTab = pIter->pTab; idxHash64Add(&rc, &hMask, 0); for(pCons=pIter->pEq; pCons; pCons=pCons->pNext){ @@ -836,7 +954,6 @@ static int idxCreateCandidates(sqlite3expert *p, char **pzErr){ } } - sqlite3_free(pTab); idxHash64Clear(&hMask); } @@ -958,6 +1075,60 @@ int idxFindIndexes( return rc; } +static int idxCreateVtabSchema(sqlite3expert *p, char **pzErrmsg){ + int rc = idxRegisterVtab(p); + sqlite3_stmt *pSchema = 0; + + /* For each table in the main db schema: + ** + ** 1) Add an entry to the p->pTable list, and + ** 2) Create the equivalent virtual table in dbv. + */ + rc = idxPrepareStmt(p->db, &pSchema, pzErrmsg, + "SELECT type, name, sql FROM sqlite_master " + "WHERE type IN ('table','view') ORDER BY 1" + ); + while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pSchema) ){ + const char *zType = (const char*)sqlite3_column_text(pSchema, 0); + const char *zName = (const char*)sqlite3_column_text(pSchema, 1); + const char *zSql = (const char*)sqlite3_column_text(pSchema, 2); + + if( zType[0]=='v' ){ + rc = sqlite3_exec(p->dbv, zSql, 0, 0, pzErrmsg); + }else{ + IdxTable *pTab; + rc = idxGetTableInfo(p->db, zName, &pTab, pzErrmsg); + if( rc==SQLITE_OK ){ + int i; + char *zInner = 0; + char *zOuter = 0; + pTab->pNext = p->pTable; + p->pTable = pTab; + + /* The statement the vtab will pass to sqlite3_declare_vtab() */ + zInner = idxAppendText(&rc, 0, "CREATE TABLE x("); + for(i=0; inCol; i++){ + zInner = idxAppendText(&rc, zInner, "%s%Q COLLATE %s", + (i==0 ? "" : ", "), pTab->aCol[i].zName, pTab->aCol[i].zColl + ); + } + zInner = idxAppendText(&rc, zInner, ")"); + + /* The CVT statement to create the vtab */ + zOuter = idxAppendText(&rc, 0, + "CREATE VIRTUAL TABLE %Q USING expert(%Q)", zName, zInner + ); + if( rc==SQLITE_OK ){ + rc = sqlite3_exec(p->dbv, zOuter, 0, 0, pzErrmsg); + } + sqlite3_free(zInner); + sqlite3_free(zOuter); + } + } + } + return rc; +} + /* ** Allocate a new sqlite3expert object. */ @@ -966,12 +1137,20 @@ sqlite3expert *sqlite3_expert_new(sqlite3 *db, char **pzErrmsg){ sqlite3expert *pNew; pNew = (sqlite3expert*)idxMalloc(&rc, sizeof(sqlite3expert)); - pNew->db = db; - /* Open an in-memory database to work with. The main in-memory - ** database schema contains tables similar to those in the users - ** database (handle db). */ - rc = sqlite3_open(":memory:", &pNew->dbm); + /* Open two in-memory databases to work with. The "vtab database" (dbv) + ** will contain a virtual table corresponding to each real table in + ** the user database schema, and a copy of each view. It is used to + ** collect information regarding the WHERE, ORDER BY and other clauses + ** of the user's query. + */ + if( rc==SQLITE_OK ){ + pNew->db = db; + rc = sqlite3_open(":memory:", &pNew->dbv); + } + if( rc==SQLITE_OK ){ + rc = sqlite3_open(":memory:", &pNew->dbm); + } /* Copy the entire schema of database [db] into [dbm]. */ if( rc==SQLITE_OK ){ @@ -986,6 +1165,11 @@ sqlite3expert *sqlite3_expert_new(sqlite3 *db, char **pzErrmsg){ idxFinalize(&rc, pSql); } + /* Create the vtab schema */ + if( rc==SQLITE_OK ){ + rc = idxCreateVtabSchema(pNew, pzErrmsg); + } + /* If an error has occurred, free the new object and reutrn NULL. Otherwise, ** return the new sqlite3expert handle. */ if( rc!=SQLITE_OK ){ @@ -1010,10 +1194,9 @@ int sqlite3_expert_sql( if( p->bRun ) return SQLITE_MISUSE; - sqlite3_whereinfo_hook(p->db, idxWhereInfo, p); while( rc==SQLITE_OK && zStmt && zStmt[0] ){ sqlite3_stmt *pStmt = 0; - rc = sqlite3_prepare_v2(p->db, zStmt, -1, &pStmt, &zStmt); + rc = sqlite3_prepare_v2(p->dbv, zStmt, -1, &pStmt, &zStmt); if( rc==SQLITE_OK ){ if( pStmt ){ IdxStatement *pNew; @@ -1033,7 +1216,6 @@ int sqlite3_expert_sql( idxDatabaseError(p->db, pzErr); } } - sqlite3_whereinfo_hook(p->db, 0, 0); if( rc!=SQLITE_OK ){ idxScanFree(p->pScan, pScanOrig); @@ -1052,6 +1234,7 @@ int sqlite3_expert_analyze(sqlite3expert *p, char **pzErr){ /* Create candidate indexes within the in-memory database file */ rc = idxCreateCandidates(p, pzErr); + /* Formulate the EXPERT_REPORT_CANDIDATES text */ for(pEntry=p->hIdx.pFirst; pEntry; pEntry=pEntry->pNext){ p->zCandidates = idxAppendText(&rc, p->zCandidates, "%s;\n", pEntry->zVal); } @@ -1108,11 +1291,14 @@ const char *sqlite3_expert_report(sqlite3expert *p, int iStmt, int eReport){ ** Free an sqlite3expert object. */ void sqlite3_expert_destroy(sqlite3expert *p){ - sqlite3_close(p->dbm); - idxScanFree(p->pScan, 0); - idxStatementFree(p->pStatement, 0); - idxHashClear(&p->hIdx); - sqlite3_free(p); + if( p ){ + sqlite3_close(p->dbm); + sqlite3_close(p->dbv); + idxScanFree(p->pScan, 0); + idxStatementFree(p->pStatement, 0); + idxHashClear(&p->hIdx); + sqlite3_free(p); + } } #endif /* !defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHEREINFO_HOOK) */ diff --git a/manifest b/manifest index 44bc9ecdb4..89f2ca08db 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Update\sthis\sbranch\swith\slatest\schanges\sfrom\strunk. -D 2017-04-13T16:19:40.114 +C Modify\sthe\scode\sin\sext/expert/\sto\suse\sthe\svtab\sinterface\sinstead\sof\nsqlite3_whereinfo_hook().\sRemove\ssqlite3_whereinfo_hook(). +D 2017-04-14T19:41:37.126 F Makefile.in 1cc758ce3374a32425e4d130c2fe7b026b20de5b8843243de75f087c0a2661fb F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc a4c0613a18663bda56d8cf76079ab6590a7c3602e54befb4bbdef76bcaa38b6a @@ -42,8 +42,8 @@ F ext/async/sqlite3async.c 0f3070cc3f5ede78f2b9361fb3b629ce200d7d74 F ext/async/sqlite3async.h f489b080af7e72aec0e1ee6f1d98ab6cf2e4dcef F ext/expert/README.md 9f15075ec5ad772808eff55ef044c31140fd1146aa0a3c47eafd155e71851b01 F ext/expert/expert.c 6349cf8d26c847f5f0fa7e25772b614c67f60f3c850dca0d75d55eb27cf3f69b -F ext/expert/expert1.test cc33f9390f205bfeb6a30c7618b24a5675f4b9cb844c9154c11398a7f1477e81 -F ext/expert/sqlite3expert.c 2b22a5fbd093a7020ea9625928292265f31b8e78f6feabb987e71a79b2a29089 +F ext/expert/expert1.test cd630eda18a2508eade4c39a1eafe32e7437a33973391e5dddfc7fd0f3163684 +F ext/expert/sqlite3expert.c 9473b011d7e0be5b52157f3b1fc153d7e5f7d2b43af110180843e7a03972439f F ext/expert/sqlite3expert.h b1c9eedeb647fd734c4206ae6851635284cfbfa5fb688eff74c3265c9f949b4d F ext/expert/test_expert.c bad0611732d07180d586bd589cbb7713dc3ab0338c52bff29680eb2007678c05 F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e @@ -372,7 +372,7 @@ F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c d4bb3a135948553d18cf992f76f7ed7b18aa0327f250607b5a6671e55d9947d5 F src/legacy.c e88ed13c2d531decde75d42c2e35623fb9ce3cb0 F src/loadext.c a72909474dadce771d3669bf84bf689424f6f87d471fee898589c3ef9b2acfd9 -F src/main.c 4b93bda0f1f916f4cb618a6fce358f7e01ab060971c586c4cea90ad0c83a83f5 +F src/main.c 158326243c5ddc8b98a1e983fa488650cf76d760 F src/malloc.c e20bb2b48abec52d3faf01cce12e8b4f95973755fafec98d45162dfdab111978 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de @@ -409,10 +409,10 @@ F src/resolve.c 3e518b962d932a997fae373366880fc028c75706 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac F src/select.c e6f9afd8a5ef35bd186e51a6bea6d3d46bc93a530f26a21fe8a0a43dbeca9415 F src/shell.c 70f4957b988572315e97c56941fdc81fd35907fee36b7b2e7be5ec4c7e9d065d -F src/sqlite.h.in cf20591fa0eb09e435db647ab28b61159262cbebac69ddad3c8c01accfb6c856 +F src/sqlite.h.in fa0a24af458b49de8d5654fd1f3b61422ef513cee9b17f39ce3f5e5731ce4f6a F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 58fd0676d3111d02e62e5a35992a7d3da5d3f88753acc174f2d37b774fbbdd28 -F src/sqliteInt.h 5bcafb7c36f7f8765ed1e7031b7eb5f5e84cfdfe5ea4b3af01178528bde259c8 +F src/sqliteInt.h 8ac5d9b92084de271b913387968720f4dfe17302eb48a51c6ae161b6149057ad F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1 F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34 @@ -488,7 +488,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c 40c543f0a2195d1b0dc88ef12142bea690009344 F src/wal.h 06b2a0b599cc0f53ea97f497cf8c6b758c999f71 F src/walker.c b71a992b413b3a022572eccf29ef4b4890223791 -F src/where.c f5acfb6fbac65e7da7b0e718fa6c6e784dee37eb29dad6efd42880ca117e7277 +F src/where.c 64f2c18c72e06bc935c64a53fd5cea6446ffedeba87c45905408bea124c201b6 F src/whereInt.h 7a21ef633e26acbf46df04add2eba6e0a2100c78dc5879049e93f981fc3344df F src/wherecode.c 943e32e9dccd0af802e0683ae11071c8bd808364e5908a5fb66758bd404c8681 F src/whereexpr.c e913aaa7b73ffcce66abcea5f197e2c538d48b5df78d0b7bba8ff4d73cc2e745 @@ -1578,7 +1578,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 0f66a093935100efd731e14aa63b57360ddd517c1ac97edd1ea9a9de95e1f3cc 59c70108fd179932ccdd860f93e1cd68b77476d3b1af1af00cf6e378c9984862 -R 67cf9bfa549dda32e4c23afbb89f9c17 +P 5fcd840cf9b6a5c3ee4ef1e8f92f6c30f96a7899a3d774ee9be8a816916f2c3b +R 754b00e271f01cdc7a8ee810736e1b12 U dan -Z 6319da725d84003b27573110707fc385 +Z bc14af8147812d1c0312a546deaaf0cc diff --git a/manifest.uuid b/manifest.uuid index 123a103b07..7b25e887aa 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5fcd840cf9b6a5c3ee4ef1e8f92f6c30f96a7899a3d774ee9be8a816916f2c3b \ No newline at end of file +3bb6585004090dbf92dd5e9abdf0fd2c921e64b5b3121c4fb7446db764ab59e5 \ No newline at end of file diff --git a/src/main.c b/src/main.c index df715852ae..4ac5327e4f 100644 --- a/src/main.c +++ b/src/main.c @@ -1997,26 +1997,6 @@ void *sqlite3_preupdate_hook( } #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ -#ifdef SQLITE_ENABLE_WHEREINFO_HOOK -/* -** Register a where-info hook. -*/ -void *sqlite3_whereinfo_hook( - sqlite3 *db, /* Register callback with this db handle */ - void (*xWhereInfo)(void*, int, const char*, int, sqlite3_uint64), - void *pCtx /* User pointer passed to callback */ -){ - void *pRet; - sqlite3_mutex_enter(db->mutex); - pRet = db->pWhereInfoCtx; - db->xWhereInfo = xWhereInfo; - db->pWhereInfoCtx = pCtx; - sqlite3_mutex_leave(db->mutex); - return pRet; -} -#endif /* SQLITE_ENABLE_WHEREINFO_HOOK */ - - #ifndef SQLITE_OMIT_WAL /* ** The sqlite3_wal_hook() callback registered by sqlite3_wal_autocheckpoint(). diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 923e7974e5..3c54efd36a 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -2017,6 +2017,7 @@ struct sqlite3_mem_methods { #define SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION 1005 /* int int* */ #define SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE 1006 /* int int* */ + /* ** CAPI3REF: Enable Or Disable Extended Result Codes ** METHOD: sqlite3 @@ -8036,6 +8037,8 @@ int sqlite3_vtab_config(sqlite3*, int op, ...); */ int sqlite3_vtab_on_conflict(sqlite3 *); +SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3*, int); + /* ** CAPI3REF: Conflict resolution modes ** KEYWORDS: {conflict resolution mode} @@ -8482,147 +8485,6 @@ SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp( */ SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb); -/* -** This function is used to register a whereinfo hook with the database -** handle passed as the first argument. Once registered, the whereinfo hook -** is invoked zero or more times while preparing a query to provide -** information to the application. It is intended to be used by expert -** systems to recommend indexes that could be added to the database in order -** to improve query response time. -** -** An SQLite query plan describes the way data is read from zero or more -** database tables. For each table read, the data required may be -** constrained by equality or range constraints on one or more columns, -** and it may or may not be required in order sorted by one or more -** columns. For example, the following query: -** -**
-**     SELECT * FROM t1 WHERE t1.a = ? ORDER BY t1.b;
-** 
-** -** reads data from table t1. It requires only those rows for which t1.a -** is set to a specific value, and requires them sorted in order of -** column t1.b. Internally, SQLite determines this and attempts to locate -** an index that can be used to efficiently find the required subset of -** rows and/or allows the rows to be read from the database in the -** required order. In this case, ideally an index of the form: -** -**
-**     CREATE INDEX i1 ON t1(a, b);
-** 
-** -** The data passed to the whereinfo hook during query preparation do -** not describe the actual query plan to the application. Instead, it -** describes the parts of the query that SQLite could use an index to -** optimize if a suitable index existed. In this case, that only -** rows with t1.a=? are required, and that they are required sorted -** in order by t1.b. -** -** Each time the whereinfo hook is invoked, the first argument is a -** copy of the (void*) pointer passed as the second argument to this -** API function. The second is always one of the SQLITE_WHEREINFO_XXX -** constants defined below. -** -** For each table read by a query, the whereinfo hook is invoked as follows: -** -**
    -**
  • Once with SQLITE_WHEREINFO_TABLE as the second argument. This -** indicates the table that subsequent callbacks (until the next -** SQLITE_WHEREINFO_TABLE) apply to. -** -**
  • If SQLite requires rows in a specific order, once with -** SQLITE_WHEREINFO_ORDERBY for each column of the table that is -** one of the sort keys. -** -**
  • If there are any " = ?" constraints that restrict the rows -** required by SQLite, once with SQLITE_WHEREINFO_EQUALS for each -** such constraint. -** -**
  • If there are any " > ?" constraints (or any other range -** constraint) that restrict the rows required by SQLite, once with -** SQLITE_WHEREINFO_RANGE for each such constraint. -**
-** -** The third, fourth and fifth arguments passed to the whereinfo callback -** are interpreted differently, depending on the SQLITE_WHEREINFO_XXX value -** as follows: -** -**
-**
SQLITE_WHEREINFO_TABLE -**
The third argument passed in this case is the name of the table. -** The fourth is the index of the database in which the table is -** located (0 for "main", 1 for "temp" or higher for an attached -** database). The fifth argument is a bitmask that indicates which -** of the tables columns may be required by the query. If the leftmost -** column of the table is used in some way, bit 0 of the bitmask is -** set. If the next-to-leftmost is used, bit 1 etc. Bit 63 is used to -** represent all columns with an index of 63 or higher. If bit 63 -** is set, the application should assume that the query requires all -** columns from the table with an index of 63 or greater. -** -**
SQLITE_WHEREINFO_ORDERBY -**
The third argument passed in this case is the name of the collation -** sequence to sort by. The fourth is the index of the table column to -** sort by (0 for the leftmost column, 1 for the next-to-leftmost -** etc.). The fifth argument is a boolean flag - true for a DESC sort -** or false for ASC. -** -**
SQLITE_WHEREINFO_EQUALS -**
The third argument passed in this case is the name of the collation -** sequence used by the constraint. The fourth is the index of the -** table column in the constraint. If the current table is not part -** of a join, then the value passed as the fifth argument is always -** zero. Or, if it is part of a join, then the fifth parameter passed -** to this callback is a mask of other tables that the current -** constraint depends on. For example, in the query: -** -**
-**      SELECT * FROM t1, t2 WHERE t1.a = (t2.b+1);
-**   
-** -** the fifth parameter passed to the the SQLITE_WHEREINFO_EQUALS -** whereinfo callback would have the bit assigned to table "t2" -** set to true. There is no way for the application to determine -** the specific bit in the mask assigned to any table, but the bit -** assignments are consistent while parsing a single query. -** -**
SQLITE_WHEREINFO_RANGE -**
As for SQLITE_WHEREINFO_EQUALS. -**
-** -** Note that if a WHERE clause includes an OR expression, then there may be -** more than one set of callbacks for a single table. For example, the -** following SQL: -** -**
-**    SELECT * FROM t1 WHERE t1.a=? OR t1.b=?
-** 
-** -** Provokes the same callbacks as the following two queries executed in -** series. -** -**
-**    SELECT * FROM t1 WHERE t1.a=?;
-**    SELECT * FROM t1 WHERE t1.b=?;
-** 
-*/ -SQLITE_EXPERIMENTAL void *sqlite3_whereinfo_hook( - sqlite3 *db, /* Register callback with this db handle */ - void (*xWhereInfo)( - void*, /* Copy of pCtx */ - int, /* SQLITE_WHEREINFO_XXX constant */ - const char*, - int, - sqlite3_uint64 - ), - void *pCtx /* User pointer passed to callback */ -); - -#define SQLITE_WHEREINFO_TABLE 1 -#define SQLITE_WHEREINFO_EQUALS 2 -#define SQLITE_WHEREINFO_RANGE 3 -#define SQLITE_WHEREINFO_ORDERBY 4 - /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 0c5d4b74d7..22892c967f 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1399,6 +1399,7 @@ struct sqlite3 { VtabCtx *pVtabCtx; /* Context for active vtab connect/create */ VTable **aVTrans; /* Virtual tables with open transactions */ VTable *pDisconnect; /* Disconnect these in next sqlite3_prepare() */ + void *pVtabWC; /* For sqlite3_vtab_collation() */ #endif Hash aFunc; /* Hash table of connection functions */ Hash aCollSeq; /* All collating sequences */ @@ -1431,10 +1432,6 @@ struct sqlite3 { #ifdef SQLITE_USER_AUTHENTICATION sqlite3_userauth auth; /* User authentication information */ #endif -#ifdef SQLITE_ENABLE_WHEREINFO_HOOK - void (*xWhereInfo)(void*, int, const char*, int, u64); - void *pWhereInfoCtx; -#endif }; /* diff --git a/src/where.c b/src/where.c index 3cbd959771..df7582832a 100644 --- a/src/where.c +++ b/src/where.c @@ -885,7 +885,8 @@ static sqlite3_index_info *allocateIndexInfo( */ pIdxInfo = sqlite3DbMallocZero(pParse->db, sizeof(*pIdxInfo) + (sizeof(*pIdxCons) + sizeof(*pUsage))*nTerm - + sizeof(*pIdxOrderBy)*nOrderBy ); + + sizeof(*pIdxOrderBy)*nOrderBy + ); if( pIdxInfo==0 ){ sqlite3ErrorMsg(pParse, "out of memory"); return 0; @@ -3116,6 +3117,34 @@ static int whereLoopAddVirtualOne( } +struct BestIndexCtx { + WhereClause *pWC; + sqlite3_index_info *pIdxInfo; + ExprList *pOrderBy; + Parse *pParse; +}; + +const char *sqlite3_vtab_collation(sqlite3 *db, int iCons){ + struct BestIndexCtx *p = (struct BestIndexCtx*)db->pVtabWC; + const char *zRet = 0; + if( p && iCons>=0 ){ + if( iConspIdxInfo->nConstraint ){ + int iTerm = p->pIdxInfo->aConstraint[iCons].iTermOffset; + Expr *pX = p->pWC->a[iTerm].pExpr; + CollSeq *pC = sqlite3BinaryCompareCollSeq(p->pParse,pX->pLeft,pX->pRight); + zRet = (pC ? pC->zName : "BINARY"); + }else{ + iCons -= p->pIdxInfo->nConstraint; + if( iConspIdxInfo->nOrderBy ){ + Expr *pX = p->pOrderBy->a[iCons].pExpr; + CollSeq *pC = sqlite3ExprCollSeq(p->pParse, pX); + zRet = (pC ? pC->zName : "BINARY"); + } + } + } + return zRet; +} + /* ** Add all WhereLoop objects for a table of the join identified by ** pBuilder->pNew->iTab. That table is guaranteed to be a virtual table. @@ -3157,6 +3186,8 @@ static int whereLoopAddVirtual( WhereLoop *pNew; Bitmask mBest; /* Tables used by best possible plan */ u16 mNoOmit; + struct BestIndexCtx bic; + void *pSaved; assert( (mPrereq & mUnusable)==0 ); pWInfo = pBuilder->pWInfo; @@ -3178,6 +3209,13 @@ static int whereLoopAddVirtual( return SQLITE_NOMEM_BKPT; } + bic.pWC = pWC; + bic.pIdxInfo = p; + bic.pParse = pParse; + bic.pOrderBy = pBuilder->pOrderBy; + pSaved = pParse->db->pVtabWC; + pParse->db->pVtabWC = (void*)&bic; + /* First call xBestIndex() with all constraints usable. */ WHERETRACE(0x40, (" VirtualOne: all usable\n")); rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn); @@ -3254,6 +3292,7 @@ static int whereLoopAddVirtual( if( p->needToFreeIdxStr ) sqlite3_free(p->idxStr); sqlite3DbFreeNN(pParse->db, p); + pParse->db->pVtabWC = pSaved; return rc; } #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -3277,7 +3316,7 @@ static int whereLoopAddOr( WhereLoopBuilder sSubBuild; WhereOrSet sSum, sCur; struct SrcList_item *pItem; - + pWC = pBuilder->pWC; pWCEnd = pWC->a + pWC->nTerm; pNew = pBuilder->pNew; @@ -3294,7 +3333,7 @@ static int whereLoopAddOr( WhereTerm *pOrTerm; int once = 1; int i, j; - + sSubBuild = *pBuilder; sSubBuild.pOrderBy = 0; sSubBuild.pOrSet = &sCur; @@ -4278,201 +4317,6 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){ return 0; } -#ifdef SQLITE_ENABLE_WHEREINFO_HOOK - -static void whereTraceWC( - Parse *pParse, - struct SrcList_item *pItem, - WhereClause *pWC -){ - sqlite3 *db = pParse->db; - Table *pTab = pItem->pTab; - void (*x)(void*, int, const char*, int, u64) = db->xWhereInfo; - void *pCtx = db->pWhereInfoCtx; - int ii; - - /* Issue callbacks for WO_SINGLE constraints */ - for(ii=0; iinCol; ii++){ - int opMask = WO_SINGLE; - WhereScan scan; - WhereTerm *pTerm; - for(pTerm=whereScanInit(&scan, pWC, pItem->iCursor, ii, opMask, 0); - pTerm; - pTerm=whereScanNext(&scan) - ){ - int eOp; - Expr *pX = pTerm->pExpr; - CollSeq *pC = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight); - if( pTerm->eOperator & (WO_IS|WO_EQ|WO_IN) ){ - eOp = SQLITE_WHEREINFO_EQUALS; - }else{ - eOp = SQLITE_WHEREINFO_RANGE; - } - x(pCtx, eOp, (pC ? pC->zName : "BINARY"), ii, pTerm->prereqRight); - } - } -} - -/* -** If there are any OR terms in WHERE clause pWC, make the associated -** where-info hook callbacks. -*/ -static void whereTraceOR( - Parse *pParse, - struct SrcList_item *pItem, - WhereClause *pWC -){ - sqlite3 *db = pParse->db; - WhereClause tempWC; - struct TermAndIdx { - WhereTerm *pTerm; - int iIdx; - } aOr[4]; - int nOr = 0; - Table *pTab = pItem->pTab; - int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); - int ii; - - memset(aOr, 0, sizeof(aOr)); - - /* Iterate through OR nodes */ - for(ii=0; iinTerm; ii++){ - WhereTerm *pTerm = &pWC->a[ii]; - if( pTerm->eOperator & WO_OR ){ - /* Check that each branch of this OR term contains at least - ** one reference to the table currently being processed. If that - ** is not the case, this term can be ignored. */ - WhereClause * const pOrWC = &pTerm->u.pOrInfo->wc; - WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm]; - WhereTerm *pOrTerm; - WhereClause *pTermWC; - WhereScan scan; - - for(pOrTerm=pOrWC->a; pOrTermeOperator & WO_AND)!=0 ){ - pTermWC = &pOrTerm->u.pAndInfo->wc; - }else{ - tempWC.pWInfo = pWC->pWInfo; - tempWC.pOuter = pWC; - tempWC.op = TK_AND; - tempWC.nTerm = 1; - tempWC.a = pOrTerm; - pTermWC = &tempWC; - } - - for(iCol=0; iColnCol; iCol++){ - int iCsr = pItem->iCursor; - if( !whereScanInit(&scan, pTermWC, iCsr, iCol, WO_SINGLE, 0) ){ - break; - } - } - if( iCol==pTab->nCol ) break; - } - - if( pOrTerm==pOrWCEnd ){ - aOr[nOr].pTerm = pTerm; - aOr[nOr].iIdx = pOrWC->nTerm; - nOr++; - if( nOr==ArraySize(aOr) ) break; - } - } - } - - while( 1 ){ - for(ii=0; iiu.pOrInfo->wc.nTerm; - }else{ - aOr[ii].iIdx--; - break; - } - } - if( ii==nOr ) break; - - /* Table name callback */ - db->xWhereInfo(db->pWhereInfoCtx, - SQLITE_WHEREINFO_TABLE, pTab->zName, iDb, pItem->colUsed - ); - /* whereTraceWC(pParse, pItem, pWC); */ - for(ii=0; iiu.pOrInfo->wc; - if( aOr[ii].iIdxnTerm ){ - WhereClause *pTermWC; - WhereTerm *pOrTerm = &pOrWC->a[aOr[ii].iIdx]; - if( (pOrTerm->eOperator & WO_AND)!=0 ){ - pTermWC = &pOrTerm->u.pAndInfo->wc; - }else{ - tempWC.pWInfo = pWC->pWInfo; - tempWC.pOuter = pWC; - tempWC.op = TK_AND; - tempWC.nTerm = 1; - tempWC.a = pOrTerm; - pTermWC = &tempWC; - } - whereTraceWC(pParse, pItem, pTermWC); - } - } - } -} - -/* -** If there is a where-info hook attached to the database handle, issue all -** required callbacks for the current sqlite3WhereBegin() call. -*/ -static void whereTraceBuilder( - Parse *pParse, - WhereLoopBuilder *p -){ - sqlite3 *db = pParse->db; - if( db->xWhereInfo && db->init.busy==0 ){ - void (*x)(void*, int, const char*, int, u64) = db->xWhereInfo; - void *pCtx = db->pWhereInfoCtx; - int ii; - SrcList *pTabList = p->pWInfo->pTabList; - - /* Loop through each element of the FROM clause. Ignore any sub-selects - ** or views. Invoke the xWhereInfo() callback multiple times for each - ** real table. */ - for(ii=0; iinSrc; ii++){ - struct SrcList_item *pItem = &pTabList->a[ii]; - if( pItem->pSelect==0 ){ - Table *pTab = pItem->pTab; - int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); - - /* Table name callback */ - x(pCtx, SQLITE_WHEREINFO_TABLE, pTab->zName, iDb, pItem->colUsed); - - /* ORDER BY callbacks */ - if( p->pOrderBy ){ - int i; - for(i=0; ipOrderBy->nExpr; i++){ - Expr *pExpr = p->pOrderBy->a[i].pExpr; - CollSeq *pColl = sqlite3ExprCollSeq(pParse, pExpr); - pExpr = sqlite3ExprSkipCollate(pExpr); - if( pExpr->op==TK_COLUMN && pExpr->iTable==pItem->iCursor ){ - int iCol = pExpr->iColumn; - if( pColl && iCol>=0 ){ - int bDesc = p->pOrderBy->a[i].sortOrder; - x(pCtx, SQLITE_WHEREINFO_ORDERBY, pColl->zName, iCol, bDesc); - } - } - } - } - - /* WHERE callbacks */ - whereTraceWC(pParse, pItem, p->pWC); - - /* OR-clause processing */ - whereTraceOR(pParse, pItem, p->pWC); - } - } - } -} -#else -# define whereTraceBuilder(x,y) -#endif - /* ** Generate the beginning of the loop used for WHERE clause processing. ** The return value is a pointer to an opaque structure that contains @@ -4745,9 +4589,6 @@ WhereInfo *sqlite3WhereBegin( } #endif - /* Invoke the where-info hook, if one has been registered. */ - whereTraceBuilder(pParse, &sWLB); - if( nTabList!=1 || whereShortCut(&sWLB)==0 ){ rc = whereLoopAddAll(&sWLB); if( rc ) goto whereBeginError;