]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Update the WHERE clause processing infrastructure in preparation for adding
authordrh <drh@noemail.net>
Wed, 17 Dec 2008 19:22:15 +0000 (19:22 +0000)
committerdrh <drh@noemail.net>
Wed, 17 Dec 2008 19:22:15 +0000 (19:22 +0000)
multi-index OR evaluation. (CVS 6037)

FossilOrigin-Name: 78401b33febf678cfeec2a35514eb4172de420ab

manifest
manifest.uuid
src/sqliteInt.h
src/where.c

index d026825f191d88383f64aa45790975b170b857d9..abbe072de277dafcfa2b9384c340617928ef2008 100644 (file)
--- 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
index 3a1e45d72ee8ae2c27781dc90756de20e02678df..d9fc840f04d1e8ece8be0750d873375412a9361c 100644 (file)
@@ -1 +1 @@
-34b56600ec0c5cd7b5faab265750252bc9850e3e
\ No newline at end of file
+78401b33febf678cfeec2a35514eb4172de420ab
\ No newline at end of file
index c3177afa220a02ed476a1cf8885feadcea653dea..0fab2f5129318f7f76e963a8db0e5e6f0a76e53e 100644 (file)
@@ -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 */
index fa950be57e595412b04c1850f826d17dfc15eacc..1490b54b1ff23e8f9c2bfa232f81330f363af5d2 100644 (file)
@@ -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 <op> <expr>
 **
 ** where X is a column name and <op> 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 <op> 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 <op> <expr>) OR (t1.Y <op> <expr>) 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 <op> <expr>" */
-  int leftColumn;         /* Column number of X in "X <op> <expr>" */
+  union {
+    int leftColumn;         /* Column number of X in "X <op> <expr>" */
+    WhereOrInfo *pOrInfo;   /* Extra information if eOperator==WO_OR */
+    WhereAndInfo *pAndInfo; /* Extra information if eOperator==WO_AND */
+  } u;
   u16 eOperator;          /* A WO_xx value describing <op> */
   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<sOr.nTerm );
-      iColumn = sOr.a[j].leftColumn;
+      iColumn = sOr.a[j].u.leftColumn;
       iCursor = sOr.a[j].leftCursor;
       ok = iCursor>=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 rowid<EXPR eliminates two-thirds or rows */
+        cost /= 3;  /* Guess that rowid<EXPR eliminates two-thirds of rows */
       }
       if( findTerm(pWC, iCur, -1, notReady, WO_GT|WO_GE, 0) ){
         wsFlags |= WHERE_BTM_LIMIT;
@@ -1884,9 +1952,9 @@ static int codeEqualityTerm(
 ** of nEq including 0.  If nEq==0, this routine is nearly a no-op.
 ** The only thing it does is allocate the pLevel->iMem 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 );