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:
12
manifest
12
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
|
||||
|
||||
@@ -1 +1 @@
|
||||
171aa833a2e1650c3d9cf9bd6438ae46f6c35871
|
||||
b9fc89e432fbe4e5b41959a42797641907e075e3
|
||||
137
src/expr.c
137
src/expr.c
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user