Ensure WHERE clause terms involving tables on the right end of a join
are not prematurely evaluated when tables on the left end of the join
make use of the OR-clause optimization.
Fix for ticket [31338dca7e].
FossilOrigin-Name: 2c2de252666662f5459904fc33a9f2956cbff23c
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 1202971..1e15dbd 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -1894,6 +1894,7 @@
#define WHERE_OMIT_OPEN 0x0010 /* Table cursor are already open */
#define WHERE_OMIT_CLOSE 0x0020 /* Omit close of table & index cursors */
#define WHERE_FORCE_TABLE 0x0040 /* Do not use an index-only search */
+#define WHERE_ONETABLE_ONLY 0x0080 /* Only code the 1st table in pTabList */
/*
** The WHERE clause processing routine has two halves. The
@@ -1906,6 +1907,7 @@
Parse *pParse; /* Parsing and code generating context */
u16 wctrlFlags; /* Flags originally passed to sqlite3WhereBegin() */
u8 okOnePass; /* Ok to use one-pass algorithm for UPDATE or DELETE */
+ u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */
SrcList *pTabList; /* List of tables in the join */
int iTop; /* The very beginning of the WHERE loop */
int iContinue; /* Jump here to continue with next record */
diff --git a/src/where.c b/src/where.c
index 067ec40..90215ed 100644
--- a/src/where.c
+++ b/src/where.c
@@ -3265,13 +3265,14 @@
*/
WhereClause *pOrWc; /* The OR-clause broken out into subterms */
WhereTerm *pFinal; /* Final subterm within the OR-clause. */
- SrcList oneTab; /* Shortened table list */
+ SrcList *pOrTab; /* Shortened table list or OR-clause generation */
int regReturn = ++pParse->nMem; /* Register used with OP_Gosub */
int regRowset = 0; /* Register for RowSet object */
int regRowid = 0; /* Register holding rowid */
int iLoopBody = sqlite3VdbeMakeLabel(v); /* Start of loop body */
int iRetInit; /* Address of regReturn init */
+ int untestedTerms = 0; /* Some terms not completely tested */
int ii;
pTerm = pLevel->plan.u.pTerm;
@@ -3280,11 +3281,29 @@
assert( (pTerm->wtFlags & TERM_ORINFO)!=0 );
pOrWc = &pTerm->u.pOrInfo->wc;
pFinal = &pOrWc->a[pOrWc->nTerm-1];
+ pLevel->op = OP_Return;
+ pLevel->p1 = regReturn;
- /* Set up a SrcList containing just the table being scanned by this loop. */
- oneTab.nSrc = 1;
- oneTab.nAlloc = 1;
- oneTab.a[0] = *pTabItem;
+ /* Set up a new SrcList ni pOrTab containing the table being scanned
+ ** by this loop in the a[0] slot and all notReady tables in a[1..] slots.
+ ** This becomes the SrcList in the recursive call to sqlite3WhereBegin().
+ */
+ if( pWInfo->nLevel>1 ){
+ int nNotReady; /* The number of notReady tables */
+ struct SrcList_item *origSrc; /* Original list of tables */
+ nNotReady = pWInfo->nLevel - iLevel - 1;
+ pOrTab = sqlite3StackAllocRaw(pParse->db,
+ sizeof(*pOrTab)+ nNotReady*sizeof(pOrTab->a[0]));
+ if( pOrTab==0 ) return notReady;
+ pOrTab->nSrc = pOrTab->nAlloc = nNotReady + 1;
+ memcpy(pOrTab->a, pTabItem, sizeof(*pTabItem));
+ origSrc = pWInfo->pTabList->a;
+ for(k=1; k<=nNotReady; k++){
+ memcpy(&pOrTab->a[k], &origSrc[pLevel[k].iFrom], sizeof(pOrTab->a[k]));
+ }
+ }else{
+ pOrTab = pWInfo->pTabList;
+ }
/* Initialize the rowset register to contain NULL. An SQL NULL is
** equivalent to an empty rowset.
@@ -3309,8 +3328,9 @@
if( pOrTerm->leftCursor==iCur || pOrTerm->eOperator==WO_AND ){
WhereInfo *pSubWInfo; /* Info for single OR-term scan */
/* Loop through table entries that match term pOrTerm. */
- pSubWInfo = sqlite3WhereBegin(pParse, &oneTab, pOrTerm->pExpr, 0,
- WHERE_OMIT_OPEN | WHERE_OMIT_CLOSE | WHERE_FORCE_TABLE);
+ pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrTerm->pExpr, 0,
+ WHERE_OMIT_OPEN | WHERE_OMIT_CLOSE |
+ WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY);
if( pSubWInfo ){
if( (wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
int iSet = ((ii==pOrWc->nTerm-1)?-1:ii);
@@ -3322,19 +3342,24 @@
}
sqlite3VdbeAddOp2(v, OP_Gosub, regReturn, iLoopBody);
+ /* The pSubWInfo->untestedTerms flag means that this OR term
+ ** contained one or more AND term from a notReady table. The
+ ** terms from the notReady table could not be tested and will
+ ** need to be tested later.
+ */
+ if( pSubWInfo->untestedTerms ) untestedTerms = 1;
+
/* Finish the loop through table entries that match term pOrTerm. */
sqlite3WhereEnd(pSubWInfo);
}
}
}
sqlite3VdbeChangeP1(v, iRetInit, sqlite3VdbeCurrentAddr(v));
- /* sqlite3VdbeAddOp2(v, OP_Null, 0, regRowset); */
sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrBrk);
sqlite3VdbeResolveLabel(v, iLoopBody);
- pLevel->op = OP_Return;
- pLevel->p1 = regReturn;
- disableTerm(pLevel, pTerm);
+ if( pWInfo->nLevel>1 ) sqlite3StackFree(pParse->db, pOrTab);
+ if( !untestedTerms ) disableTerm(pLevel, pTerm);
}else
#endif /* SQLITE_OMIT_OR_OPTIMIZATION */
@@ -3362,7 +3387,12 @@
testcase( pTerm->wtFlags & TERM_VIRTUAL );
testcase( pTerm->wtFlags & TERM_CODED );
if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
- if( (pTerm->prereqAll & notReady)!=0 ) continue;
+ if( (pTerm->prereqAll & notReady)!=0 ){
+ testcase( pWInfo->untestedTerms==0
+ && (pWInfo->wctrlFlags & WHERE_ONETABLE_ONLY)!=0 );
+ pWInfo->untestedTerms = 1;
+ continue;
+ }
pE = pTerm->pExpr;
assert( pE!=0 );
if( pLevel->iLeftJoin && !ExprHasProperty(pE, EP_FromJoin) ){
@@ -3385,7 +3415,12 @@
testcase( pTerm->wtFlags & TERM_VIRTUAL );
testcase( pTerm->wtFlags & TERM_CODED );
if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
- if( (pTerm->prereqAll & notReady)!=0 ) continue;
+ if( (pTerm->prereqAll & notReady)!=0 ){
+ testcase( pWInfo->untestedTerms==0
+ && (pWInfo->wctrlFlags & WHERE_ONETABLE_ONLY)!=0 );
+ pWInfo->untestedTerms = 1;
+ continue;
+ }
assert( pTerm->pExpr );
sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL);
pTerm->wtFlags |= TERM_CODED;
@@ -3528,6 +3563,7 @@
){
int i; /* Loop counter */
int nByteWInfo; /* Num. bytes allocated for WhereInfo struct */
+ int nTabList; /* Number of elements in pTabList */
WhereInfo *pWInfo; /* Will become the return value of this function */
Vdbe *v = pParse->pVdbe; /* The virtual database engine */
Bitmask notReady; /* Cursors that are not yet positioned */
@@ -3547,6 +3583,13 @@
return 0;
}
+ /* This function normally generates a nested loop for all tables in
+ ** pTabList. But if the WHERE_ONETABLE_ONLY flag is set, then we should
+ ** only generate code for the first table in pTabList and assume that
+ ** any cursors associated with subsequent tables are uninitialized.
+ */
+ nTabList = (wctrlFlags & WHERE_ONETABLE_ONLY) ? 1 : pTabList->nSrc;
+
/* Allocate and initialize the WhereInfo structure that will become the
** return value. A single allocation is used to store the WhereInfo
** struct, the contents of WhereInfo.a[], the WhereClause structure
@@ -3555,7 +3598,7 @@
** some architectures. Hence the ROUND8() below.
*/
db = pParse->db;
- nByteWInfo = ROUND8(sizeof(WhereInfo)+(pTabList->nSrc-1)*sizeof(WhereLevel));
+ nByteWInfo = ROUND8(sizeof(WhereInfo)+(nTabList-1)*sizeof(WhereLevel));
pWInfo = sqlite3DbMallocZero(db,
nByteWInfo +
sizeof(WhereClause) +
@@ -3564,7 +3607,7 @@
if( db->mallocFailed ){
goto whereBeginError;
}
- pWInfo->nLevel = pTabList->nSrc;
+ pWInfo->nLevel = nTabList;
pWInfo->pParse = pParse;
pWInfo->pTabList = pTabList;
pWInfo->iBreak = sqlite3VdbeMakeLabel(v);
@@ -3583,7 +3626,7 @@
/* Special case: a WHERE clause that is constant. Evaluate the
** expression and either jump over all of the code or fall thru.
*/
- if( pWhere && (pTabList->nSrc==0 || sqlite3ExprIsConstantNotJoin(pWhere)) ){
+ if( pWhere && (nTabList==0 || sqlite3ExprIsConstantNotJoin(pWhere)) ){
sqlite3ExprIfFalse(pParse, pWhere, pWInfo->iBreak, SQLITE_JUMPIFNULL);
pWhere = 0;
}
@@ -3603,6 +3646,11 @@
** to virtual table cursors are set. This is used to selectively disable
** the OR-to-IN transformation in exprAnalyzeOrTerm(). It is not helpful
** with virtual tables.
+ **
+ ** Note that bitmasks are created for all pTabList->nSrc tables in
+ ** pTabList, not just the first nTabList tables. nTabList is normally
+ ** equal to pTabList->nSrc but might be shortened to 1 if the
+ ** WHERE_ONETABLE_ONLY flag is set.
*/
assert( pWC->vmask==0 && pMaskSet->n==0 );
for(i=0; i<pTabList->nSrc; i++){
@@ -3654,7 +3702,7 @@
pLevel = pWInfo->a;
andFlags = ~0;
WHERETRACE(("*** Optimizer Start ***\n"));
- for(i=iFrom=0, pLevel=pWInfo->a; i<pTabList->nSrc; i++, pLevel++){
+ for(i=iFrom=0, pLevel=pWInfo->a; i<nTabList; i++, pLevel++){
WhereCost bestPlan; /* Most efficient plan seen so far */
Index *pIdx; /* Index for FROM table at pTabItem */
int j; /* For looping over FROM tables */
@@ -3699,8 +3747,8 @@
*/
for(isOptimal=1; isOptimal>=0 && bestJ<0; isOptimal--){
Bitmask mask = (isOptimal ? 0 : notReady);
- assert( (pTabList->nSrc-iFrom)>1 || isOptimal );
- for(j=iFrom, pTabItem=&pTabList->a[j]; j<pTabList->nSrc; j++, pTabItem++){
+ assert( (nTabList-iFrom)>1 || isOptimal );
+ for(j=iFrom, pTabItem=&pTabList->a[j]; j<nTabList; j++, pTabItem++){
int doNotReorder; /* True if this table should not be reordered */
WhereCost sCost; /* Cost information from best[Virtual]Index() */
ExprList *pOrderBy; /* ORDER BY clause for index to optimize */
@@ -3797,7 +3845,7 @@
** searching those tables.
*/
sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */
- for(i=0, pLevel=pWInfo->a; i<pTabList->nSrc; i++, pLevel++){
+ for(i=0, pLevel=pWInfo->a; i<nTabList; i++, pLevel++){
Table *pTab; /* Table to open */
int iDb; /* Index of database containing table/index */
@@ -3876,7 +3924,7 @@
** program.
*/
notReady = ~(Bitmask)0;
- for(i=0; i<pTabList->nSrc; i++){
+ for(i=0; i<nTabList; i++){
notReady = codeOneLoopStart(pWInfo, i, wctrlFlags, notReady);
pWInfo->iContinue = pWInfo->a[i].addrCont;
}
@@ -3888,7 +3936,7 @@
** the index is listed as "{}". If the primary key is used the
** index name is '*'.
*/
- for(i=0; i<pTabList->nSrc; i++){
+ for(i=0; i<nTabList; i++){
char *z;
int n;
pLevel = &pWInfo->a[i];
@@ -3956,7 +4004,7 @@
/* Generate loop termination code.
*/
sqlite3ExprCacheClear(pParse);
- for(i=pTabList->nSrc-1; i>=0; i--){
+ for(i=pWInfo->nLevel-1; i>=0; i--){
pLevel = &pWInfo->a[i];
sqlite3VdbeResolveLabel(v, pLevel->addrCont);
if( pLevel->op!=OP_Noop ){
@@ -4002,7 +4050,8 @@
/* Close all of the cursors that were opened by sqlite3WhereBegin.
*/
- for(i=0, pLevel=pWInfo->a; i<pTabList->nSrc; i++, pLevel++){
+ assert( pWInfo->nLevel==1 || pWInfo->nLevel==pTabList->nSrc );
+ for(i=0, pLevel=pWInfo->a; i<pWInfo->nLevel; i++, pLevel++){
struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom];
Table *pTab = pTabItem->pTab;
assert( pTab!=0 );