From: drh Date: Wed, 17 Dec 2008 19:22:15 +0000 (+0000) Subject: Update the WHERE clause processing infrastructure in preparation for adding X-Git-Tag: version-3.6.10~147 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=700a22612b1e86b8ff22894c3258bb0951c81c19;p=thirdparty%2Fsqlite.git Update the WHERE clause processing infrastructure in preparation for adding multi-index OR evaluation. (CVS 6037) FossilOrigin-Name: 78401b33febf678cfeec2a35514eb4172de420ab --- diff --git a/manifest b/manifest index d026825f19..abbe072de2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\ssavepoint\sfeature.\sThis\sfeature\sis\slargely\suntested\sat\sthis\spoint.\s(CVS\s6036) -D 2008-12-17T17:30:26 +C Update\sthe\sWHERE\sclause\sprocessing\sinfrastructure\sin\spreparation\sfor\sadding\nmulti-index\sOR\sevaluation.\s(CVS\s6037) +D 2008-12-17T19:22:16 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in f7e4c81c347b04f7b0f1c1b081a168645d7b8af7 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -157,7 +157,7 @@ F src/select.c a4316c5e8a417687e159b3d3ae689363d1dec5df F src/shell.c 60638e2fdfe97f1eb9c18caf87d3744d8269d012 F src/sqlite.h.in 065a828e299960316aa34f05b9f0f10f33afe4c8 F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17 -F src/sqliteInt.h e26694bae99940ab603f30d15bf493d301d4e249 +F src/sqliteInt.h d7f8532c81038b1133d55c68f96afaf93ffb9138 F src/sqliteLimit.h f435e728c6b620ef7312814d660a81f9356eb5c8 F src/status.c 237b193efae0cf6ac3f0817a208de6c6c6ef6d76 F src/table.c 23db1e5f27c03160987c122a078b4bb51ef0b2f8 @@ -205,7 +205,7 @@ F src/vdbeblob.c b0dcebfafedcf9c0addc7901ad98f6f986c08935 F src/vdbemem.c f9c859ac17e2e05a0f249868ce4f191f69edd31d F src/vtab.c e39e011d7443a8d574b1b9cde207a35522e6df43 F src/walker.c 488c2660e13224ff70c0c82761118efb547f8f0d -F src/where.c 0ef44949222bc9ddcdecf4e44febb9f6dfae4411 +F src/where.c 685a1e8d2b84946a4804ae7973e4704f76a71ac6 F tclinstaller.tcl 4356d9d94d2b5ed5e68f9f0c80c4df3048dd7617 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 F test/alias.test 597662c5d777a122f9a3df0047ea5c5bd383a911 @@ -679,7 +679,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81 F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e -P 20a4ca5d361ecbb982129171f10cccac4f5ad093 -R 149ae663dd4ddd00dcd03e3c7806764e -U danielk1977 -Z d50bab95338b938b6f59c39d60f1fcf0 +P 34b56600ec0c5cd7b5faab265750252bc9850e3e +R f6c50ff68624e5438bfedc944eca4eae +U drh +Z 9d46e7b6dc85f65598c88f42e3cd2bc3 diff --git a/manifest.uuid b/manifest.uuid index 3a1e45d72e..d9fc840f04 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -34b56600ec0c5cd7b5faab265750252bc9850e3e \ No newline at end of file +78401b33febf678cfeec2a35514eb4172de420ab \ No newline at end of file diff --git a/src/sqliteInt.h b/src/sqliteInt.h index c3177afa22..0fab2f5129 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.810 2008/12/17 17:30:26 danielk1977 Exp $ +** @(#) $Id: sqliteInt.h,v 1.811 2008/12/17 19:22:16 drh Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -1539,11 +1539,10 @@ struct SrcList { ** */ struct WhereLevel { - int iFrom; /* Which entry in the FROM clause */ - int wsFlags; /* "Where-Scan" flags show the choosen scan strategy */ - int iMem; /* First memory cell used by this level */ + u32 wsFlags; /* "Where-Scan" flags show the choosen scan strategy */ int iLeftJoin; /* Memory cell used to implement LEFT OUTER JOIN */ Index *pIdx; /* Index used. NULL if no index */ + struct WhereTerm *pTerm; /* Where term containing OR clause */ int iTabCur; /* The VDBE cursor used to access the table */ int iIdxCur; /* The VDBE cursor used to access pIdx */ int addrBrk; /* Jump here to break out of the loop */ @@ -1552,8 +1551,9 @@ struct WhereLevel { int addrFirst; /* First instruction of interior of the loop */ int op, p1, p2; /* Opcode used to terminate the loop */ u8 p5; /* P5 operand of the opcode that terminates the loop */ - int nEq; /* Number of == or IN constraints on this loop */ - int nIn; /* Number of IN operators constraining this loop */ + u8 iFrom; /* Which entry in the FROM clause */ + u16 nEq; /* Number of == or IN constraints on this loop */ + u16 nIn; /* Number of IN operators constraining this loop */ struct InLoop { int iCur; /* The VDBE cursor used by this IN operator */ int addrInTop; /* Top of the IN loop */ diff --git a/src/where.c b/src/where.c index fa950be57e..1490b54b1f 100644 --- a/src/where.c +++ b/src/where.c @@ -16,7 +16,7 @@ ** so is applicable. Because this module is responsible for selecting ** indices, you might also think of this module as the "query optimizer". ** -** $Id: where.c,v 1.337 2008/12/12 17:56:16 drh Exp $ +** $Id: where.c,v 1.338 2008/12/17 19:22:16 drh Exp $ */ #include "sqliteInt.h" @@ -36,6 +36,8 @@ int sqlite3WhereTrace = 0; */ typedef struct WhereClause WhereClause; typedef struct ExprMaskSet ExprMaskSet; +typedef struct WhereOrInfo WhereOrInfo; +typedef struct WhereAndInfo WhereAndInfo; /* ** The query generator uses an array of instances of this structure to @@ -55,13 +57,26 @@ typedef struct ExprMaskSet ExprMaskSet; ** X ** ** where X is a column name and is one of certain operators, -** then WhereTerm.leftCursor and WhereTerm.leftColumn record the -** cursor number and column number for X. WhereTerm.operator records +** then WhereTerm.leftCursor and WhereTerm.u.leftColumn record the +** cursor number and column number for X. WhereTerm.eOperator records ** the using a bitmask encoding defined by WO_xxx below. The ** use of a bitmask encoding for the operator allows us to search ** quickly for terms that match any of several different operators. ** -** prereqRight and prereqAll record sets of cursor numbers, +** A WhereTerm might also be two or more subterms connected by OR: +** +** (t1.X ) OR (t1.Y ) OR .... +** +** In this second case, wtFlag as the TERM_ORINFO set and eOperator==WO_OR +** and the WhereTerm.u.pOrInfo field points to auxiliary information that +** is collected about the +** +** If a term in the WHERE clause does not match either of the two previous +** categories, then eOperator==0. The WhereTerm.pExpr field is still set +** to the original subexpression content and wtFlags is set up appropriately +** but no other fields in the WhereTerm object are meaningful. +** +** When eOperator!=0, prereqRight and prereqAll record sets of cursor numbers, ** but they do so indirectly. A single ExprMaskSet structure translates ** cursor number into bits and the translated bit is stored in the prereq ** fields. The translation is used in order to maximize the number of @@ -82,7 +97,11 @@ struct WhereTerm { Expr *pExpr; /* Pointer to the subexpression that is this term */ int iParent; /* Disable pWC->a[iParent] when this term disabled */ int leftCursor; /* Cursor number of X in "X " */ - int leftColumn; /* Column number of X in "X " */ + union { + int leftColumn; /* Column number of X in "X " */ + WhereOrInfo *pOrInfo; /* Extra information if eOperator==WO_OR */ + WhereAndInfo *pAndInfo; /* Extra information if eOperator==WO_AND */ + } u; u16 eOperator; /* A WO_xx value describing */ u8 wtFlags; /* TERM_xxx bit flags. See below */ u8 nChild; /* Number of children that must disable us */ @@ -98,7 +117,9 @@ struct WhereTerm { #define TERM_VIRTUAL 0x02 /* Added by the optimizer. Do not code */ #define TERM_CODED 0x04 /* This term is already coded */ #define TERM_COPIED 0x08 /* Has a child */ -#define TERM_OR_OK 0x10 /* Used during OR-clause processing */ +#define TERM_ORINFO 0x10 /* Need to free the WhereTerm.u.pOrInfo object */ +#define TERM_ANDINFO 0x20 /* Need to free the WhereTerm.u.pAndInfo obj */ +#define TERM_OR_OK 0x40 /* Used during OR-clause processing */ /* ** An instance of the following structure holds all information about a @@ -113,6 +134,25 @@ struct WhereClause { WhereTerm aStatic[4]; /* Initial static space for a[] */ }; +/* +** A WhereTerm with eOperator==WO_OR has its u.pOrInfo pointer set to +** a dynamically allocated instance of the following structure. +*/ +struct WhereOrInfo { + WhereClause wc; /* The OR subexpression broken out */ + double cost; /* Cost of evaluating this OR subexpression */ +}; + +/* +** A WhereTerm with eOperator==WO_AND has its u.pAndInfo pointer set to +** a dynamically allocated instance of the following structure. +*/ +struct WhereAndInfo { + WhereClause wc; /* The OR subexpression broken out */ + Index *pIdx; /* Index to use */ + double cost; /* Cost of evaluating this OR subexpression */ +}; + /* ** An instance of the following structure keeps track of a mapping ** between VDBE cursor numbers and bits of the bitmasks in WhereTerm. @@ -158,18 +198,20 @@ struct ExprMaskSet { #define WO_GE (WO_EQ<<(TK_GE-TK_EQ)) #define WO_MATCH 0x040 #define WO_ISNULL 0x080 -#define WO_OR 0x100 +#define WO_OR 0x100 /* Two or more OR-connected terms */ +#define WO_AND 0x200 /* Two or more AND-connected terms */ #define WO_ALL 0xfff /* Mask of all possible WO_* values */ /* -** Value for wsFlags returned by bestIndex(). These flags determine which -** search strategies are appropriate. +** 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.wtFlags field is usually set to WO_IN|WO_EQ|WO_ISNULL. -** But if the table is the right table of a left join, WhereLevel.wtFlags -** is set to WO_IN|WO_EQ. The WhereLevel.wtFlags field can then be used as +** 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. @@ -203,6 +245,27 @@ static void whereClauseInit( pWC->a = pWC->aStatic; } +/* Forward reference */ +static void whereClauseClear(WhereClause*); + +/* +** Deallocate all memory associated with a WhereOrInfo object. +*/ +static void whereOrInfoDelete(sqlite3 *db, WhereOrInfo *p){ + if( p ){ + whereClauseClear(&p->wc); + } +} + +/* +** Deallocate all memory associated with a WhereAndInfo object. +*/ +static void whereAndInfoDelete(sqlite3 *db, WhereAndInfo *p){ + if( p ){ + whereClauseClear(&p->wc); + } +} + /* ** Deallocate a WhereClause structure. The WhereClause structure ** itself is not freed. This routine is the inverse of whereClauseInit(). @@ -215,6 +278,11 @@ static void whereClauseClear(WhereClause *pWC){ if( a->wtFlags & TERM_DYNAMIC ){ sqlite3ExprDelete(db, a->pExpr); } + if( a->wtFlags & TERM_ORINFO ){ + whereOrInfoDelete(db, a->u.pOrInfo); + }else if( a->wtFlags & TERM_ANDINFO ){ + whereAndInfoDelete(db, a->u.pAndInfo); + } } if( pWC->a!=pWC->aStatic ){ sqlite3DbFree(db, pWC->a); @@ -477,7 +545,7 @@ static WhereTerm *findTerm( for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){ if( pTerm->leftCursor==iCur && (pTerm->prereqRight & notReady)==0 - && pTerm->leftColumn==iColumn + && pTerm->u.leftColumn==iColumn && (pTerm->eOperator & op)!=0 ){ if( pIdx && pTerm->eOperator!=WO_ISNULL ){ @@ -666,7 +734,7 @@ static int orTermIsOptCandidate(WhereTerm *pOrTerm, int iCursor, int iColumn){ if( pOrTerm->leftCursor!=iCursor ){ return 0; } - if( pOrTerm->leftColumn!=iColumn ){ + if( pOrTerm->u.leftColumn!=iColumn ){ return 0; } affRight = sqlite3ExprAffinity(pOrTerm->pExpr->pRight); @@ -785,7 +853,7 @@ static void exprAnalyze( Expr *pRight = pExpr->pRight; if( pLeft->op==TK_COLUMN ){ pTerm->leftCursor = pLeft->iTable; - pTerm->leftColumn = pLeft->iColumn; + pTerm->u.leftColumn = pLeft->iColumn; pTerm->eOperator = operatorMask(op); } if( pRight && pRight->op==TK_COLUMN ){ @@ -812,7 +880,7 @@ static void exprAnalyze( exprCommute(pParse, pDup); pLeft = pDup->pLeft; pNew->leftCursor = pLeft->iTable; - pNew->leftColumn = pLeft->iColumn; + pNew->u.leftColumn = pLeft->iColumn; pNew->prereqRight = prereqLeft; pNew->prereqAll = prereqAll; pNew->eOperator = operatorMask(pDup->op); @@ -873,7 +941,7 @@ static void exprAnalyze( if( db->mallocFailed ) goto or_not_possible; do{ assert( j=0; for(i=sOr.nTerm-1, pOrTerm=sOr.a; i>=0 && ok; i--, pOrTerm++){ @@ -1000,7 +1068,7 @@ or_not_possible: pNewTerm = &pWC->a[idxNew]; pNewTerm->prereqRight = prereqExpr; pNewTerm->leftCursor = pLeft->iTable; - pNewTerm->leftColumn = pLeft->iColumn; + pNewTerm->u.leftColumn = pLeft->iColumn; pNewTerm->eOperator = WO_MATCH; pNewTerm->iParent = idxTerm; pTerm = &pWC->a[idxTerm]; @@ -1362,7 +1430,7 @@ static double bestVirtualIndex( testcase( pTerm->eOperator==WO_IN ); testcase( pTerm->eOperator==WO_ISNULL ); if( pTerm->eOperator & (WO_IN|WO_ISNULL) ) continue; - pIdxCons[j].iColumn = pTerm->leftColumn; + pIdxCons[j].iColumn = pTerm->u.leftColumn; pIdxCons[j].iTermOffset = i; pIdxCons[j].op = (u8)pTerm->eOperator; /* The direct assignment in the previous line is possible only because @@ -1594,7 +1662,7 @@ static double bestIndex( if( pTerm ){ if( findTerm(pWC, iCur, -1, notReady, WO_LT|WO_LE, 0) ){ wsFlags |= WHERE_TOP_LIMIT; - cost /= 3; /* Guess that rowidiMem memory cell. ** -** This routine always allocates at least one memory cell and puts -** the address of that memory cell in pLevel->iMem. The code that -** calls this routine will use pLevel->iMem to store the termination +** This routine always allocates at least one memory cell and returns +** the index of that memory cell. The code that +** calls this routine will use that memory cell to store the termination ** key value of the loop. If one or more IN operators appear, then ** this routine allocates an additional nEq memory cells for internal ** use. @@ -1911,9 +1979,8 @@ static int codeAllEqualityTerms( ** value. If there are IN operators we'll need one for each == or ** IN constraint. */ - pLevel->iMem = pParse->nMem + 1; - regBase = pParse->nMem + 2; - pParse->nMem += pLevel->nEq + 2 + nExtraReg; + regBase = pParse->nMem + 1; + pParse->nMem += pLevel->nEq + 1 + nExtraReg; /* Evaluate the equality constraints */ @@ -2480,6 +2547,7 @@ WhereInfo *sqlite3WhereBegin( */ int testOp = OP_Noop; int start; + int memEndValue = 0; WhereTerm *pStart, *pEnd; assert( omitTable==0 ); @@ -2524,8 +2592,8 @@ WhereInfo *sqlite3WhereBegin( pX = pEnd->pExpr; assert( pX!=0 ); assert( pEnd->leftCursor==iCur ); - pLevel->iMem = ++pParse->nMem; - sqlite3ExprCode(pParse, pX->pRight, pLevel->iMem); + memEndValue = ++pParse->nMem; + sqlite3ExprCode(pParse, pX->pRight, memEndValue); if( pX->op==TK_LT || pX->op==TK_GT ){ testOp = bRev ? OP_Le : OP_Ge; }else{ @@ -2540,8 +2608,7 @@ WhereInfo *sqlite3WhereBegin( if( testOp!=OP_Noop ){ int r1 = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp2(v, OP_Rowid, iCur, r1); - /* sqlite3VdbeAddOp2(v, OP_SCopy, pLevel->iMem, 0); */ - sqlite3VdbeAddOp3(v, testOp, pLevel->iMem, addrBrk, r1); + sqlite3VdbeAddOp3(v, testOp, memEndValue, addrBrk, r1); sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC | SQLITE_JUMPIFNULL); sqlite3ReleaseTempReg(pParse, r1); } @@ -2734,8 +2801,36 @@ WhereInfo *sqlite3WhereBegin( pLevel->p1 = iIdxCur; disableTerm(pLevel, pRangeStart); disableTerm(pLevel, pRangeEnd); + }else if( pLevel->wsFlags & WHERE_MULTI_OR ){ + /* Case 4: Two or more separately indexed terms connected by OR + ** + ** Example: + ** + ** CREATE TABLE t1(a,b,c,d); + ** CREATE INDEX i1 ON t1(a); + ** CREATE INDEX i2 ON t1(b); + ** CREATE INDEX i3 ON t1(c); + ** + ** SELECT * FROM t1 WHERE a=5 OR b=7 OR (c=11 AND d=13) + ** + ** In the example, there are three indexed terms connected by OR. + ** The top of the loop is constructed by creating a RowSet object + ** and populating it. Then looping over elements of the rowset. + ** + ** Null 1 + ** # fill RowSet 1 with entries where a=5 using i1 + ** # fill Rowset 1 with entries where b=7 using i2 + ** # fill Rowset 1 with entries where c=11 and d=13 i3 and t1 + ** A: RowSetRead 1, B, 2 + ** Seek i, 2 + ** + ** The bottom of the loop looks like this: + ** + ** C: Goto 0, A + ** B: + */ }else{ - /* Case 4: There is no usable index. We must do a complete + /* Case 5: There is no usable index. We must do a complete ** scan of the entire table. */ assert( omitTable==0 );