]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
A new implementation of indexing with the IS operator that works correctly
authordrh <drh@noemail.net>
Thu, 14 May 2015 01:05:25 +0000 (01:05 +0000)
committerdrh <drh@noemail.net>
Thu, 14 May 2015 01:05:25 +0000 (01:05 +0000)
when the IS operator is in the WHERE clause and the operands are from
opposite sides of a LEFT JOIN.

FossilOrigin-Name: 4541688b3f56f5cd3d5b299594b58c577ad633bb

manifest
manifest.uuid
src/where.c
src/whereInt.h
test/where4.test

index 88aafccee73c4309c1b7404cd96df0bb4f7e4157..438ec2311050ba3b65ef489ffeb74b10c77120fb 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Simplified\simplementation\sof\sindexing\swith\sthe\sIS\soperator.
-D 2015-05-13T19:33:41.990
+C A\snew\simplementation\sof\sindexing\swith\sthe\sIS\soperator\sthat\sworks\scorrectly\nwhen\sthe\sIS\soperator\sis\sin\sthe\sWHERE\sclause\sand\sthe\soperands\sare\sfrom\s\nopposite\ssides\sof\sa\sLEFT\sJOIN.
+D 2015-05-14T01:05:25.274
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in edfc69769e613a6359c42c06ea1d42c3bece1736
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -307,8 +307,8 @@ F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb
 F src/wal.c ce2cb2d06faab54d1bce3e739bec79e063dd9113
 F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4
 F src/walker.c c253b95b4ee44b21c406e2a1052636c31ea27804
-F src/where.c fa61aca146422a2a056aeb0381a6bb1c37d46c07
-F src/whereInt.h 31d30e83621c21358d031510d5c6a3e474cd8bb7
+F src/where.c 64afb483fe8bede1e31e6c66bc532530a4600543
+F src/whereInt.h a6f5a762bc1b4b1c76e1cea79976b437ac35a435
 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
 F test/aggnested.test b35b4cd69fc913f90d39a575e171e1116c3a4bb7
@@ -1168,7 +1168,7 @@ F test/walthread.test de8dbaf6d9e41481c460ba31ca61e163d7348f8e
 F test/where.test 1ff3d9f8da0a6c0dc5ccfd38d9225b2cdb5b6afb
 F test/where2.test 23fdb5d8e756554aad4ca7ae03de9dd8367a2c6e
 F test/where3.test 1ad55ba900bd7747f98b6082e65bd3e442c5004e
-F test/where4.test d8420ceeb8323a41ceff1f1841fc528e824e1ecf
+F test/where4.test a4603fa0d018bd4b9430dac840c9c522af421dd5
 F test/where5.test fdf66f96d29a064b63eb543e28da4dfdccd81ad2
 F test/where6.test 5da5a98cec820d488e82708301b96cb8c18a258b
 F test/where7.test 5a4b0abc207d71da4deecd734ad8579e8dd40aa8
@@ -1258,7 +1258,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh 0abfd78ceb09b7f7c27c688c8e3fe93268a13b32
 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 24263d08b11c88416d270013bdaf5ff45125cb4d
-R 16be24e7bac77474bad7ab9d3d121f18
+P 95b1f9bf14e490c6c6bba9ea78aeab712a44aab5
+R 6e0cd1da1bcd5d739d5d402ab5201f6c
 U drh
-Z 3bc6b705447a86373e017a72613abb32
+Z 3608a708c2f560b3e002545b6da23dda
index 660fdcd24793ec4d4a293cbe669c563b39dcfdbd..2dcd8f706481b80303e2104fe584c70c0b98a698 100644 (file)
@@ -1 +1 @@
-95b1f9bf14e490c6c6bba9ea78aeab712a44aab5
\ No newline at end of file
+4541688b3f56f5cd3d5b299594b58c577ad633bb
\ No newline at end of file
index aad9799211cdb93b7551391e76fad9ea4099ed3b..3c96f93a7ee80780c08f2c66c954d40b4d62c1fa 100644 (file)
@@ -417,7 +417,7 @@ static u16 operatorMask(int op){
   }else if( op==TK_ISNULL ){
     c = WO_ISNULL;
   }else if( op==TK_IS ){
-    c = WO_EQ;  /* IS works like ==, just without the IsNull tests */
+    c = WO_IS;
   }else{
     assert( (WO_EQ<<(op-TK_EQ)) < 0x7fff );
     c = (u16)(WO_EQ<<(op-TK_EQ));
@@ -429,7 +429,7 @@ static u16 operatorMask(int op){
   assert( op!=TK_LE || c==WO_LE );
   assert( op!=TK_GT || c==WO_GT );
   assert( op!=TK_GE || c==WO_GE );
-  assert( op!=TK_IS || c==WO_EQ );
+  assert( op!=TK_IS || c==WO_IS );
   return c;
 }
 
@@ -490,11 +490,12 @@ static WhereTerm *whereScanNext(WhereScan *pScan){
                 continue;
               }
             }
-            if( (pTerm->eOperator & WO_EQ)!=0
+            if( (pTerm->eOperator & (WO_EQ|WO_IS))!=0
              && (pX = pTerm->pExpr->pRight)->op==TK_COLUMN
              && pX->iTable==pScan->aEquiv[0]
              && pX->iColumn==pScan->aEquiv[1]
             ){
+              testcase( pTerm->eOperator & WO_IS );
               continue;
             }
             pScan->k = k+1;
@@ -596,9 +597,11 @@ static WhereTerm *findTerm(
   WhereScan scan;
 
   p = whereScanInit(&scan, pWC, iCur, iColumn, op, pIdx);
+  op &= WO_EQ|WO_IS;
   while( p ){
     if( (p->prereqRight & notReady)==0 ){
-      if( p->prereqRight==0 && (p->eOperator&WO_EQ)!=0 ){
+      if( p->prereqRight==0 && (p->eOperator&op)!=0 ){
+        testcase( p->eOperator & WO_IS );
         return p;
       }
       if( pResult==0 ) pResult = p;
@@ -1679,7 +1682,7 @@ static int termCanDriveIndex(
 ){
   char aff;
   if( pTerm->leftCursor!=pSrc->iCursor ) return 0;
-  if( (pTerm->eOperator & WO_EQ)==0 ) return 0;
+  if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) return 0;
   if( (pTerm->prereqRight & notReady)!=0 ) return 0;
   if( pTerm->u.leftColumn<0 ) return 0;
   aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity;
@@ -1955,7 +1958,7 @@ static sqlite3_index_info *allocateIndexInfo(
     testcase( pTerm->eOperator & WO_IN );
     testcase( pTerm->eOperator & WO_ISNULL );
     testcase( pTerm->eOperator & WO_ALL );
-    if( (pTerm->eOperator & ~(WO_ISNULL|WO_EQUIV))==0 ) continue;
+    if( (pTerm->eOperator & ~(WO_ISNULL|WO_EQUIV|WO_IS))==0 ) continue;
     if( pTerm->wtFlags & TERM_VNULL ) continue;
     pIdxCons[j].iColumn = pTerm->u.leftColumn;
     pIdxCons[j].iTermOffset = i;
@@ -4104,16 +4107,18 @@ static Bitmask codeOneLoopStart(
     Expr *pE, *pEAlt;
     WhereTerm *pAlt;
     if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
-    if( pTerm->eOperator!=(WO_EQUIV|WO_EQ) ) continue;
+    if( (pTerm->eOperator&(WO_EQUIV|WO_EQ|WO_IS))<=WO_EQUIV ) continue;
     if( pTerm->leftCursor!=iCur ) continue;
     if( pLevel->iLeftJoin ) continue;
     pE = pTerm->pExpr;
     assert( !ExprHasProperty(pE, EP_FromJoin) );
     assert( (pTerm->prereqRight & pLevel->notReady)!=0 );
-    pAlt = findTerm(pWC, iCur, pTerm->u.leftColumn, notReady, WO_EQ|WO_IN, 0);
+    pAlt = findTerm(pWC, iCur, pTerm->u.leftColumn, notReady,
+                    WO_EQ|WO_IN|WO_IS, 0);
     if( pAlt==0 ) continue;
     if( pAlt->wtFlags & (TERM_CODED) ) continue;
-    testcase( (pAlt->eOperator & WO_EQ)!=0 && pAlt->pExpr->op==TK_IS );
+    testcase( pAlt->eOperator & WO_EQ );
+    testcase( pAlt->eOperator & WO_IS );
     testcase( pAlt->eOperator & WO_IN );
     VdbeModuleComment((v, "begin transitive constraint"));
     pEAlt = sqlite3StackAllocRaw(db, sizeof(*pEAlt));
@@ -4656,7 +4661,7 @@ static void whereLoopOutputAdjust(
         /* In the absence of explicit truth probabilities, use heuristics to
         ** guess a reasonable truth probability. */
         pLoop->nOut--;
-        if( pTerm->eOperator&WO_EQ ){
+        if( pTerm->eOperator&(WO_EQ|WO_IS) ){
           Expr *pRight = pTerm->pExpr->pRight;
           testcase( pTerm->pExpr->op==TK_IS );
           if( sqlite3ExprIsInteger(pRight, &k) && k>=(-1) && k<=1 ){
@@ -4729,7 +4734,7 @@ static int whereLoopAddBtreeIndex(
   }else if( pProbe->tnum<=0 || (pSrc->jointype & JT_LEFT)!=0 ){
     opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE;
   }else{
-    opMask = WO_EQ|WO_IN|WO_ISNULL|WO_GT|WO_GE|WO_LT|WO_LE;
+    opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS;
   }
   if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
 
@@ -4792,7 +4797,7 @@ static int whereLoopAddBtreeIndex(
       assert( nIn>0 );  /* RHS always has 2 or more terms...  The parser
                         ** changes "x IN (?)" into "x=?". */
 
-    }else if( eOp & (WO_EQ) ){
+    }else if( eOp & (WO_EQ|WO_IS) ){
       pNew->wsFlags |= WHERE_COLUMN_EQ;
       if( iCol<0 || (nInMul==0 && pNew->u.btree.nEq==pProbe->nKeyCol-1) ){
         if( iCol>=0 && pProbe->uniqNotNull==0 ){
@@ -4842,7 +4847,7 @@ static int whereLoopAddBtreeIndex(
       whereRangeScanEst(pParse, pBuilder, pBtm, pTop, pNew);
     }else{
       int nEq = ++pNew->u.btree.nEq;
-      assert( eOp & (WO_ISNULL|WO_EQ|WO_IN) );
+      assert( eOp & (WO_ISNULL|WO_EQ|WO_IN|WO_IS) );
 
       assert( pNew->nOut==saved_nOut );
       if( pTerm->truthProb<=0 && iCol>=0 ){
@@ -4859,8 +4864,9 @@ static int whereLoopAddBtreeIndex(
          && ((eOp & WO_IN)==0 || !ExprHasProperty(pTerm->pExpr, EP_xIsSelect))
         ){
           Expr *pExpr = pTerm->pExpr;
-          if( (eOp & (WO_EQ|WO_ISNULL))!=0 ){
-            testcase( (eOp & WO_EQ)!=0 && pExpr->op==TK_IS );
+          if( (eOp & (WO_EQ|WO_ISNULL|WO_IS))!=0 ){
+            testcase( eOp & WO_EQ );
+            testcase( eOp & WO_IS );
             testcase( eOp & WO_ISNULL );
             rc = whereEqualScanEst(pParse, pBuilder, pExpr->pRight, &nOut);
           }else{
@@ -5697,9 +5703,9 @@ static i8 wherePathSatisfiesOrderBy(
       if( pOBExpr->op!=TK_COLUMN ) continue;
       if( pOBExpr->iTable!=iCur ) continue;
       pTerm = findTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn,
-                       ~ready, WO_EQ|WO_ISNULL, 0);
+                       ~ready, WO_EQ|WO_ISNULL|WO_IS, 0);
       if( pTerm==0 ) continue;
-      if( (pTerm->eOperator&WO_EQ)!=0 && pOBExpr->iColumn>=0 ){
+      if( (pTerm->eOperator&(WO_EQ|WO_IS))!=0 && pOBExpr->iColumn>=0 ){
         const char *z1, *z2;
         pColl = sqlite3ExprCollSeq(pWInfo->pParse, pOrderBy->a[i].pExpr);
         if( !pColl ) pColl = db->pDfltColl;
@@ -5739,7 +5745,7 @@ static i8 wherePathSatisfiesOrderBy(
         /* Skip over == and IS NULL terms */
         if( j<pLoop->u.btree.nEq
          && pLoop->nSkip==0
-         && ((i = pLoop->aLTerm[j]->eOperator) & (WO_EQ|WO_ISNULL))!=0
+         && ((i = pLoop->aLTerm[j]->eOperator) & (WO_EQ|WO_ISNULL|WO_IS))!=0
         ){
           if( i & WO_ISNULL ){
             testcase( isOrderDistinct );
@@ -6312,8 +6318,9 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){
   pLoop = pBuilder->pNew;
   pLoop->wsFlags = 0;
   pLoop->nSkip = 0;
-  pTerm = findTerm(pWC, iCur, -1, 0, WO_EQ, 0);
+  pTerm = findTerm(pWC, iCur, -1, 0, WO_EQ|WO_IS, 0);
   if( pTerm ){
+    testcase( pTerm->eOperator & WO_IS );
     pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_IPK|WHERE_ONEROW;
     pLoop->aLTerm[0] = pTerm;
     pLoop->nLTerm = 1;
@@ -6328,10 +6335,10 @@ static int whereShortCut(WhereLoopBuilder *pBuilder){
        || pIdx->nKeyCol>ArraySize(pLoop->aLTermSpace) 
       ) continue;
       for(j=0; j<pIdx->nKeyCol; j++){
-        pTerm = findTerm(pWC, iCur, pIdx->aiColumn[j], 0, WO_EQ, pIdx);
+        pTerm = findTerm(pWC, iCur, pIdx->aiColumn[j], 0, WO_EQ|WO_IS, pIdx);
         if( pTerm==0 ) break;
+         testcase( pTerm->eOperator & WO_IS );
         pLoop->aLTerm[j] = pTerm;
-        testcase( pTerm->pExpr->op==TK_IS );
       }
       if( j!=pIdx->nKeyCol ) continue;
       pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_ONEROW|WHERE_INDEXED;
index e7730d71edc78db7215f56e148c1aac76ab227b2..3a5a48e8400cb73837e35d7eab927915d46fd6ef 100644 (file)
@@ -429,21 +429,22 @@ struct WhereInfo {
 ** OR-ed combination of these values can be used when searching for
 ** particular WhereTerms within a WhereClause.
 */
-#define WO_IN     0x001
-#define WO_EQ     0x002                      /* Used for both == and IS */
+#define WO_IN     0x0001
+#define WO_EQ     0x0002
 #define WO_LT     (WO_EQ<<(TK_LT-TK_EQ))
 #define WO_LE     (WO_EQ<<(TK_LE-TK_EQ))
 #define WO_GT     (WO_EQ<<(TK_GT-TK_EQ))
 #define WO_GE     (WO_EQ<<(TK_GE-TK_EQ))
-#define WO_MATCH  0x040
-#define WO_ISNULL 0x080
-#define WO_OR     0x100       /* Two or more OR-connected terms */
-#define WO_AND    0x200       /* Two or more AND-connected terms */
-#define WO_EQUIV  0x400       /* Of the form A==B, both columns */
-#define WO_NOOP   0x800       /* This term does not restrict search space */
+#define WO_MATCH  0x0040
+#define WO_IS     0x0080
+#define WO_ISNULL 0x0100
+#define WO_OR     0x0200       /* Two or more OR-connected terms */
+#define WO_AND    0x0400       /* Two or more AND-connected terms */
+#define WO_EQUIV  0x0800       /* Of the form A==B, both columns */
+#define WO_NOOP   0x1000       /* This term does not restrict search space */
 
-#define WO_ALL    0xfff       /* Mask of all possible WO_* values */
-#define WO_SINGLE 0x0ff       /* Mask of all non-compound WO_* values */
+#define WO_ALL    0x1fff       /* Mask of all possible WO_* values */
+#define WO_SINGLE 0x01ff       /* Mask of all non-compound WO_* values */
 
 /*
 ** These are definitions of bits in the WhereLoop.wsFlags field.
index a26e9ad35528b9531850e3193b231a23c6ba2ca9..6bceb9174b93e32b615eca2d8d9032469ce32ba5 100644 (file)
@@ -57,6 +57,10 @@ proc count sql {
 do_test where4-1.1 {
   count {SELECT rowid FROM t1 WHERE w IS NULL}
 } {7 2}
+do_test where4-1.1b {
+  unset -nocomplain null
+  count {SELECT rowid FROM t1 WHERE w IS $null}
+} {7 2}
 do_test where4-1.2 {
   count {SELECT rowid FROM t1 WHERE +w IS NULL}
 } {7 6}
@@ -143,6 +147,17 @@ do_test where4-3.2 {
     SELECT * FROM t2 LEFT JOIN t3 ON a=x WHERE y IS NULL;
   }
 } {2 2 {} 3 {} {}}
+do_test where4-3.3 {
+  execsql {
+    SELECT * FROM t2 LEFT JOIN t3 ON a=x WHERE NULL is y;
+  }
+} {2 2 {} 3 {} {}}
+do_test where4-3.4 {
+  unset -nocomplain null
+  execsql {
+    SELECT * FROM t2 LEFT JOIN t3 ON a=x WHERE y IS $null;
+  }
+} {2 2 {} 3 {} {}}
 
 # Ticket #2189.  Probably the same bug as #2177.
 #