/*
** 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
+** objects using (maskSelf,prereq,) as the primary key. Note that the
** same join term might have multiple associated WhereLoop objects.
*/
struct WhereLoop {
Bitmask maskSelf; /* Bitmask identifying table iTab */
u16 iTab; /* Index of the table coded by this loop */
u16 nTerm; /* Number of entries in aTerm[] */
- u16 iOb, nOb; /* ORDER BY terms satisfied by this strategy */
+ u32 wsFlags; /* WHERE_* flags describing the plan */
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 */
union {
struct { /* Information for internal btree tables */
int nEq; /* Number of equality constraints */
Index *pIndex; /* Index used, or NULL */
} btree;
- struct { /* Information for virtualt tables */
+ struct { /* Information for virtual tables */
int idxNum; /* Index number */
- int needFree; /* True if sqlite3_free(idxStr) is needed */
+ u8 needFree; /* True if sqlite3_free(idxStr) is needed */
+ u8 isOrdered; /* True if satisfies ORDER BY */
char *idxStr; /* Index identifier string */
} vtab;
} u;
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 */
+ u8 isOrdered; /* True if this path satisfies ORDER BY */
+ u8 isOrderedValid; /* True if the isOrdered field is valid */
WhereLoop **aLoop; /* Array of WhereLoop objects implementing this path */
};
** Value for wsFlags returned by bestIndex() and stored in
** WhereLevel.wsFlags. These flags determine which search
** strategies are appropriate.
-**
-** The least significant 12 bits is reserved as a mask for WO_ values above.
-** The WhereLevel.wsFlags field is usually set to WO_IN|WO_EQ|WO_ISNULL.
-** But if the table is the right table of a left join, WhereLevel.wsFlags
-** is set to WO_IN|WO_EQ. The WhereLevel.wsFlags field can then be used as
-** the "op" parameter to findTerm when we are resolving equality constraints.
-** ISNULL constraints will then not be used on the right table of a left
-** join. Tickets #2177 and #2189.
*/
-#define WHERE_ROWID_EQ 0x00001000 /* rowid=EXPR or rowid IN (...) */
-#define WHERE_ROWID_RANGE 0x00002000 /* rowid<EXPR and/or rowid>EXPR */
-#define WHERE_IPK 0x00008000 /* x is the INTEGER PRIMARY KEY */
-#define WHERE_COLUMN_EQ 0x00010000 /* x=EXPR or x IN (...) or x IS NULL */
-#define WHERE_COLUMN_RANGE 0x00020000 /* x<EXPR and/or x>EXPR */
-#define WHERE_COLUMN_IN 0x00040000 /* x IN (...) */
-#define WHERE_COLUMN_NULL 0x00080000 /* x IS NULL */
-#define WHERE_INDEXED 0x000f0000 /* Anything that uses an index */
-#define WHERE_NOT_FULLSCAN 0x100f3000 /* Does not do a full table scan */
-#define WHERE_IN_ABLE 0x080f1000 /* Able to support an IN operator */
-#define WHERE_TOP_LIMIT 0x00100000 /* x<EXPR or x<=EXPR constraint */
-#define WHERE_BTM_LIMIT 0x00200000 /* x>EXPR or x>=EXPR constraint */
-#define WHERE_BOTH_LIMIT 0x00300000 /* Both x>EXPR and x<EXPR */
-#define WHERE_IDX_ONLY 0x00400000 /* Use index only - omit table */
-#define WHERE_ORDERED 0x00800000 /* Output will appear in correct order */
-#define WHERE_REVERSE 0x01000000 /* Scan in reverse order */
-#define WHERE_UNIQUE 0x02000000 /* Selects no more than one row */
-#define WHERE_ALL_UNIQUE 0x04000000 /* This and all prior have one row */
-#define WHERE_OB_UNIQUE 0x00004000 /* Values in ORDER BY columns are
+#define WHERE_ROWID_EQ 0x00000001 /* rowid=EXPR or rowid IN (...) */
+#define WHERE_ROWID_RANGE 0x00000002 /* rowid<EXPR and/or rowid>EXPR */
+#define WHERE_NULL_OK 0x00000004 /* Ok to use WO_ISNULL */
+#define WHERE_IPK 0x00000008 /* x is the INTEGER PRIMARY KEY */
+#define WHERE_COLUMN_EQ 0x00000010 /* x=EXPR or x IN (...) or x IS NULL */
+#define WHERE_COLUMN_RANGE 0x00000020 /* x<EXPR and/or x>EXPR */
+#define WHERE_COLUMN_IN 0x00000040 /* x IN (...) */
+#define WHERE_COLUMN_NULL 0x00000080 /* x IS NULL */
+#define WHERE_INDEXED 0x000000f0 /* Anything that uses an index */
+#define WHERE_NOT_FULLSCAN 0x000200f3 /* Does not do a full table scan */
+#define WHERE_IN_ABLE 0x000100f1 /* Able to support an IN operator */
+#define WHERE_TOP_LIMIT 0x00000100 /* x<EXPR or x<=EXPR constraint */
+#define WHERE_BTM_LIMIT 0x00000200 /* x>EXPR or x>=EXPR constraint */
+#define WHERE_BOTH_LIMIT 0x00000300 /* Both x>EXPR and x<EXPR */
+#define WHERE_IDX_ONLY 0x00000400 /* Use index only - omit table */
+#define WHERE_ORDERED 0x00000800 /* Output will appear in correct order */
+#define WHERE_REVERSE 0x00001000 /* Scan in reverse order */
+#define WHERE_UNIQUE 0x00002000 /* Selects no more than one row */
+#define WHERE_ALL_UNIQUE 0x00004000 /* This and all prior have one row */
+#define WHERE_OB_UNIQUE 0x00008000 /* Values in ORDER BY columns are
** different for every output row */
-#define WHERE_VIRTUALTABLE 0x08000000 /* Use virtual-table processing */
-#define WHERE_FREEIDXSTR 0x04000000 /* neeed to free WhereLoop.u.idxStr */
-#define WHERE_MULTI_OR 0x10000000 /* OR using multiple indices */
-#define WHERE_TEMP_INDEX 0x20000000 /* Uses an ephemeral index */
-#define WHERE_DISTINCT 0x40000000 /* Correct order for DISTINCT */
-#define WHERE_COVER_SCAN 0x80000000 /* Full scan of a covering index */
+#define WHERE_VIRTUALTABLE 0x00010000 /* Use virtual-table processing */
+#define WHERE_MULTI_OR 0x00020000 /* OR using multiple indices */
+#define WHERE_TEMP_INDEX 0x00040000 /* Uses an ephemeral index */
+#define WHERE_DISTINCT 0x00080000 /* Correct order for DISTINCT */
+#define WHERE_COVER_SCAN 0x00100000 /* Full scan of a covering index */
+#define WHERE_SINGLE_ROW 0x00200000 /* No more than one row guaranteed */
/*
** This module contains many separate subroutines that work together to
WhereClause * const pOrWC = &pTerm->u.pOrInfo->wc;
WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm];
WhereTerm *pOrTerm;
- int flags = WHERE_MULTI_OR;
double rTotal = 0;
double nRow = 0;
Bitmask used = 0;
p->cost.used = used;
p->cost.plan.nRow = nRow;
p->cost.plan.nOBSat = p->i ? p->aLevel[p->i-1].plan.nOBSat : 0;
- p->cost.plan.wsFlags = flags;
+ p->cost.plan.wsFlags = WHERE_MULTI_OR;
p->cost.plan.u.pTerm = pTerm;
}
}
if( pSrc->colUsed & (((Bitmask)1)<<(BMS-1)) ){
nColumn += pTable->nCol - BMS + 1;
}
- pLevel->plan.wsFlags |= WHERE_COLUMN_EQ | WHERE_IDX_ONLY | WO_EQ;
+ pLevel->plan.wsFlags |= WHERE_COLUMN_EQ | WHERE_IDX_ONLY;
/* Construct the Index object to describe this index */
nByte = sizeof(Index);
bestOrClauseIndex(p);
bestAutomaticIndex(p);
- p->cost.plan.wsFlags |= eqTermMask;
+ if( eqTermMask & WO_ISNULL ) p->cost.plan.wsFlags |= WHERE_NULL_OK;
}
/*
int regBase; /* Base register */
int nReg; /* Number of registers to allocate */
char *zAff; /* Affinity string to return */
+ int eqFlags; /* WO_EQ|WO_IN and maybe also WO_ISNULL */
/* This module is only called on query plans that use an index. */
assert( pLevel->plan.wsFlags & WHERE_INDEXED );
/* Evaluate the equality constraints
*/
assert( pIdx->nColumn>=nEq );
+ eqFlags = (pLevel->plan.wsFlags&WHERE_NULL_OK) ? (WO_EQ|WO_IN|WO_ISNULL)
+ : (WO_EQ|WO_IN);
for(j=0; j<nEq; j++){
int r1;
int k = pIdx->aiColumn[j];
- pTerm = findTerm(pWC, iCur, k, notReady, pLevel->plan.wsFlags, pIdx);
+ pTerm = findTerm(pWC, iCur, k, notReady, eqFlags, pIdx);
if( pTerm==0 ) break;
/* The following true for indices with redundant columns.
** Ex: CREATE INDEX i1 ON t1(a,b,a); SELECT * FROM t1 WHERE a=0 AND b=0; */
sqlite3DebugPrintf(" %-15s", z);
sqlite3_free(z);
}
- sqlite3DebugPrintf(" fg %08x OB %d,%d N %2d",
- p->wsFlags, p->iOb, p->nOb, p->nTerm);
+ sqlite3DebugPrintf(" fg %08x N %2d", p->wsFlags, p->nTerm);
sqlite3DebugPrintf(" cost %.4g,%.4g,%.4g\n",
p->prereq, p->rSetup, p->rRun, p->nOut);
}
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
){
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
){
pNew->prereq = 0;
pNew->u.btree.pIndex = 0;
pNew->wsFlags = 0;
- pNew->iOb = pNew->nOb = 0;
pNew->rRun = (double)pSrc->pTab->nRowEst;
pNew->nOut = (double)pSrc->pTab->nRowEst;
rc = whereLoopInsert(pBuilder->pWInfo, pNew);
pNew->prereq = 0;
pNew->iTab = iTab;
pNew->maskSelf = getMask(pBuilder->pWC->pMaskSet, pSrc->iCursor);
- pNew->iOb = 0;
- pNew->nOb = 0;
pNew->rSetup = 0;
pNew->wsFlags = WHERE_VIRTUALTABLE;
pNew->nTerm = 0;
pNew->u.vtab.needFree = pIdxInfo->needToFreeIdxStr;
pIdxInfo->needToFreeIdxStr = 0;
pNew->u.vtab.idxStr = pIdxInfo->idxStr;
- pNew->iOb = 0;
- if( pIdxInfo->orderByConsumed ){
- pNew->nOb = (u16)(pIdxInfo->nOrderBy&0xffff);
- }else{
- pNew->nOb = 0;
- }
+ pNew->u.vtab.isOrdered = (u8)(pIdxInfo->nOrderBy!=0);
pNew->rSetup = (double)0;
pNew->rRun = pIdxInfo->estimatedCost;
pNew->nOut = (double)25;
return rc;
}
+/*
+** Examine a WherePath to see if it outputs rows in the requested ORDER BY
+** (or GROUP BY) without requiring a separate source operation. Return 1
+** if it does and 0 if it does not and -1 if we cannot tell.
+*/
+static int wherePathSatisfiesOrderBy(
+ WhereInfo *pWInfo, /* The WHERE clause */
+ WherePath *pPath, /* The WherePath to check */
+ int nLoop, /* Number of entries in pPath->aLoop[] */
+ WhereLoop *pLoop /* Add this WhereLoop to the end of pPath->aLoop[] */
+){
+ if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){
+ return nLoop==0 && pLoop->u.vtab.isOrdered;
+ }else{
+ /* TBD: Check to see if pFrom + pWLoop satisfies the ORDER BY.
+ ** (1) If yes: set isOrderedValid and isOrdered to 1.
+ ** (2) If no: set isOrderedValid to 1 and isOrdered to 0.
+ ** (3) unknown: no-op */
+ return 0;
+ }
+}
+
+
/*
** Given the list of WhereLoop objects on pWInfo->pLoops, this routine
** attempts to find the lowest cost path that visits each WhereLoop
int ii, jj; /* Loop counters */
double rCost; /* Cost of a path */
double mxCost; /* Maximum cost of a set of paths */
+ double rSortCost; /* Cost to do a sort */
int nTo, nFrom; /* Number of valid entries in aTo[] and aFrom[] */
WherePath *aFrom; /* All nFrom paths at the previous level */
WherePath *aTo; /* The nTo best paths at the current level */
WherePath *pFrom; /* An element of aFrom[] that we are working on */
WherePath *pTo; /* An element of aTo[] that we are working on */
WhereLoop *pWLoop; /* One of the WhereLoop objects */
+ WhereLoop *pNext; /* Next loop */
WhereLoop **pX; /* Used to divy up the pSpace memory */
char *pSpace; /* Temporary memory used by this routine */
pFrom->aLoop = pX;
}
+ /* Seed the search with a single WherePath containing zero WhereLoops */
aFrom[0].nRow = (double)1;
nFrom = 1;
+
+ /* Precompute the cost of sorting the final result set, if the caller
+ ** to sqlite3WhereBegin() was concerned about sorting */
+ rSortCost = (double)0;
+ if( pWInfo->pOrderBy==0 ){
+ aFrom[0].isOrderedValid = 1;
+ }else{
+ /* Compute an estimate on the cost to sort the entire result set */
+ rSortCost = (double)1;
+ for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pNext){
+ pNext = pWLoop->pNextLoop;
+ rCost = pWLoop->nOut;
+ while( pNext && pNext->iTab==pWLoop->iTab ){
+ if( pNext->nOut<rCost ) rCost = pNext->nOut;
+ pNext = pNext->pNextLoop;
+ }
+ rSortCost *= rCost;
+ }
+ rSortCost *= estLog(rSortCost);
+ }
+
+ /* Compute successively longer WherePaths using the previous generation
+ ** of WherePaths as the basis for the next. Keep track of the mxChoice
+ ** best paths at each generation */
for(iLoop=0; iLoop<nLoop; iLoop++){
nTo = 0;
for(ii=0, pFrom=aFrom; ii<nFrom; ii++, pFrom++){
for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){
Bitmask maskNew;
+ u8 isOrderedValid = pFrom->isOrderedValid;
+ u8 isOrdered = pFrom->isOrdered;
if( (pWLoop->prereq & ~pFrom->maskLoop)!=0 ) continue;
if( (pWLoop->maskSelf & pFrom->maskLoop)!=0 ) continue;
+ /* At this point, pWLoop is a candidate to be the next loop.
+ ** Compute its cost */
rCost = pWLoop->rSetup + pWLoop->rRun*pFrom->nRow + pFrom->rCost;
maskNew = pFrom->maskLoop | pWLoop->maskSelf;
- for(jj=0, pTo=aTo; jj<nTo && pTo->maskLoop!=maskNew; jj++){}
+ if( !isOrderedValid ){
+ switch( wherePathSatisfiesOrderBy(pWInfo, pFrom, iLoop, pWLoop) ){
+ case 1: /* Yes. pFrom+pWLoop does satisfy the ORDER BY clause */
+ isOrdered = 1;
+ isOrderedValid = 1;
+ break;
+ case 0: /* No. pFrom+pWLoop will require a separate sort */
+ isOrdered = 0;
+ isOrderedValid = 1;
+ rCost += rSortCost;
+ break;
+ default: /* Cannot tell yet. Try again on the next iteration */
+ break;
+ }
+ }
+ /* Check to see if pWLoop should be added to the mxChoice best so far */
+ for(jj=0, pTo=aTo; jj<nTo; jj++, pTo++){
+ if( pTo->maskLoop==maskNew && pTo->isOrderedValid==isOrderedValid ){
+ break;
+ }
+ }
if( jj>=nTo ){
if( nTo>=mxChoice && rCost>=mxCost ) continue;
if( nTo<mxChoice ){
}else{
if( pTo->rCost<=rCost ) continue;
}
+ /* pWLoop is a winner. Add it to the set of best so far */
pTo->maskLoop = pFrom->maskLoop | pWLoop->maskSelf;
pTo->nRow = pFrom->nRow * pWLoop->nOut;
pTo->rCost = rCost;
+ pTo->isOrderedValid = isOrderedValid;
+ pTo->isOrdered = isOrdered;
memcpy(pTo->aLoop, pFrom->aLoop, sizeof(WhereLoop*)*iLoop);
pTo->aLoop[iLoop] = pWLoop;
if( nTo>=mxChoice ){
}
#endif
- /* Swap the roles of aFrom and aTo in preparation for the next
- ** cycle. */
+ /* Swap the roles of aFrom and aTo for the next generation */
pFrom = aTo;
aTo = aFrom;
aFrom = pFrom;
if( nFrom==0 ){ sqlite3DbFree(db, pSpace); return SQLITE_ERROR; }
assert( nFrom>0 );
- /* Find the lowest cost path and load it into pWInfo->a[].pWLoop */
+ /* Find the lowest cost path. pFrom will be left pointing to that path */
pFrom = aFrom;
for(ii=1; ii<nFrom; ii++){
if( pFrom->rCost>aFrom[ii].rCost ) pFrom = &aFrom[ii];
}
assert( pWInfo->nLevel==nLoop );
+ /* Load the lowest cost path into pWInfo */
for(iLoop=0; iLoop<nLoop; iLoop++){
pWInfo->a[iLoop].pWLoop = pFrom->aLoop[iLoop];
}
+ if( pFrom->isOrdered ){
+ pWInfo->nOBSat = pWInfo->pOrderBy->nExpr;
+ }
/* Free temporary memory and return success */
sqlite3DbFree(db, pSpace);
pWInfo->nLevel = nTabList;
pWInfo->pParse = pParse;
pWInfo->pTabList = pTabList;
+ pWInfo->pOrderBy = pOrderBy;
+ pWInfo->pDistinct = pDistinct;
pWInfo->iBreak = sqlite3VdbeMakeLabel(v);
pWInfo->pWC = sWBI.pWC = (WhereClause *)&((u8 *)pWInfo)[nByteWInfo];
pWInfo->wctrlFlags = wctrlFlags;