1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-06 15:49:35 +03:00

Begin inserting some experimental code for the next generation query planner.

FossilOrigin-Name: ccaf4c3f7e1ec45e058d594d9b5c26818a37722a
This commit is contained in:
drh
2013-05-02 00:15:01 +00:00
parent a309552e2c
commit f1b5f5b855
4 changed files with 249 additions and 44 deletions

View File

@@ -1,5 +1,5 @@
C Do\snot\suse\sa\stransitive\sconstraint\sto\san\sIN\soperator\swhere\sthe\sRHS\sis\sa\nconstant\sif\sthere\sexists\sa\sdirect\s==\soperator\sto\sanother\stable\sin\san\souter\nloop. C Begin\sinserting\ssome\sexperimental\scode\sfor\sthe\snext\sgeneration\squery\splanner.
D 2013-05-01T17:58:35.871 D 2013-05-02T00:15:01.231
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in ce81671efd6223d19d4c8c6b88ac2c4134427111 F Makefile.in ce81671efd6223d19d4c8c6b88ac2c4134427111
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -194,7 +194,7 @@ F src/shell.c 5d527e5d08f05ec2c43ff194ea44bf62b974f4c9
F src/sqlite.h.in 5a5a22a9b192d81a9e5dee00274e3a0484c4afb1 F src/sqlite.h.in 5a5a22a9b192d81a9e5dee00274e3a0484c4afb1
F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0 F src/sqlite3.rc fea433eb0a59f4c9393c8e6d76a6e2596b1fe0c0
F src/sqlite3ext.h d936f797812c28b81b26ed18345baf8db28a21a5 F src/sqlite3ext.h d936f797812c28b81b26ed18345baf8db28a21a5
F src/sqliteInt.h de835c584032769461c123a564381f9808542c0e F src/sqliteInt.h 0d76a0aa7c64536c6f55d11a8f9f40df0636af6a
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
F src/status.c bedc37ec1a6bb9399944024d63f4c769971955a9 F src/status.c bedc37ec1a6bb9399944024d63f4c769971955a9
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
@@ -263,7 +263,7 @@ F src/vtab.c b05e5f1f4902461ba9f5fc49bb7eb7c3a0741a83
F src/wal.c 436bfceb141b9423c45119e68e444358ee0ed35d F src/wal.c 436bfceb141b9423c45119e68e444358ee0ed35d
F src/wal.h a4d3da523d55a226a0b28e9058ef88d0a8051887 F src/wal.h a4d3da523d55a226a0b28e9058ef88d0a8051887
F src/walker.c 4fa43583d0a84b48f93b1e88f11adf2065be4e73 F src/walker.c 4fa43583d0a84b48f93b1e88f11adf2065be4e73
F src/where.c 12d4200eb6ae991cad02367c391db076ac1af1b0 F src/where.c fc62bea654f27d5787bc428cd8f3f176d764f785
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
F test/aggnested.test 45c0201e28045ad38a530b5a144b73cd4aa2cfd6 F test/aggnested.test 45c0201e28045ad38a530b5a144b73cd4aa2cfd6
@@ -1060,7 +1060,10 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac
P 329478cbed06f93652de50abdb31a6b41af02b9e P faedaeace9c7ed9a8aaf96700caee09db0c0c061
R cc457983507eee39e1b071292dee5b26 R 84f8c9b09f28282800936ed72686b19f
T *branch * nextgen-query-plan-exp
T *sym-nextgen-query-plan-exp *
T -sym-trunk *
U drh U drh
Z 944723cfd5dfa8d16765eaab2fd82886 Z 18faca4b1f68b4b3514e189f395d77ff

View File

@@ -1 +1 @@
faedaeace9c7ed9a8aaf96700caee09db0c0c061 ccaf4c3f7e1ec45e058d594d9b5c26818a37722a

View File

@@ -2065,6 +2065,7 @@ struct WhereInfo {
int iBreak; /* Jump here to break out of the loop */ int iBreak; /* Jump here to break out of the loop */
int nLevel; /* Number of nested loop */ int nLevel; /* Number of nested loop */
struct WhereClause *pWC; /* Decomposition of the WHERE clause */ struct WhereClause *pWC; /* Decomposition of the WHERE clause */
struct WhereLoop *pLoops; /* List of all WhereLoop objects */
double savedNQueryLoop; /* pParse->nQueryLoop outside the WHERE loop */ double savedNQueryLoop; /* pParse->nQueryLoop outside the WHERE loop */
double nRowOut; /* Estimated number of output rows */ double nRowOut; /* Estimated number of output rows */
WhereLevel a[1]; /* Information about each nest loop in WHERE */ WhereLevel a[1]; /* Information about each nest loop in WHERE */

View File

@@ -39,6 +39,43 @@ typedef struct WhereMaskSet WhereMaskSet;
typedef struct WhereOrInfo WhereOrInfo; typedef struct WhereOrInfo WhereOrInfo;
typedef struct WhereAndInfo WhereAndInfo; typedef struct WhereAndInfo WhereAndInfo;
typedef struct WhereCost WhereCost; typedef struct WhereCost WhereCost;
typedef struct WhereLoop WhereLoop;
typedef struct WherePath WherePath;
typedef struct WhereTerm WhereTerm;
/*
** Each instance of this object represents a way of evaluating one
** term of a join. The WhereClause object holds a table of these
** objects using (iTab,prereq,iOb,nOb) as the primary key. Note that the
** same join term might have multiple associated WhereLoop objects.
*/
struct WhereLoop {
Bitmask prereq; /* Bitmask of other loops that must run first */
int iTab; /* Index of the table coded by this loop */
u16 iOb, nOb; /* ORDER BY terms satisfied by this strategy */
double rSetup; /* One-time setup cost (ex: create transient index) */
double rRun; /* Cost of running each loop */
double nOut; /* Estimated number of output rows */
u32 wsFlags; /* WHERE_* flags describing the plan */
u16 nEq; /* Number of equality constraints */
u16 nTerm; /* Number of entries in aTerm[] */
Index *pIndex; /* Index used */
WhereTerm *aTerm; /* WhereTerms used */
WhereLoop *pNextLoop; /* Next WhereLoop object in the WhereClause */
};
/*
** Each instance of this object holds a sequence of WhereLoop objects
** that implement some or all of the entire query plan.
*/
struct WherePath {
Bitmask maskLoop; /* Bitmask of all WhereLoop objects in this path */
double nRow; /* Estimated number of rows generated by this path */
double rCost; /* Total cost of this path */
WhereLoop *aLoop[1]; /* Array of WhereLoop objects implementing this path */
WherePath *pNextPath; /* Next path in order of increasing cost */
WherePath *pPrevPath; /* Previous path in cost order */
};
/* /*
** The query generator uses an array of instances of this structure to ** The query generator uses an array of instances of this structure to
@@ -91,7 +128,6 @@ typedef struct WhereCost WhereCost;
** in prereqRight and prereqAll. The default is 64 bits, hence SQLite ** in prereqRight and prereqAll. The default is 64 bits, hence SQLite
** is only able to process joins with 64 or fewer tables. ** is only able to process joins with 64 or fewer tables.
*/ */
typedef struct WhereTerm WhereTerm;
struct WhereTerm { struct WhereTerm {
Expr *pExpr; /* Pointer to the subexpression that is this term */ Expr *pExpr; /* Pointer to the subexpression that is this term */
int iParent; /* Disable pWC->a[iParent] when this term disabled */ int iParent; /* Disable pWC->a[iParent] when this term disabled */
@@ -1793,9 +1829,9 @@ static void bestOrClauseIndex(WhereBestIdx *p){
sBOI.pDistinct = 0; sBOI.pDistinct = 0;
sBOI.ppIdxInfo = 0; sBOI.ppIdxInfo = 0;
for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){ for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){
WHERETRACE(("... Multi-index OR testing for term %d of %d....\n", /*WHERETRACE(("... Multi-index OR testing for term %d of %d....\n",
(pOrTerm - pOrWC->a), (pTerm - pWC->a) (pOrTerm - pOrWC->a), (pTerm - pWC->a)
)); ));*/
if( (pOrTerm->eOperator& WO_AND)!=0 ){ if( (pOrTerm->eOperator& WO_AND)!=0 ){
sBOI.pWC = &pOrTerm->u.pAndInfo->wc; sBOI.pWC = &pOrTerm->u.pAndInfo->wc;
bestIndex(&sBOI); bestIndex(&sBOI);
@@ -1822,15 +1858,15 @@ static void bestOrClauseIndex(WhereBestIdx *p){
/* If there is an ORDER BY clause, increase the scan cost to account /* If there is an ORDER BY clause, increase the scan cost to account
** for the cost of the sort. */ ** for the cost of the sort. */
if( p->pOrderBy!=0 ){ if( p->pOrderBy!=0 ){
WHERETRACE(("... sorting increases OR cost %.9g to %.9g\n", /*WHERETRACE(("... sorting increases OR cost %.9g to %.9g\n",
rTotal, rTotal+nRow*estLog(nRow))); rTotal, rTotal+nRow*estLog(nRow)));*/
rTotal += nRow*estLog(nRow); rTotal += nRow*estLog(nRow);
} }
/* If the cost of scanning using this OR term for optimization is /* If the cost of scanning using this OR term for optimization is
** less than the current cost stored in pCost, replace the contents ** less than the current cost stored in pCost, replace the contents
** of pCost. */ ** of pCost. */
WHERETRACE(("... multi-index OR cost=%.9g nrow=%.9g\n", rTotal, nRow)); /*WHERETRACE(("... multi-index OR cost=%.9g nrow=%.9g\n", rTotal, nRow));*/
if( rTotal<p->cost.rCost ){ if( rTotal<p->cost.rCost ){
p->cost.rCost = rTotal; p->cost.rCost = rTotal;
p->cost.used = used; p->cost.used = used;
@@ -1927,8 +1963,8 @@ static void bestAutomaticIndex(WhereBestIdx *p){
pWCEnd = &pWC->a[pWC->nTerm]; pWCEnd = &pWC->a[pWC->nTerm];
for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){ for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
if( termCanDriveIndex(pTerm, pSrc, p->notReady) ){ if( termCanDriveIndex(pTerm, pSrc, p->notReady) ){
WHERETRACE(("auto-index reduces cost from %.1f to %.1f\n", /*WHERETRACE(("auto-index reduces cost from %.1f to %.1f\n",
p->cost.rCost, costTempIdx)); p->cost.rCost, costTempIdx));*/
p->cost.rCost = costTempIdx; p->cost.rCost = costTempIdx;
p->cost.plan.nRow = logN + 1; p->cost.plan.nRow = logN + 1;
p->cost.plan.wsFlags = WHERE_TEMP_INDEX; p->cost.plan.wsFlags = WHERE_TEMP_INDEX;
@@ -2113,7 +2149,7 @@ static sqlite3_index_info *allocateIndexInfo(WhereBestIdx *p){
int nOrderBy; int nOrderBy;
sqlite3_index_info *pIdxInfo; sqlite3_index_info *pIdxInfo;
WHERETRACE(("Recomputing index info for %s...\n", pSrc->pTab->zName)); /*WHERETRACE(("Recomputing index info for %s...\n", pSrc->pTab->zName));*/
/* Count the number of possible WHERE clause constraints referring /* Count the number of possible WHERE clause constraints referring
** to this virtual table */ ** to this virtual table */
@@ -2222,7 +2258,7 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
int i; int i;
int rc; int rc;
WHERETRACE(("xBestIndex for %s\n", pTab->zName)); /*WHERETRACE(("xBestIndex for %s\n", pTab->zName));*/
TRACE_IDX_INPUTS(p); TRACE_IDX_INPUTS(p);
rc = pVtab->pModule->xBestIndex(pVtab, p); rc = pVtab->pModule->xBestIndex(pVtab, p);
TRACE_IDX_OUTPUTS(p); TRACE_IDX_OUTPUTS(p);
@@ -2733,8 +2769,8 @@ static int whereRangeScanEst(
}else{ }else{
*pRangeDiv = (double)p->aiRowEst[0]/(double)(iUpper - iLower); *pRangeDiv = (double)p->aiRowEst[0]/(double)(iUpper - iLower);
} }
WHERETRACE(("range scan regions: %u..%u div=%g\n", /*WHERETRACE(("range scan regions: %u..%u div=%g\n",
(u32)iLower, (u32)iUpper, *pRangeDiv)); (u32)iLower, (u32)iUpper, *pRangeDiv));*/
return SQLITE_OK; return SQLITE_OK;
} }
} }
@@ -2791,7 +2827,7 @@ static int whereEqualScanEst(
if( pRhs==0 ) return SQLITE_NOTFOUND; if( pRhs==0 ) return SQLITE_NOTFOUND;
rc = whereKeyStats(pParse, p, pRhs, 0, a); rc = whereKeyStats(pParse, p, pRhs, 0, a);
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
WHERETRACE(("equality scan regions: %d\n", (int)a[1])); /*WHERETRACE(("equality scan regions: %d\n", (int)a[1]));*/
*pnRow = a[1]; *pnRow = a[1];
} }
whereEqualScanEst_cancel: whereEqualScanEst_cancel:
@@ -2837,7 +2873,7 @@ static int whereInScanEst(
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
if( nRowEst > p->aiRowEst[0] ) nRowEst = p->aiRowEst[0]; if( nRowEst > p->aiRowEst[0] ) nRowEst = p->aiRowEst[0];
*pnRow = nRowEst; *pnRow = nRowEst;
WHERETRACE(("IN row estimate: est=%g\n", nRowEst)); /*WHERETRACE(("IN row estimate: est=%g\n", nRowEst));*/
} }
return rc; return rc;
} }
@@ -3049,10 +3085,10 @@ static int isSortingIndex(
}else{ }else{
Expr *pRight = pConstraint->pExpr->pRight; Expr *pRight = pConstraint->pExpr->pRight;
if( pRight->op==TK_COLUMN ){ if( pRight->op==TK_COLUMN ){
WHERETRACE((" .. isOrderedColumn(tab=%d,col=%d)", /*WHERETRACE((" .. isOrderedColumn(tab=%d,col=%d)",
pRight->iTable, pRight->iColumn)); pRight->iTable, pRight->iColumn));*/
isEq = isOrderedColumn(p, pRight->iTable, pRight->iColumn); isEq = isOrderedColumn(p, pRight->iTable, pRight->iColumn);
WHERETRACE((" -> isEq=%d\n", isEq)); /*WHERETRACE((" -> isEq=%d\n", isEq));*/
/* If the constraint is of the form X=Y where Y is an ordered value /* If the constraint is of the form X=Y where Y is an ordered value
** in an outer loop, then make sure the sort order of Y matches the ** in an outer loop, then make sure the sort order of Y matches the
@@ -3319,10 +3355,10 @@ static void bestBtreeIndex(WhereBestIdx *p){
WhereTerm *pFirstTerm = 0; /* First term matching the index */ WhereTerm *pFirstTerm = 0; /* First term matching the index */
#endif #endif
WHERETRACE(( /*WHERETRACE((
" %s(%s):\n", " %s(%s):\n",
pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk") pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk")
)); ));*/
memset(&pc, 0, sizeof(pc)); memset(&pc, 0, sizeof(pc));
pc.plan.nOBSat = nPriorSat; pc.plan.nOBSat = nPriorSat;
@@ -3403,10 +3439,10 @@ static void bestBtreeIndex(WhereBestIdx *p){
if( bSort && (pSrc->jointype & JT_LEFT)==0 ){ if( bSort && (pSrc->jointype & JT_LEFT)==0 ){
int bRev = 2; int bRev = 2;
int bObUnique = 0; int bObUnique = 0;
WHERETRACE((" --> before isSortIndex: nPriorSat=%d\n",nPriorSat)); /*WHERETRACE((" --> before isSortIndex: nPriorSat=%d\n",nPriorSat));*/
pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, &bRev, &bObUnique); pc.plan.nOBSat = isSortingIndex(p, pProbe, iCur, &bRev, &bObUnique);
WHERETRACE((" --> after isSortIndex: bRev=%d bObU=%d nOBSat=%d\n", /*WHERETRACE((" --> after isSortIndex: bRev=%d bObU=%d nOBSat=%d\n",
bRev, bObUnique, pc.plan.nOBSat)); bRev, bObUnique, pc.plan.nOBSat));*/
if( nPriorSat<pc.plan.nOBSat || (pc.plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){ if( nPriorSat<pc.plan.nOBSat || (pc.plan.wsFlags & WHERE_ALL_UNIQUE)!=0 ){
pc.plan.wsFlags |= WHERE_ORDERED; pc.plan.wsFlags |= WHERE_ORDERED;
if( bObUnique ) pc.plan.wsFlags |= WHERE_OB_UNIQUE; if( bObUnique ) pc.plan.wsFlags |= WHERE_OB_UNIQUE;
@@ -3634,14 +3670,14 @@ static void bestBtreeIndex(WhereBestIdx *p){
} }
WHERETRACE(( /*WHERETRACE((
" nEq=%d nInMul=%d rangeDiv=%d bSort=%d bLookup=%d wsFlags=0x%08x\n" " nEq=%d nInMul=%d rangeDiv=%d bSort=%d bLookup=%d wsFlags=0x%08x\n"
" notReady=0x%llx log10N=%.1f nRow=%.1f cost=%.1f\n" " notReady=0x%llx log10N=%.1f nRow=%.1f cost=%.1f\n"
" used=0x%llx nOBSat=%d\n", " used=0x%llx nOBSat=%d\n",
pc.plan.nEq, nInMul, (int)rangeDiv, bSort, bLookup, pc.plan.wsFlags, pc.plan.nEq, nInMul, (int)rangeDiv, bSort, bLookup, pc.plan.wsFlags,
p->notReady, log10N, pc.plan.nRow, pc.rCost, pc.used, p->notReady, log10N, pc.plan.nRow, pc.rCost, pc.used,
pc.plan.nOBSat pc.plan.nOBSat
)); ));*/
/* If this index is the best we have seen so far, then record this /* If this index is the best we have seen so far, then record this
** index and its cost in the p->cost structure. ** index and its cost in the p->cost structure.
@@ -3677,9 +3713,9 @@ static void bestBtreeIndex(WhereBestIdx *p){
|| p->cost.plan.u.pIdx==pSrc->pIndex || p->cost.plan.u.pIdx==pSrc->pIndex
); );
WHERETRACE((" best index is %s cost=%.1f\n", /*WHERETRACE((" best index is %s cost=%.1f\n",
p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk", p->cost.plan.u.pIdx ? p->cost.plan.u.pIdx->zName : "ipk",
p->cost.rCost)); p->cost.rCost));*/
bestOrClauseIndex(p); bestOrClauseIndex(p);
bestAutomaticIndex(p); bestAutomaticIndex(p);
@@ -4928,6 +4964,13 @@ static int nQPlan = 0; /* Next free slow in _query_plan[] */
#endif /* SQLITE_TEST */ #endif /* SQLITE_TEST */
/*
** Delete a WhereLoop object
*/
static void whereLoopDelete(sqlite3 *db, WhereLoop *p){
sqlite3DbFree(db, p->aTerm);
sqlite3DbFree(db, p);
}
/* /*
** Free a WhereInfo structure ** Free a WhereInfo structure
@@ -4953,10 +4996,140 @@ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){
} }
} }
whereClauseClear(pWInfo->pWC); whereClauseClear(pWInfo->pWC);
while( pWInfo->pLoops ){
WhereLoop *p = pWInfo->pLoops;
pWInfo->pLoops = p->pNextLoop;
whereLoopDelete(db, p);
}
sqlite3DbFree(db, pWInfo); sqlite3DbFree(db, pWInfo);
} }
} }
/*
** Insert or replace a WhereLoop entry using the template supplied.
**
** An existing WhereLoop entry might be overwritten if the new template
** is better and has fewer dependencies. Or the template will be ignored
** and no insert will occur if an existing WhereLoop is faster and has
** fewer dependencies than the template. Otherwise a new WhereLoop is
** added based no the template.
*/
static int whereLoopInsert(WhereInfo *pWInfo, WhereLoop *pTemplate){
WhereLoop **ppPrev, *p;
sqlite3 *db = pWInfo->pParse->db;
/* Search for an existing WhereLoop to overwrite, or which takes
** priority over pTemplate.
*/
for(ppPrev=&pWInfo->pLoops, p=*ppPrev; p; ppPrev=&p->pNextLoop, p=*ppPrev){
if( p->iTab!=pTemplate->iTab ) continue;
if( (p->prereq & pTemplate->prereq)==p->prereq
&& p->nOb>=pTemplate->nOb
&& p->iOb==pTemplate->iOb
&& p->rSetup<=pTemplate->rSetup
&& p->rRun<=pTemplate->rRun
){
/* Already holding an equal or better WhereLoop.
** Return without changing or adding anything */
return SQLITE_OK;
}
if( (p->prereq & pTemplate->prereq)==pTemplate->prereq
&& p->nOb<=pTemplate->nOb
&& p->iOb==pTemplate->iOb
&& p->rSetup>=pTemplate->rSetup
&& p->rRun>=pTemplate->rRun
){
/* Overwrite an existing WhereLoop with a better one */
sqlite3DbFree(db, p->aTerm);
*ppPrev = p->pNextLoop;
break;
}
}
/* If we reach this point it means that either p[] should be overwritten
** with pTemplate[] if p[] exists, or if p==NULL then allocate a new
** WhereLoop and insert it.
*/
if( p==0 ){
p = sqlite3DbMallocRaw(db, sizeof(WhereLoop));
if( p==0 ) return SQLITE_NOMEM;
}
*p = *pTemplate;
p->pNextLoop = pWInfo->pLoops;
pWInfo->pLoops = p;
if( pTemplate->nTerm<=0 ) return SQLITE_OK;
p->aTerm = sqlite3DbMallocRaw(db, pTemplate->nTerm*sizeof(p->aTerm[0]));
if( p->aTerm==0 ){
p->nTerm = 0;
return SQLITE_NOMEM;
}
memcpy(p->aTerm, pTemplate->aTerm, pTemplate->nTerm*sizeof(p->aTerm[0]));
return SQLITE_OK;
}
/*
** Add all WhereLoop objects for the iTab-th table of the join. That
** table is guaranteed to be a b-tree table, not a virtual table.
*/
static void whereLoopAddBtree(
WhereInfo *pWInfo, /* WHERE clause information */
int iTab, /* The table to process */
Bitmask mExtra /* Extra prerequesites for using this table */
){
WhereLoop *pNew; /* Template WhereLoop object */
sqlite3 *db = pWInfo->pParse->db;
pNew = sqlite3DbMallocZero(db, sizeof(*pNew));
if( pNew==0 ) return;
/* Insert a full table scan */
pNew->iTab = iTab;
pNew->rSetup = (double)0;
pNew->rRun = (double)1000000;
pNew->nOut = (double)1000000;
whereLoopInsert(pWInfo, pNew);
whereLoopDelete(db, pNew);
}
/*
** Add all WhereLoop objects for the iTab-th table of the join. That
** table is guaranteed to be a virtual table.
*/
static void whereLoopAddVirtual(
WhereInfo *pWInfo, /* WHERE clause information */
int iTab, /* The table to process */
Bitmask mExtra /* Extra prerequesites for using this table */
){
}
/*
** Add all WhereLoop objects for all tables
*/
static void whereLoopAddAll(WhereInfo *pWInfo){
Bitmask mExtra = 0;
Bitmask mPrior = 0;
int iTab;
SrcList *pTabList = pWInfo->pTabList;
struct SrcList_item *pItem;
WhereClause *pWC = pWInfo->pWC;
sqlite3 *db = pWInfo->pParse->db;
/* Loop over the tables in the join, from left to right */
for(iTab=0, pItem=pTabList->a; iTab<pTabList->nSrc; iTab++, pItem++){
if( IsVirtual(pItem->pTab) ){
whereLoopAddVirtual(pWInfo, iTab, mExtra);
}else{
whereLoopAddBtree(pWInfo, iTab, mExtra);
}
mPrior |= getMask(pWC->pMaskSet, pItem->iCursor);
if( (pItem->jointype & (JT_LEFT|JT_CROSS))!=0 ){
mExtra = mPrior;
}
if( db->mallocFailed ) break;
}
}
/* /*
** Generate the beginning of the loop used for WHERE clause processing. ** Generate the beginning of the loop used for WHERE clause processing.
@@ -5187,6 +5360,35 @@ WhereInfo *sqlite3WhereBegin(
pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE;
} }
/* Construct the WhereLoop objects */
WHERETRACE(("*** Optimizer Start ***\n"));
whereLoopAddAll(pWInfo);
/* Display all of the WhereLoop objects if wheretrace is enabled */
#if defined(SQLITE_DEBUG) \
&& (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHERETRACE))
if( sqlite3WhereTrace ){
WhereLoop *p;
int nb = 2*((nTabList+15)/16);
for(p=pWInfo->pLoops; p; p=p->pNextLoop){
struct SrcList_item *pItem = pTabList->a + p->iTab;
Table *pTab = pItem->pTab;
sqlite3DebugPrintf("%02d.%0*llx", p->iTab, nb, p->prereq);
sqlite3DebugPrintf(" %s",
pItem->zAlias ? pItem->zAlias : pTab->zName);
if( p->pIndex ){
sqlite3DebugPrintf(" index %s nEq %d", p->pIndex->zName, p->nEq);
}else{
sqlite3DebugPrintf("\n");
}
sqlite3DebugPrintf(" wsFlags %08x OB %d,%d nTerm %d\n",
p->wsFlags, p->iOb, p->nOb, p->nTerm);
sqlite3DebugPrintf(" cost %.2e + %.2e nOut %.2e\n",
p->prereq, p->rSetup, p->rRun, p->nOut);
}
}
#endif
/* Chose the best index to use for each table in the FROM clause. /* Chose the best index to use for each table in the FROM clause.
** **
** This loop fills in the following fields: ** This loop fills in the following fields:
@@ -5207,7 +5409,6 @@ WhereInfo *sqlite3WhereBegin(
sWBI.n = nTabList; sWBI.n = nTabList;
sWBI.pDistinct = pDistinct; sWBI.pDistinct = pDistinct;
andFlags = ~0; andFlags = ~0;
WHERETRACE(("*** Optimizer Start ***\n"));
for(sWBI.i=iFrom=0, pLevel=pWInfo->a; sWBI.i<nTabList; sWBI.i++, pLevel++){ for(sWBI.i=iFrom=0, pLevel=pWInfo->a; sWBI.i<nTabList; sWBI.i++, pLevel++){
WhereCost bestPlan; /* Most efficient plan seen so far */ WhereCost bestPlan; /* Most efficient plan seen so far */
Index *pIdx; /* Index for FROM table at pTabItem */ Index *pIdx; /* Index for FROM table at pTabItem */
@@ -5221,7 +5422,7 @@ WhereInfo *sqlite3WhereBegin(
memset(&bestPlan, 0, sizeof(bestPlan)); memset(&bestPlan, 0, sizeof(bestPlan));
bestPlan.rCost = SQLITE_BIG_DBL; bestPlan.rCost = SQLITE_BIG_DBL;
WHERETRACE(("*** Begin search for loop %d ***\n", sWBI.i)); /*WHERETRACE(("*** Begin search for loop %d ***\n", sWBI.i));*/
/* Loop through the remaining entries in the FROM clause to find the /* Loop through the remaining entries in the FROM clause to find the
** next nested loop. The loop tests all FROM clause entries ** next nested loop. The loop tests all FROM clause entries
@@ -5304,8 +5505,8 @@ WhereInfo *sqlite3WhereBegin(
sWBI.notReady = (isOptimal ? m : sWBI.notValid); sWBI.notReady = (isOptimal ? m : sWBI.notValid);
if( sWBI.pSrc->pIndex==0 ) nUnconstrained++; if( sWBI.pSrc->pIndex==0 ) nUnconstrained++;
WHERETRACE((" === trying table %d (%s) with isOptimal=%d ===\n", /*WHERETRACE((" === trying table %d (%s) with isOptimal=%d ===\n",
j, sWBI.pSrc->pTab->zName, isOptimal)); j, sWBI.pSrc->pTab->zName, isOptimal));*/
assert( sWBI.pSrc->pTab ); assert( sWBI.pSrc->pTab );
#ifndef SQLITE_OMIT_VIRTUALTABLE #ifndef SQLITE_OMIT_VIRTUALTABLE
if( IsVirtual(sWBI.pSrc->pTab) ){ if( IsVirtual(sWBI.pSrc->pTab) ){
@@ -5335,9 +5536,9 @@ WhereInfo *sqlite3WhereBegin(
** for the outer loop that table which benefits the least from ** for the outer loop that table which benefits the least from
** being in the inner loop. The following code scales the ** being in the inner loop. The following code scales the
** outer loop cost estimate to accomplish that. */ ** outer loop cost estimate to accomplish that. */
WHERETRACE((" scaling cost from %.1f to %.1f\n", /*WHERETRACE((" scaling cost from %.1f to %.1f\n",
sWBI.cost.rCost, sWBI.cost.rCost,
sWBI.cost.rCost/pWInfo->a[j].rOptCost)); sWBI.cost.rCost/pWInfo->a[j].rOptCost));*/
sWBI.cost.rCost /= pWInfo->a[j].rOptCost; sWBI.cost.rCost /= pWInfo->a[j].rOptCost;
} }
@@ -5367,11 +5568,11 @@ WhereInfo *sqlite3WhereBegin(
|| NEVER((sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0)) || NEVER((sWBI.cost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0))
&& (bestJ<0 || compareCost(&sWBI.cost, &bestPlan)) /* (4) */ && (bestJ<0 || compareCost(&sWBI.cost, &bestPlan)) /* (4) */
){ ){
WHERETRACE((" === table %d (%s) is best so far\n" /*WHERETRACE((" === table %d (%s) is best so far\n"
" cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=%08x\n", " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=%08x\n",
j, sWBI.pSrc->pTab->zName, j, sWBI.pSrc->pTab->zName,
sWBI.cost.rCost, sWBI.cost.plan.nRow, sWBI.cost.rCost, sWBI.cost.plan.nRow,
sWBI.cost.plan.nOBSat, sWBI.cost.plan.wsFlags)); sWBI.cost.plan.nOBSat, sWBI.cost.plan.wsFlags));*/
bestPlan = sWBI.cost; bestPlan = sWBI.cost;
bestJ = j; bestJ = j;
} }
@@ -5388,11 +5589,11 @@ WhereInfo *sqlite3WhereBegin(
testcase( bestJ>iFrom && (pTabList->a[iFrom].jointype & JT_CROSS)!=0 ); testcase( bestJ>iFrom && (pTabList->a[iFrom].jointype & JT_CROSS)!=0 );
testcase( bestJ>iFrom && bestJ<nTabList-1 testcase( bestJ>iFrom && bestJ<nTabList-1
&& (pTabList->a[bestJ+1].jointype & JT_LEFT)!=0 ); && (pTabList->a[bestJ+1].jointype & JT_LEFT)!=0 );
WHERETRACE(("*** Optimizer selects table %d (%s) for loop %d with:\n" /*WHERETRACE(("*** Optimizer selects table %d (%s) for loop %d with:\n"
" cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=0x%08x\n", " cost=%.1f, nRow=%.1f, nOBSat=%d, wsFlags=0x%08x\n",
bestJ, pTabList->a[bestJ].pTab->zName, bestJ, pTabList->a[bestJ].pTab->zName,
pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow, pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow,
bestPlan.plan.nOBSat, bestPlan.plan.wsFlags)); bestPlan.plan.nOBSat, bestPlan.plan.wsFlags));*/
if( (bestPlan.plan.wsFlags & WHERE_DISTINCT)!=0 ){ if( (bestPlan.plan.wsFlags & WHERE_DISTINCT)!=0 ){
assert( pWInfo->eDistinct==0 ); assert( pWInfo->eDistinct==0 );
pWInfo->eDistinct = WHERE_DISTINCT_ORDERED; pWInfo->eDistinct = WHERE_DISTINCT_ORDERED;