mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-01 06:27:03 +03:00
Modify the code in ext/expert/ to use the vtab interface instead of
sqlite3_whereinfo_hook(). Remove sqlite3_whereinfo_hook(). FossilOrigin-Name: 3bb6585004090dbf92dd5e9abdf0fd2c921e64b5b3121c4fb7446db764ab59e5
This commit is contained in:
@ -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.
|
||||
#
|
||||
|
@ -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,79 +319,189 @@ 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;
|
||||
typedef struct ExpertVtab ExpertVtab;
|
||||
struct ExpertVtab {
|
||||
sqlite3_vtab base;
|
||||
IdxTable *pTab;
|
||||
sqlite3expert *pExpert;
|
||||
};
|
||||
|
||||
#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
|
||||
static char *expertDequote(const char *zIn){
|
||||
int n = strlen(zIn);
|
||||
char *zRet = sqlite3_malloc(n);
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
case SQLITE_WHEREINFO_EQUALS:
|
||||
case SQLITE_WHEREINFO_RANGE: {
|
||||
IdxConstraint *pNew = idxNewConstraint(&p->rc, zVal);
|
||||
if( pNew==0 ) return;
|
||||
pNew->iCol = iVal;
|
||||
pNew->depmask = mask;
|
||||
|
||||
if( eOp==SQLITE_WHEREINFO_RANGE ){
|
||||
pNew->pNext = p->pScan->pRange;
|
||||
p->pScan->pRange = pNew;
|
||||
}else{
|
||||
pNew->pNext = p->pScan->pEq;
|
||||
p->pScan->pEq = 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;
|
||||
}
|
||||
}
|
||||
|
||||
*ppVtab = (sqlite3_vtab*)p;
|
||||
return rc;
|
||||
}
|
||||
|
||||
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; i<pIdxInfo->nConstraint; 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;
|
||||
}
|
||||
}
|
||||
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
|
||||
** the error message to callback function xOut.
|
||||
@ -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; i<pTab->nCol; 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). */
|
||||
/* 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){
|
||||
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) */
|
||||
|
22
manifest
22
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
|
||||
|
@ -1 +1 @@
|
||||
5fcd840cf9b6a5c3ee4ef1e8f92f6c30f96a7899a3d774ee9be8a816916f2c3b
|
||||
3bb6585004090dbf92dd5e9abdf0fd2c921e64b5b3121c4fb7446db764ab59e5
|
20
src/main.c
20
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().
|
||||
|
144
src/sqlite.h.in
144
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:
|
||||
**
|
||||
** <pre>
|
||||
** SELECT * FROM t1 WHERE t1.a = ? ORDER BY t1.b;
|
||||
** </pre>
|
||||
**
|
||||
** 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:
|
||||
**
|
||||
** <pre>
|
||||
** CREATE INDEX i1 ON t1(a, b);
|
||||
** </pre>
|
||||
**
|
||||
** 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:
|
||||
**
|
||||
** <ul>
|
||||
** <li> Once with SQLITE_WHEREINFO_TABLE as the second argument. This
|
||||
** indicates the table that subsequent callbacks (until the next
|
||||
** SQLITE_WHEREINFO_TABLE) apply to.
|
||||
**
|
||||
** <li> 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.
|
||||
**
|
||||
** <li> If there are any "<col> = ?" constraints that restrict the rows
|
||||
** required by SQLite, once with SQLITE_WHEREINFO_EQUALS for each
|
||||
** such constraint.
|
||||
**
|
||||
** <li> If there are any "<col> > ?" constraints (or any other range
|
||||
** constraint) that restrict the rows required by SQLite, once with
|
||||
** SQLITE_WHEREINFO_RANGE for each such constraint.
|
||||
** </ul>
|
||||
**
|
||||
** The third, fourth and fifth arguments passed to the whereinfo callback
|
||||
** are interpreted differently, depending on the SQLITE_WHEREINFO_XXX value
|
||||
** as follows:
|
||||
**
|
||||
** <dl>
|
||||
** <dt> SQLITE_WHEREINFO_TABLE
|
||||
** <dd> 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.
|
||||
**
|
||||
** <dt> SQLITE_WHEREINFO_ORDERBY
|
||||
** <dd> 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.
|
||||
**
|
||||
** <dt> SQLITE_WHEREINFO_EQUALS
|
||||
** <dd> 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:
|
||||
**
|
||||
** <pre>
|
||||
** SELECT * FROM t1, t2 WHERE t1.a = (t2.b+1);
|
||||
** </pre>
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
** <dt> SQLITE_WHEREINFO_RANGE
|
||||
** <dd> As for SQLITE_WHEREINFO_EQUALS.
|
||||
** </dl>
|
||||
**
|
||||
** 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:
|
||||
**
|
||||
** <pre>
|
||||
** SELECT * FROM t1 WHERE t1.a=? OR t1.b=?
|
||||
** </pre>
|
||||
**
|
||||
** Provokes the same callbacks as the following two queries executed in
|
||||
** series.
|
||||
**
|
||||
** <pre>
|
||||
** SELECT * FROM t1 WHERE t1.a=?;
|
||||
** SELECT * FROM t1 WHERE t1.b=?;
|
||||
** </pre>
|
||||
*/
|
||||
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.
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
/*
|
||||
|
239
src/where.c
239
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( iCons<p->pIdxInfo->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( iCons<p->pIdxInfo->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 */
|
||||
@ -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; ii<pTab->nCol; 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; ii<pWC->nTerm; 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; pOrTerm<pOrWCEnd; pOrTerm++){
|
||||
int iCol;
|
||||
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;
|
||||
}
|
||||
|
||||
for(iCol=0; iCol<pTab->nCol; 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; ii<nOr; ii++){
|
||||
if( aOr[ii].iIdx==0 ){
|
||||
aOr[ii].iIdx = aOr[ii].pTerm->u.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; ii<nOr; ii++){
|
||||
WhereClause * const pOrWC = &aOr[ii].pTerm->u.pOrInfo->wc;
|
||||
if( aOr[ii].iIdx<pOrWC->nTerm ){
|
||||
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; ii<pTabList->nSrc; 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; i<p->pOrderBy->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;
|
||||
|
Reference in New Issue
Block a user