From 32dcc847b5173d211bec344c89f8c535034a26fc Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 16 Nov 2018 13:56:15 +0000 Subject: [PATCH] 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 --- ext/misc/fileio.c | 48 ++++++++++++++++++++++++++++++++++---------- manifest | 16 +++++++-------- manifest.uuid | 2 +- src/where.c | 36 ++++++++++++++++----------------- test/bestindex4.test | 18 +++++++++-------- 5 files changed, 74 insertions(+), 46 deletions(-) diff --git a/ext/misc/fileio.c b/ext/misc/fileio.c index 47f474b6b5..3a4fa18f96 100644 --- a/ext/misc/fileio.c +++ b/ext/misc/fileio.c @@ -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; inConstraint; 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{ diff --git a/manifest b/manifest index d038e8fe24..0f58e5ca46 100644 --- a/manifest +++ b/manifest @@ -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 diff --git a/manifest.uuid b/manifest.uuid index fcb164c061..e40050dba3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c537c9c3630ca979bdccab977275bfc11cce33ea54adb71a4bd4f46c85f65c6f \ No newline at end of file +684013cef6bfcfd920a4aec645df9f5d41ace8b34e75fca61759c1b4f82cc89e \ No newline at end of file diff --git a/src/where.c b/src/where.c index f4d4138c8f..5d68272503 100644 --- a/src/where.c +++ b/src/where.c @@ -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; inConstraint; 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 ); diff --git a/test/bestindex4.test b/test/bestindex4.test index 9a92625c4a..e395012c4f 100644 --- a/test/bestindex4.test +++ b/test/bestindex4.test @@ -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: }