]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix the sqlite3FindInIndex() to ensure that it always uses a prefix of
authordrh <drh@noemail.net>
Fri, 26 Aug 2016 21:15:35 +0000 (21:15 +0000)
committerdrh <drh@noemail.net>
Fri, 26 Aug 2016 21:15:35 +0000 (21:15 +0000)
the index and uses no repeated columns.  Enhanced comments.

FossilOrigin-Name: b9fc89e432fbe4e5b41959a42797641907e075e3

manifest
manifest.uuid
src/expr.c

index e112f421dc718f26cb549f7e64dd3c5d22ef7175..05accaa53d60d98f165811243f8f885f45ad2d5e 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\san\sEXPLAIN\sQUERY\sPLAN\sline\sfor\swhen\sa\sindex\sis\sused\sto\simplement\nan\sIN\soperator.
-D 2016-08-26T19:54:12.433
+C Fix\sthe\ssqlite3FindInIndex()\sto\sensure\sthat\sit\salways\suses\sa\sprefix\sof\nthe\sindex\sand\suses\sno\srepeated\scolumns.\s\sEnhanced\scomments.
+D 2016-08-26T21:15:35.199
 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088
 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
 F Makefile.msc 5017381e4853b1472e01d5bb926be1268eba429c
@@ -338,7 +338,7 @@ F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7
 F src/date.c 95c9a8d00767e7221a8e9a31f4e913fc8029bf6b
 F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d
 F src/delete.c 76c084f0265f4a3cd1ecf17eee112a94f1ccbc05
-F src/expr.c 935366a02ad0c33e7cd19ff382ef9bcdfa1aa7a9
+F src/expr.c 4c80148f83127862f69ff509fb0aa261396df24e
 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
 F src/fkey.c e2be0968c1adc679c87e467aa5b4f167588f38a8
 F src/func.c 29cc9acb170ec1387b9f63eb52cd85f8de96c771
@@ -1521,7 +1521,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 078bb69e99891ba3b76a39ac974990714c43908f
-R 155df167db2a04d38d0a374ab683c0e3
+P 171aa833a2e1650c3d9cf9bd6438ae46f6c35871
+R 94dfe8764b4b5e69457ee17a8ecdc781
 U drh
-Z d9ed9721b1e3c6348d8b59f53fc4268d
+Z ef81f77fdd077e1b395585c253a1bf6f
index 5d4390647562db34db2411fdcac9124732f3c8f4..e5a0ab8485f840b7f4b3b408dd6f5fa13e1527df 100644 (file)
@@ -1 +1 @@
-171aa833a2e1650c3d9cf9bd6438ae46f6c35871
\ No newline at end of file
+b9fc89e432fbe4e5b41959a42797641907e075e3
\ No newline at end of file
index 08b72fed802fe87db2c8e0e293cd7522b76877d0..8ede47731f594514897baca6011d173d6efd7c82 100644 (file)
@@ -1960,7 +1960,6 @@ static Select *isCandidateForInOpt(Expr *pX){
   if( IsVirtual(pTab) ) return 0;        /* FROM clause not a virtual table */
   pEList = p->pEList;
   assert( pEList!=0 );
-
   /* All SELECT results must be columns. */
   for(i=0; i<pEList->nExpr; i++){
     Expr *pRes = pEList->a[i].pExpr;
@@ -2150,11 +2149,7 @@ int sqlite3FindInIndex(
     sqlite3CodeVerifySchema(pParse, iDb);
     sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
 
-    /* This function is only called from two places. In both cases the vdbe
-    ** has already been allocated. So assume sqlite3GetVdbe() is always
-    ** successful here.
-    */
-    assert(v);
+    assert(v);  /* sqlite3GetVdbe() has always been previously called */
     if( nExpr==1 && pEList->a[0].pExpr->iColumn<0 ){
       /* The "x IN (SELECT rowid FROM table)" case */
       int iAddr = sqlite3CodeOnce(pParse);
@@ -2195,67 +2190,79 @@ int sqlite3FindInIndex(
         }
       }
 
-      /* The collation sequence used by the comparison. If an index is to
-      ** be used in place of a temp-table, it must be ordered according
-      ** to this collation sequence.  */
-
-      for(pIdx=pTab->pIndex; pIdx && eType==0 && affinity_ok; pIdx=pIdx->pNext){
-        if( pIdx->nColumn<nExpr ) continue;
-        if( mustBeUnique ){
-          if( pIdx->nKeyCol>nExpr
-           ||(pIdx->nColumn>nExpr && !IsUniqueIndex(pIdx))
-          ){
-            continue;
+      if( affinity_ok ){
+        /* Search for an existing index that will work for this IN operator */
+        for(pIdx=pTab->pIndex; pIdx && eType==0; pIdx=pIdx->pNext){
+          Bitmask colUsed;      /* Columns of the index used */
+          Bitmask mCol;         /* Mask for the current column */
+          if( pIdx->nColumn<nExpr ) continue;
+          /* Maximum nColumn is BMS-2, not BMS-1, so that we can compute
+          ** BITMASK(nExpr) without overflowing */
+          testcase( pIdx->nColumn==BMS-2 );
+          testcase( pIdx->nColumn==BMS-1 );
+          if( pIdx->nColumn>=BMS-1 ) continue;
+          if( mustBeUnique ){
+            if( pIdx->nKeyCol>nExpr
+             ||(pIdx->nColumn>nExpr && !IsUniqueIndex(pIdx))
+            ){
+              continue;  /* This index is not unique over the IN RHS columns */
+            }
           }
-        }
-
-        for(i=0; i<nExpr; i++){
-          Expr *pLhs = sqlite3VectorFieldSubexpr(pX->pLeft, i);
-          Expr *pRhs = pEList->a[i].pExpr;
-          CollSeq *pReq = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs);
-          int j;
-
-          assert( pReq!=0 || pRhs->iColumn==XN_ROWID || pParse->nErr );
-          for(j=0; j<nExpr; j++){
-            if( pIdx->aiColumn[j]!=pRhs->iColumn ) continue;
-            assert( pIdx->azColl[j] );
-            if( pReq==0 ) continue;
-            if( sqlite3StrICmp(pReq->zName, pIdx->azColl[j])!=0 ) continue;
-            break;
+  
+          colUsed = 0;   /* Columns of index used so far */
+          for(i=0; i<nExpr; i++){
+            Expr *pLhs = sqlite3VectorFieldSubexpr(pX->pLeft, i);
+            Expr *pRhs = pEList->a[i].pExpr;
+            CollSeq *pReq = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs);
+            int j;
+  
+            assert( pReq!=0 || pRhs->iColumn==XN_ROWID || pParse->nErr );
+            for(j=0; j<nExpr; j++){
+              if( pIdx->aiColumn[j]!=pRhs->iColumn ) continue;
+              assert( pIdx->azColl[j] );
+              if( pReq==0 ) continue;
+              if( sqlite3StrICmp(pReq->zName, pIdx->azColl[j])!=0 ) continue;
+              break;
+            }
+            if( j==nExpr ) break;
+            mCol = MASKBIT(j);
+            if( mCol & colUsed ) break; /* Each column used only once */
+            colUsed |= mCol;
+            if( aiMap ) aiMap[i] = j;
           }
-          if( j==nExpr ) break;
-          if( aiMap ) aiMap[i] = j;
-        }
-
-        if( i==nExpr ){
-          int iAddr = sqlite3CodeOnce(pParse); VdbeCoverage(v);
-#ifndef SQLITE_OMIT_EXPLAIN
-          sqlite3VdbeAddOp4(v, OP_Explain, 0, 0, 0,
-            sqlite3MPrintf(db, "USING INDEX %s FOR IN-OPERATOR", pIdx->zName),
-            P4_DYNAMIC);
-#endif
-          sqlite3VdbeAddOp3(v, OP_OpenRead, iTab, pIdx->tnum, iDb);
-          sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
-          VdbeComment((v, "%s", pIdx->zName));
-          assert( IN_INDEX_INDEX_DESC == IN_INDEX_INDEX_ASC+1 );
-          eType = IN_INDEX_INDEX_ASC + pIdx->aSortOrder[0];
-
-          if( prRhsHasNull ){
-            *prRhsHasNull = ++pParse->nMem;
-#ifdef SQLITE_ENABLE_COLUMN_USED_MASK
-            i64 mask = (1<<nExpr)-1;
-            sqlite3VdbeAddOp4Dup8(v, OP_ColumnsUsed, 
-                iTab, 0, 0, (u8*)&mask, P4_INT64);
-#endif
-            if( nExpr==1 ){
-              sqlite3SetHasNullFlag(v, iTab, *prRhsHasNull);
+  
+          assert( i==nExpr || colUsed!=(MASKBIT(nExpr)-1) );
+          if( colUsed==(MASKBIT(nExpr)-1) ){
+            /* If we reach this point, that means the index pIdx is usable */
+            int iAddr = sqlite3CodeOnce(pParse); VdbeCoverage(v);
+  #ifndef SQLITE_OMIT_EXPLAIN
+            sqlite3VdbeAddOp4(v, OP_Explain, 0, 0, 0,
+              sqlite3MPrintf(db, "USING INDEX %s FOR IN-OPERATOR",pIdx->zName),
+              P4_DYNAMIC);
+  #endif
+            sqlite3VdbeAddOp3(v, OP_OpenRead, iTab, pIdx->tnum, iDb);
+            sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
+            VdbeComment((v, "%s", pIdx->zName));
+            assert( IN_INDEX_INDEX_DESC == IN_INDEX_INDEX_ASC+1 );
+            eType = IN_INDEX_INDEX_ASC + pIdx->aSortOrder[0];
+  
+            if( prRhsHasNull ){
+              *prRhsHasNull = ++pParse->nMem;
+  #ifdef SQLITE_ENABLE_COLUMN_USED_MASK
+              i64 mask = (1<<nExpr)-1;
+              sqlite3VdbeAddOp4Dup8(v, OP_ColumnsUsed, 
+                  iTab, 0, 0, (u8*)&mask, P4_INT64);
+  #endif
+              if( nExpr==1 ){
+                sqlite3SetHasNullFlag(v, iTab, *prRhsHasNull);
+              }
             }
+            sqlite3VdbeJumpHere(v, iAddr);
           }
-          sqlite3VdbeJumpHere(v, iAddr);
-        }
-      }
-    }
-  }
+        } /* End loop over indexes */
+      } /* End if( affinity_ok ) */
+    } /* End if not an rowid index */
+  } /* End attempt to optimize using an index */
 
   /* If no preexisting index is available for the IN clause
   ** and IN_INDEX_NOOP is an allowed reply