aboutsummaryrefslogtreecommitdiff
path: root/src/where.c
diff options
context:
space:
mode:
authordrh <drh@noemail.net>2014-04-18 01:10:05 +0000
committerdrh <drh@noemail.net>2014-04-18 01:10:05 +0000
commita1a483d54e39272c16b5fa94e97578ccddee6a8a (patch)
treeb28bb3ea6cdff4cdefa08c26c870e415e996c730 /src/where.c
parent652cc4b6dca02426e8d375a962bf8417850d9f7a (diff)
parent71794dbaeb73f594ef704fa265715b207f98edc7 (diff)
downloadsqlite-a1a483d54e39272c16b5fa94e97578ccddee6a8a.tar.gz
sqlite-a1a483d54e39272c16b5fa94e97578ccddee6a8a.zip
Merge recent trunk changes into sessions.
FossilOrigin-Name: 95e77efe076ab421bd246119c47dba5dacf9d087
Diffstat (limited to 'src/where.c')
-rw-r--r--src/where.c233
1 files changed, 159 insertions, 74 deletions
diff --git a/src/where.c b/src/where.c
index dd6893f69..e51eee535 100644
--- a/src/where.c
+++ b/src/where.c
@@ -2841,7 +2841,7 @@ static Bitmask codeOneLoopStart(
pLevel->p1 = iCur;
pLevel->p2 = sqlite3VdbeCurrentAddr(v);
sqlite3ReleaseTempRange(pParse, iReg, nConstraint+2);
- sqlite3ExprCachePop(pParse, 1);
+ sqlite3ExprCachePop(pParse);
}else
#endif /* SQLITE_OMIT_VIRTUALTABLE */
@@ -3712,6 +3712,124 @@ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){
}
/*
+** Return TRUE if the set of WHERE clause terms used by pA is a proper
+** subset of the WHERE clause terms used by pB.
+*/
+static int whereLoopProperSubset(const WhereLoop *pA, const WhereLoop *pB){
+ int i, j;
+ assert( pA->nLTerm<pB->nLTerm ); /* Checked by calling function */
+ for(j=0, i=pA->nLTerm-1; i>=0 && j>=0; i--){
+ for(j=pB->nLTerm-1; j>=0; j--){
+ if( pB->aLTerm[j]==pA->aLTerm[i] ) break;
+ }
+ }
+ return j>=0;
+}
+
+/*
+** Try to adjust the cost of WhereLoop pTemplate upwards or downwards so
+** that:
+**
+** (1) pTemplate costs less than any other WhereLoops that are a proper
+** subset of pTemplate
+**
+** (2) pTemplate costs more than any other WhereLoops for which pTemplate
+** is a proper subset.
+**
+** To say "WhereLoop X is a proper subset of Y" means that X uses fewer
+** WHERE clause terms than Y and that every WHERE clause term used by X is
+** also used by Y.
+*/
+static void whereLoopAdjustCost(const WhereLoop *p, WhereLoop *pTemplate){
+ if( (pTemplate->wsFlags & WHERE_INDEXED)==0 ) return;
+ for(; p; p=p->pNextLoop){
+ if( p->iTab!=pTemplate->iTab ) continue;
+ if( (p->wsFlags & WHERE_INDEXED)==0 ) continue;
+ if( p->nLTerm<pTemplate->nLTerm
+ && (p->rRun<pTemplate->rRun || (p->rRun==pTemplate->rRun &&
+ p->nOut<=pTemplate->nOut))
+ && whereLoopProperSubset(p, pTemplate)
+ ){
+ pTemplate->rRun = p->rRun;
+ pTemplate->nOut = p->nOut - 1;
+ }else
+ if( p->nLTerm>pTemplate->nLTerm
+ && (p->rRun>pTemplate->rRun || (p->rRun==pTemplate->rRun &&
+ p->nOut>=pTemplate->nOut))
+ && whereLoopProperSubset(pTemplate, p)
+ ){
+ pTemplate->rRun = p->rRun;
+ pTemplate->nOut = p->nOut + 1;
+ }
+ }
+}
+
+/*
+** Search the list of WhereLoops in *ppPrev looking for one that can be
+** supplanted by pTemplate.
+**
+** Return NULL if the WhereLoop list contains an entry that can supplant
+** pTemplate, in other words if pTemplate does not belong on the list.
+**
+** If pX is a WhereLoop that pTemplate can supplant, then return the
+** link that points to pX.
+**
+** If pTemplate cannot supplant any existing element of the list but needs
+** to be added to the list, then return a pointer to the tail of the list.
+*/
+static WhereLoop **whereLoopFindLesser(
+ WhereLoop **ppPrev,
+ const WhereLoop *pTemplate
+){
+ WhereLoop *p;
+ for(p=(*ppPrev); p; ppPrev=&p->pNextLoop, p=*ppPrev){
+ if( p->iTab!=pTemplate->iTab || p->iSortIdx!=pTemplate->iSortIdx ){
+ /* If either the iTab or iSortIdx values for two WhereLoop are different
+ ** then those WhereLoops need to be considered separately. Neither is
+ ** a candidate to replace the other. */
+ continue;
+ }
+ /* In the current implementation, the rSetup value is either zero
+ ** or the cost of building an automatic index (NlogN) and the NlogN
+ ** is the same for compatible WhereLoops. */
+ assert( p->rSetup==0 || pTemplate->rSetup==0
+ || p->rSetup==pTemplate->rSetup );
+
+ /* whereLoopAddBtree() always generates and inserts the automatic index
+ ** case first. Hence compatible candidate WhereLoops never have a larger
+ ** rSetup. Call this SETUP-INVARIANT */
+ assert( p->rSetup>=pTemplate->rSetup );
+
+ /* If existing WhereLoop p is better than pTemplate, pTemplate can be
+ ** discarded. WhereLoop p is better if:
+ ** (1) p has no more dependencies than pTemplate, and
+ ** (2) p has an equal or lower cost than pTemplate
+ */
+ if( (p->prereq & pTemplate->prereq)==p->prereq /* (1) */
+ && p->rSetup<=pTemplate->rSetup /* (2a) */
+ && p->rRun<=pTemplate->rRun /* (2b) */
+ && p->nOut<=pTemplate->nOut /* (2c) */
+ ){
+ return 0; /* Discard pTemplate */
+ }
+
+ /* If pTemplate is always better than p, then cause p to be overwritten
+ ** with pTemplate. pTemplate is better than p if:
+ ** (1) pTemplate has no more dependences than p, and
+ ** (2) pTemplate has an equal or lower cost than p.
+ */
+ if( (p->prereq & pTemplate->prereq)==pTemplate->prereq /* (1) */
+ && p->rRun>=pTemplate->rRun /* (2a) */
+ && p->nOut>=pTemplate->nOut /* (2b) */
+ ){
+ assert( p->rSetup>=pTemplate->rSetup ); /* SETUP-INVARIANT above */
+ break; /* Cause p to be overwritten by pTemplate */
+ }
+ }
+ return ppPrev;
+}
+
+/*
** Insert or replace a WhereLoop entry using the template supplied.
**
** An existing WhereLoop entry might be overwritten if the new template
@@ -3720,25 +3838,23 @@ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){
** fewer dependencies than the template. Otherwise a new WhereLoop is
** added based on the template.
**
-** If pBuilder->pOrSet is not NULL then we only care about only the
+** If pBuilder->pOrSet is not NULL then we care about only the
** prerequisites and rRun and nOut costs of the N best loops. That
** information is gathered in the pBuilder->pOrSet object. This special
** processing mode is used only for OR clause processing.
**
** When accumulating multiple loops (when pBuilder->pOrSet is NULL) we
** still might overwrite similar loops with the new template if the
-** template is better. Loops may be overwritten if the following
+** new template is better. Loops may be overwritten if the following
** conditions are met:
**
** (1) They have the same iTab.
** (2) They have the same iSortIdx.
** (3) The template has same or fewer dependencies than the current loop
** (4) The template has the same or lower cost than the current loop
-** (5) The template uses more terms of the same index but has no additional
-** dependencies
*/
static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){
- WhereLoop **ppPrev, *p, *pNext = 0;
+ WhereLoop **ppPrev, *p;
WhereInfo *pWInfo = pBuilder->pWInfo;
sqlite3 *db = pWInfo->pParse->db;
@@ -3761,64 +3877,23 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){
return SQLITE_OK;
}
- /* Search for an existing WhereLoop to overwrite, or which takes
- ** priority over pTemplate.
+ /* Look for an existing WhereLoop to replace with pTemplate
*/
- for(ppPrev=&pWInfo->pLoops, p=*ppPrev; p; ppPrev=&p->pNextLoop, p=*ppPrev){
- if( p->iTab!=pTemplate->iTab || p->iSortIdx!=pTemplate->iSortIdx ){
- /* If either the iTab or iSortIdx values for two WhereLoop are different
- ** then those WhereLoops need to be considered separately. Neither is
- ** a candidate to replace the other. */
- continue;
- }
- /* In the current implementation, the rSetup value is either zero
- ** or the cost of building an automatic index (NlogN) and the NlogN
- ** is the same for compatible WhereLoops. */
- assert( p->rSetup==0 || pTemplate->rSetup==0
- || p->rSetup==pTemplate->rSetup );
-
- /* whereLoopAddBtree() always generates and inserts the automatic index
- ** case first. Hence compatible candidate WhereLoops never have a larger
- ** rSetup. Call this SETUP-INVARIANT */
- assert( p->rSetup>=pTemplate->rSetup );
+ whereLoopAdjustCost(pWInfo->pLoops, pTemplate);
+ ppPrev = whereLoopFindLesser(&pWInfo->pLoops, pTemplate);
- if( (p->prereq & pTemplate->prereq)==p->prereq
- && p->rSetup<=pTemplate->rSetup
- && p->rRun<=pTemplate->rRun
- && p->nOut<=pTemplate->nOut
- ){
- /* This branch taken when p is equal or better than pTemplate in
- ** all of (1) dependencies (2) setup-cost, (3) run-cost, and
- ** (4) number of output rows. */
- assert( p->rSetup==pTemplate->rSetup );
- if( p->prereq==pTemplate->prereq
- && p->nLTerm<pTemplate->nLTerm
- && (p->wsFlags & pTemplate->wsFlags & WHERE_INDEXED)!=0
- && (p->u.btree.pIndex==pTemplate->u.btree.pIndex
- || pTemplate->rRun+p->nLTerm<=p->rRun+pTemplate->nLTerm)
- ){
- /* Overwrite an existing WhereLoop with an similar one that uses
- ** more terms of the index */
- pNext = p->pNextLoop;
- break;
- }else{
- /* pTemplate is not helpful.
- ** Return without changing or adding anything */
- goto whereLoopInsert_noop;
- }
- }
- if( (p->prereq & pTemplate->prereq)==pTemplate->prereq
- && p->rRun>=pTemplate->rRun
- && p->nOut>=pTemplate->nOut
- ){
- /* Overwrite an existing WhereLoop with a better one: one that is
- ** better at one of (1) dependencies, (2) setup-cost, (3) run-cost
- ** or (4) number of output rows, and is no worse in any of those
- ** categories. */
- assert( p->rSetup>=pTemplate->rSetup ); /* SETUP-INVARIANT above */
- pNext = p->pNextLoop;
- break;
+ if( ppPrev==0 ){
+ /* There already exists a WhereLoop on the list that is better
+ ** than pTemplate, so just ignore pTemplate */
+#if WHERETRACE_ENABLED /* 0x8 */
+ if( sqlite3WhereTrace & 0x8 ){
+ sqlite3DebugPrintf("ins-noop: ");
+ whereLoopPrint(pTemplate, pBuilder->pWC);
}
+#endif
+ return SQLITE_OK;
+ }else{
+ p = *ppPrev;
}
/* If we reach this point it means that either p[] should be overwritten
@@ -3836,13 +3911,33 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){
}
#endif
if( p==0 ){
- p = sqlite3DbMallocRaw(db, sizeof(WhereLoop));
+ /* Allocate a new WhereLoop to add to the end of the list */
+ *ppPrev = p = sqlite3DbMallocRaw(db, sizeof(WhereLoop));
if( p==0 ) return SQLITE_NOMEM;
whereLoopInit(p);
+ p->pNextLoop = 0;
+ }else{
+ /* We will be overwriting WhereLoop p[]. But before we do, first
+ ** go through the rest of the list and delete any other entries besides
+ ** p[] that are also supplated by pTemplate */
+ WhereLoop **ppTail = &p->pNextLoop;
+ WhereLoop *pToDel;
+ while( *ppTail ){
+ ppTail = whereLoopFindLesser(ppTail, pTemplate);
+ if( NEVER(ppTail==0) ) break;
+ pToDel = *ppTail;
+ if( pToDel==0 ) break;
+ *ppTail = pToDel->pNextLoop;
+#if WHERETRACE_ENABLED /* 0x8 */
+ if( sqlite3WhereTrace & 0x8 ){
+ sqlite3DebugPrintf("ins-del: ");
+ whereLoopPrint(pToDel, pBuilder->pWC);
+ }
+#endif
+ whereLoopDelete(db, pToDel);
+ }
}
whereLoopXfer(db, p, pTemplate);
- p->pNextLoop = pNext;
- *ppPrev = p;
if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){
Index *pIndex = p->u.btree.pIndex;
if( pIndex && pIndex->tnum==0 ){
@@ -3850,16 +3945,6 @@ static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){
}
}
return SQLITE_OK;
-
- /* Jump here if the insert is a no-op */
-whereLoopInsert_noop:
-#if WHERETRACE_ENABLED /* 0x8 */
- if( sqlite3WhereTrace & 0x8 ){
- sqlite3DebugPrintf("ins-noop: ");
- whereLoopPrint(pTemplate, pBuilder->pWC);
- }
-#endif
- return SQLITE_OK;
}
/*