1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-12 13:01:09 +03:00

Fix the sqlite3FindInIndex() to ensure that it always uses a prefix of

the index and uses no repeated columns.  Enhanced comments.

FossilOrigin-Name: b9fc89e432fbe4e5b41959a42797641907e075e3
This commit is contained in:
drh
2016-08-26 21:15:35 +00:00
parent 363fb95bc3
commit a84a283d18
3 changed files with 79 additions and 72 deletions

View File

@@ -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

View File

@@ -1 +1 @@
171aa833a2e1650c3d9cf9bd6438ae46f6c35871
b9fc89e432fbe4e5b41959a42797641907e075e3

View 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;
}
}
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;
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);
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 */
}
}
sqlite3VdbeJumpHere(v, iAddr);
}
}
}
}
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;
}
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);
}
} /* 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