Enhance the xBestIndex interface of virtual table so that if the xBestIndex

method returns SQLITE_CONSTRAINT then that particular combination of
constraints is considered unusable and does not participate further in
query planning.

FossilOrigin-Name: 684013cef6bfcfd920a4aec645df9f5d41ace8b34e75fca61759c1b4f82cc89e
This commit is contained in:
drh 2018-11-16 13:56:15 +00:00
parent eabbf37fae
commit 32dcc847b5
5 changed files with 74 additions and 46 deletions

View File

@ -846,27 +846,53 @@ static int fsdirBestIndex(
sqlite3_index_info *pIdxInfo
){
int i; /* Loop over constraints */
int idx4 = -1; /* Index in pIdxInfo->aConstraint of PATH= */
int idx5 = -1; /* Index in pIdxInfo->aConstraint of DIR= */
int idxPath = -1; /* Index in pIdxInfo->aConstraint of PATH= */
int idxDir = -1; /* Index in pIdxInfo->aConstraint of DIR= */
int seenPath = 0; /* True if an unusable PATH= constraint is seen */
int seenDir = 0; /* True if an unusable DIR= constraint is seen */
const struct sqlite3_index_constraint *pConstraint;
(void)tab;
pConstraint = pIdxInfo->aConstraint;
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
if( pConstraint->iColumn==4 && pConstraint->usable ) idx4 = i;
if( pConstraint->iColumn==5 ) idx5 = i;
switch( pConstraint->iColumn ){
case FSDIR_COLUMN_PATH: {
if( pConstraint->usable ){
idxPath = i;
seenPath = 0;
}else if( idxPath<0 ){
seenPath = 1;
}
break;
}
case FSDIR_COLUMN_DIR: {
if( pConstraint->usable ){
idxDir = i;
seenDir = 0;
}else if( idxDir<0 ){
seenDir = 1;
}
break;
}
}
}
if( seenPath || seenDir ){
/* If input parameters are unusable, disallow this plan */
return SQLITE_CONSTRAINT;
}
if( idx4<0 || (idx5>=0 && pIdxInfo->aConstraint[idx5].usable==0) ){
if( idxPath<0 ){
pIdxInfo->idxNum = 0;
pIdxInfo->estimatedCost = (double)(((sqlite3_int64)1) << 60);
/* The pIdxInfo->estimatedCost should have been initialized to a huge
** number. Leave it unchanged. */
pIdxInfo->estimatedRows = 0x7fffffff;
}else{
pIdxInfo->aConstraintUsage[idx4].omit = 1;
pIdxInfo->aConstraintUsage[idx4].argvIndex = 1;
if( idx5>=0 ){
pIdxInfo->aConstraintUsage[idx5].omit = 1;
pIdxInfo->aConstraintUsage[idx5].argvIndex = 2;
pIdxInfo->aConstraintUsage[idxPath].omit = 1;
pIdxInfo->aConstraintUsage[idxPath].argvIndex = 1;
if( idxDir>=0 ){
pIdxInfo->aConstraintUsage[idxDir].omit = 1;
pIdxInfo->aConstraintUsage[idxDir].argvIndex = 2;
pIdxInfo->idxNum = 2;
pIdxInfo->estimatedCost = 10.0;
}else{

View File

@ -1,5 +1,5 @@
C Fix\scomments\sand\smake\smagic\snumbers\sinto\s#defines\sin\sthe\sfsdir\nimplementation.
D 2018-11-16T13:06:30.577
C Enhance\sthe\sxBestIndex\sinterface\sof\svirtual\stable\sso\sthat\sif\sthe\sxBestIndex\nmethod\sreturns\sSQLITE_CONSTRAINT\sthen\sthat\sparticular\scombination\sof\nconstraints\sis\sconsidered\sunusable\sand\sdoes\snot\sparticipate\sfurther\sin\nquery\splanning.
D 2018-11-16T13:56:15.713
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F Makefile.in b730006b54c990461d864c5387f2e6f13aadb0236804555fb010ed6865a5f058
@ -282,7 +282,7 @@ F ext/misc/csv.c 88333dc9f7dcf6a8148406f10ae04261e24e3b4c721550ae33e9e71f1265c1f
F ext/misc/dbdump.c 12389a10c410fadf1e68eeb382def92d5a7fa9ce7cce4fb86a736fa2bac1000a
F ext/misc/eval.c 6ea9b22a5fa0dd973b67ca4e53555be177bc0b7b263aadf1024429457c82c0e3
F ext/misc/explain.c c82dd86f1156d32b284e0523a4bf6a93a85ab2a812caed48963e0774f3327185
F ext/misc/fileio.c 45acde5c065cc13bc9e65d4c3d623fa47441daf6d4a996d0ac9e6c9907b1d550
F ext/misc/fileio.c e3153b04433897a18a3d17185845f286892e96fdf87f4301290d09c36ae1759f
F ext/misc/fuzzer.c 7c64b8197bb77b7d64eff7cac7848870235d4c25
F ext/misc/ieee754.c f190d0cc5182529acb15babd177781be1ac1718c
F ext/misc/json1.c b0fba11c4f4e7c80534b08e120a296a8b301ee7e4d1a33f6647f1c047c8ce8e2
@ -591,7 +591,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
F src/wal.c 3f4f653daf234fe713edbcbca3fec2350417d159d28801feabc702a22c4e213f
F src/wal.h 606292549f5a7be50b6227bd685fa76e3a4affad71bb8ac5ce4cb5c79f6a176a
F src/walker.c fb94aadc9099ff9c6506d0a8b88d51266005bcaa265403f3d7caf732a562eb66
F src/where.c f5da1079f084c569aa70fd4ada6b7e45b356e19708f1e5307493cc65857ec9e4
F src/where.c 23f955de6db320fa5afcbe569687e649e5c9be1380c742fe7c5e9939e18cbcbe
F src/whereInt.h f125f29fca80890768e0b2caa14f95db74b2dacd3a122a168f97aa7b64d6968f
F src/wherecode.c c45f03aefc2266b990df0fc4d7acc4e27f56f881f4fc0fc355b7cbc4d7189da5
F src/whereexpr.c 491f0894ad9903750cdecb7894437a0cabdffdd88f574d2b1c9ac85d14fe4b9c
@ -671,7 +671,7 @@ F test/bc_common.tcl b5e42d80305be95697e6370e015af571e5333a1c
F test/bestindex1.test 852170bddbb21daa121fabcc274640ff83d7d8705912e8b5fe7ed2c5a9a9224a
F test/bestindex2.test 9a0ccd320b6525eec3a706aae6cdab7e1b7b5abca75027e39f39f755e76e5928
F test/bestindex3.test 001788a114ad96d81d5154fe77c7f1e26e84b3a2b5635ca29e4f96f6decc534e
F test/bestindex4.test 210eac9323ef88881212d6ae90c84dad86ec14d82c6f4fc9a9ee24c76a8f2014
F test/bestindex4.test 038e3d0789332f3f1d61474f9bbc9c6d08c6bd1783a978f31f38ad82688de601
F test/bestindex5.test 67c1166131bb59f9e47c00118f7d432ca5491e6cae6ca3f87ca9db20103a78f9
F test/bestindex6.test d856a9bb63d927493575823eed44053bc36251e241aa364e54d0f2a2d302e1d4
F test/between.test 34d375fb5ce1ae283ffe82b6b233e9f38e84fc6c
@ -1778,7 +1778,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 7fffcee0fc3fe8d036f0d93ec17673992c3edcf2bb454dc90d80142435b37946
R 2a2c80e2a75c50071a742aace6ac9b0f
P c537c9c3630ca979bdccab977275bfc11cce33ea54adb71a4bd4f46c85f65c6f
R bb405b8b26464e39757216d3bb32f7e9
U drh
Z 0d4f2c0f6de8a62406810148e0ad4826
Z ba10548cd8043453179e425b1b595795

View File

@ -1 +1 @@
c537c9c3630ca979bdccab977275bfc11cce33ea54adb71a4bd4f46c85f65c6f
684013cef6bfcfd920a4aec645df9f5d41ace8b34e75fca61759c1b4f82cc89e

View File

@ -1031,9 +1031,11 @@ static sqlite3_index_info *allocateIndexInfo(
** method of the virtual table with the sqlite3_index_info object that
** comes in as the 3rd argument to this function.
**
** If an error occurs, pParse is populated with an error message and a
** non-zero value is returned. Otherwise, 0 is returned and the output
** part of the sqlite3_index_info structure is left populated.
** If an error occurs, pParse is populated with an error message and an
** appropriate error code is returned. A return of SQLITE_CONSTRAINT from
** xBestIndex is not considered an error. SQLITE_CONSTRAINT indicates that
** the current configuration of "unusable" flags in sqlite3_index_info can
** not result in a valid plan.
**
** Whether or not an error is returned, it is the responsibility of the
** caller to eventually free p->idxStr if p->needToFreeIdxStr indicates
@ -1047,7 +1049,7 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
rc = pVtab->pModule->xBestIndex(pVtab, p);
TRACE_IDX_OUTPUTS(p);
if( rc!=SQLITE_OK ){
if( rc!=SQLITE_OK && rc!=SQLITE_CONSTRAINT ){
if( rc==SQLITE_NOMEM ){
sqlite3OomFault(pParse->db);
}else if( !pVtab->zErrMsg ){
@ -1058,19 +1060,8 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
}
sqlite3_free(pVtab->zErrMsg);
pVtab->zErrMsg = 0;
#if 0
/* This error is now caught by the caller.
** Search for "xBestIndex malfunction" below */
for(i=0; i<p->nConstraint; i++){
if( !p->aConstraint[i].usable && p->aConstraintUsage[i].argvIndex>0 ){
sqlite3ErrorMsg(pParse,
"table %s: xBestIndex returned an invalid plan", pTab->zName);
}
}
#endif
return pParse->nErr;
// assert( pParse->nErr==0 || (rc!=SQLITE_OK && rc!=SQLITE_CONSTRAINT) );
return rc;
}
#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */
@ -3143,7 +3134,16 @@ static int whereLoopAddVirtualOne(
/* Invoke the virtual table xBestIndex() method */
rc = vtabBestIndex(pParse, pSrc->pTab, pIdxInfo);
if( rc ) return rc;
if( rc ){
if( rc==SQLITE_CONSTRAINT ){
/* If the xBestIndex method returns SQLITE_CONSTRAINT, that means
** that the particular combination of parameters provided is unusable.
** Make no entries in the loop table.
*/
return SQLITE_OK;
}
return rc;
}
mxTerm = -1;
assert( pNew->nLSlot>=nConstraint );

View File

@ -155,18 +155,20 @@ do_execsql_test 2.0 {
CREATE TABLE t1 (x INT PRIMARY KEY);
} {}
do_execsql_test 2.1 {
EXPLAIN QUERY PLAN SELECT * FROM t1, x1 WHERE x1.d=t1.x;
do_eqp_test 2.1 {
SELECT * FROM t1, x1 WHERE x1.d=t1.x;
} {
3 0 0 {SCAN TABLE x1 VIRTUAL TABLE INDEX 0:}
7 0 0 {SEARCH TABLE t1 USING COVERING INDEX sqlite_autoindex_t1_1 (x=?)}
QUERY PLAN
|--SCAN TABLE x1 VIRTUAL TABLE INDEX 0:
`--SEARCH TABLE t1 USING COVERING INDEX sqlite_autoindex_t1_1 (x=?)
}
do_execsql_test 2.2 {
EXPLAIN QUERY PLAN SELECT * FROM t1, x1(t1.x)
do_eqp_test 2.2 {
SELECT * FROM t1, x1(t1.x)
} {
3 0 0 {SCAN TABLE t1}
5 0 0 {SCAN TABLE x1 VIRTUAL TABLE INDEX 555:}
QUERY PLAN
|--SCAN TABLE t1
`--SCAN TABLE x1 VIRTUAL TABLE INDEX 555:
}