From: dan Date: Wed, 4 Jan 2023 17:46:29 +0000 (+0000) Subject: Ensure that LIMIT clauses may be passed through to virtual table implementations... X-Git-Tag: version-3.41.0~146 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=52c08ee98222d67cefed53e305f96fe9344273d5;p=thirdparty%2Fsqlite.git Ensure that LIMIT clauses may be passed through to virtual table implementations even if the WHERE clause uses operators that may only be optimized by virtual, not built-in, tables (!=, functions, MATCH etc.). FossilOrigin-Name: f38caab23bcef1df02618376de22d208a3333d023628cde310345505933329f1 --- diff --git a/manifest b/manifest index a4ec59a2da..86115e2546 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Employ\sdeliberate_fall_through\smacro\sto\squiet\ssome\scompilers. -D 2023-01-04T16:54:55.789 +C Ensure\sthat\sLIMIT\sclauses\smay\sbe\spassed\sthrough\sto\svirtual\stable\simplementations\seven\sif\sthe\sWHERE\sclause\suses\soperators\sthat\smay\sonly\sbe\soptimized\sby\svirtual,\snot\sbuilt-in,\stables\s(!=,\sfunctions,\sMATCH\setc.). +D 2023-01-04T17:46:29.753 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -667,7 +667,7 @@ F src/test9.c 12e5ba554d2d1cbe0158f6ab3f7ffcd7a86ee4e5 F src/test_async.c 195ab49da082053fdb0f949c114b806a49ca770a F src/test_autoext.c 915d245e736652a219a907909bb6710f0d587871 F src/test_backup.c bf5da90c9926df0a4b941f2d92825a01bbe090a0 -F src/test_bestindex.c 8294d8223b7f18a3ddb7f9a0e30815dcca4e61681f78b538c870f7d934f88b81 +F src/test_bestindex.c 1627f782e866a3f4b5ecd01cb46813686bf36612afc9755f26058717fe90b24e F src/test_blob.c ae4a0620b478548afb67963095a7417cd06a4ec0a56adb453542203bfdcb31ce F src/test_btree.c 8b2dc8b8848cf3a4db93f11578f075e82252a274 F src/test_config.c 8264637b06a3c1f0727c88d1ea32dcf7986b9e7e358a970cae87cdac8a5b2708 @@ -735,7 +735,7 @@ F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b F src/where.c d0d8e3cb2c11e77ba0f8f9ed8eada9d84dbd377167cdcf387b8eeb824c35a3ad F src/whereInt.h e25203e5bfee149f5f1225ae0166cfb4f1e65490c998a024249e98bb0647377c F src/wherecode.c 76bca3379219880d2527493b71a3be49e696f75396d3481e4de5d4ceec7886b2 -F src/whereexpr.c 05295b44b54eea76d1ba766f0908928d0e20e990c249344c9521454d3d09c7ae +F src/whereexpr.c 7c5671a04b00c876bec5e99fd4e6f688065feb4773160fbf76fd7900d2901777 F src/window.c 14836767adb26573b50f528eb37f8b1336f2c430ab38de7cead1e5c546bb4d8c F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627 @@ -828,6 +828,7 @@ F test/bestindex6.test 16942535b551273f3ad9df8d7cc4b7f22b1fcd8882714358859eb049a F test/bestindex7.test f094c669a6400777f4d2ddc3ed28e39169f1adb5be3d59b55f22ccf8c414b71e F test/bestindex8.test 333ad8c6a554b885a49b68c019166eda92b05f493a92b36b0acdf7f766d04dad F test/bestindex9.test bf2eb8556e8d5c00ef3ee18c521751cd03c1b55454b6e7683b4c6742e3131b23 +F test/bestindexA.test dd7b7439a46169b45d0305c4cbbb14fc20c7044acc2055c767d2f838b3479c3f F test/between.test b9a65fb065391980119e8a781a7409d3fcf059d89968279c750e190a9a1d5263 F test/bigfile.test aa74f4e5db51c8e54a1d9de9fa65d01d1eb20b59 F test/bigfile2.test 1b489a3a39ae90c7f027b79110d6b4e1dbc71bfc @@ -2067,8 +2068,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 9302e4bfdce5905576b8f0af7d6b3a864e1dcd58ed89bb303010a1c4f826e915 -R e73248a148e8d456f99341cdbea50943 -U larrybr -Z 81e81086d972c0e31b16fcba3d0f3fea +P 869635fb81e0868dee80ce77653a2c1d2af41b3ffbf0a3f7b788ac99dc808887 +R 09b79d8dee52dbbaabe508a6440984a8 +U dan +Z 3297969a556ae071bd8b9f4bfdf478f8 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index ca60e49c78..d2c2a2a983 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -869635fb81e0868dee80ce77653a2c1d2af41b3ffbf0a3f7b788ac99dc808887 \ No newline at end of file +f38caab23bcef1df02618376de22d208a3333d023628cde310345505933329f1 \ No newline at end of file diff --git a/src/test_bestindex.c b/src/test_bestindex.c index 67a0c8258d..65f063e50d 100644 --- a/src/test_bestindex.c +++ b/src/test_bestindex.c @@ -101,8 +101,10 @@ #ifndef SQLITE_OMIT_VIRTUALTABLE + typedef struct tcl_vtab tcl_vtab; typedef struct tcl_cursor tcl_cursor; +typedef struct TestFindFunction TestFindFunction; /* ** A fs virtual-table object @@ -111,6 +113,7 @@ struct tcl_vtab { sqlite3_vtab base; Tcl_Interp *interp; Tcl_Obj *pCmd; + TestFindFunction *pFindFunctionList; sqlite3 *db; }; @@ -120,6 +123,13 @@ struct tcl_cursor { sqlite3_stmt *pStmt; /* Read data from here */ }; +struct TestFindFunction { + tcl_vtab *pTab; + const char *zName; + TestFindFunction *pNext; +}; + + /* ** Dequote string z in place. */ @@ -223,6 +233,11 @@ static int tclConnect( /* The xDisconnect and xDestroy methods are also the same */ static int tclDisconnect(sqlite3_vtab *pVtab){ tcl_vtab *pTab = (tcl_vtab*)pVtab; + while( pTab->pFindFunctionList ){ + TestFindFunction *p = pTab->pFindFunctionList; + pTab->pFindFunctionList = p->pNext; + sqlite3_free(p); + } Tcl_DecrRefCount(pTab->pCmd); sqlite3_free(pTab); return SQLITE_OK; @@ -398,7 +413,7 @@ static void testBestIndexObjConstraints( for(ii=0; iinConstraint; ii++){ struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii]; Tcl_Obj *pElem = Tcl_NewObj(); - const char *zOp = "?"; + const char *zOp = 0; Tcl_IncrRefCount(pElem); @@ -438,7 +453,11 @@ static void testBestIndexObjConstraints( } Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("op", -1)); - Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj(zOp, -1)); + if( zOp ){ + Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj(zOp, -1)); + }else{ + Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pCons->op)); + } Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("column", -1)); Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pCons->iColumn)); Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("usable", -1)); @@ -693,6 +712,83 @@ static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ return rc; } +static void tclFunction(sqlite3_context *pCtx, int nArg, sqlite3_value **apArg){ + TestFindFunction *p = (TestFindFunction*)sqlite3_user_data(pCtx); + Tcl_Interp *interp = p->pTab->interp; + Tcl_Obj *pScript = 0; + Tcl_Obj *pRet = 0; + int ii; + + pScript = Tcl_DuplicateObj(p->pTab->pCmd); + Tcl_IncrRefCount(pScript); + Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("function", -1)); + Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj(p->zName, -1)); + + for(ii=0; iiinterp; + Tcl_Obj *pScript = 0; + int rc = SQLITE_OK; + + pScript = Tcl_DuplicateObj(pTab->pCmd); + Tcl_IncrRefCount(pScript); + Tcl_ListObjAppendElement( + interp, pScript, Tcl_NewStringObj("xFindFunction", -1) + ); + Tcl_ListObjAppendElement(interp, pScript, Tcl_NewIntObj(nArg)); + Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj(zName, -1)); + rc = Tcl_EvalObjEx(interp, pScript, TCL_EVAL_GLOBAL); + Tcl_DecrRefCount(pScript); + + if( rc==SQLITE_OK ){ + Tcl_Obj *pObj = Tcl_GetObjResult(interp); + + if( Tcl_GetIntFromObj(interp, pObj, &iRet) ){ + rc = SQLITE_ERROR; + }else if( iRet>0 ){ + int nName = strlen(zName); + int nByte = nName + 1 + sizeof(TestFindFunction); + TestFindFunction *pNew = 0; + + pNew = (TestFindFunction*)sqlite3_malloc(nByte); + if( pNew==0 ){ + iRet = 0; + }else{ + memset(pNew, 0, nByte); + pNew->zName = (const char*)&pNew[1]; + memcpy((char*)pNew->zName, zName, nName); + pNew->pTab = pTab; + pNew->pNext = pTab->pFindFunctionList; + pTab->pFindFunctionList = pNew; + *ppArg = (void*)pNew; + *pxFunc = tclFunction; + } + } + } + + return iRet; +} + /* ** A virtual table module that provides read-only access to a ** Tcl global variable namespace. @@ -716,7 +812,7 @@ static sqlite3_module tclModule = { 0, /* xSync */ 0, /* xCommit */ 0, /* xRollback */ - 0, /* xFindMethod */ + tclFindFunction, /* xFindFunction */ 0, /* xRename */ }; diff --git a/src/whereexpr.c b/src/whereexpr.c index 24246c1aaf..b466c2da2e 100644 --- a/src/whereexpr.c +++ b/src/whereexpr.c @@ -1620,6 +1620,13 @@ void SQLITE_NOINLINE sqlite3WhereAddLimit(WhereClause *pWC, Select *p){ assert( pWC->a[ii].eOperator==WO_ROWVAL ); continue; } + if( pWC->a[ii].nChild ){ + /* If this term has child terms, then they are also part of the + ** pWC->a[] array. So this term can be ignored, as a LIMIT clause + ** will only be added if each of the child terms passes the + ** (leftCursor==iCsr) test below. */ + continue; + } if( pWC->a[ii].leftCursor!=iCsr ) return; } diff --git a/test/bestindexA.test b/test/bestindexA.test new file mode 100644 index 0000000000..650404eaa0 --- /dev/null +++ b/test/bestindexA.test @@ -0,0 +1,138 @@ +# 2020-01-29 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix bestindexA + +ifcapable !vtab { + finish_test + return +} + + +proc vtab_command {method args} { + switch -- $method { + xConnect { + return "CREATE TABLE x(a, b, c)" + } + + xBestIndex { + set hdl [lindex $args 0] + set clist [$hdl constraints] + foreach c $clist { + array set C $c + lappend ::vtab_constraints [list $C(op) $C(column)] + } + return [list] + } + + xFilter { + return "" + } + + xFindFunction { + foreach {nArg name} $args {} + if {$nArg==2 && $name=="even"} { + return 152 + } + return 0 + } + + } + + return {} +} + +register_tcl_module db +do_execsql_test 1.0 { + CREATE VIRTUAL TABLE t1 USING tcl(vtab_command); +} + +proc do_xbestindex_test {tn sql res} { + set script [subst { + execsql "$sql" + set ::vtab_constraints + }] + + uplevel [list do_test $tn $script [list {*}$res]] + set ::vtab_constraints [list] +} + +do_xbestindex_test 1.1 { + SELECT * FROM t1 WHERE a=? +} { + {eq 0} +} + +do_xbestindex_test 1.2 { + SELECT * FROM t1 WHERE a=? LIMIT 10 +} { + {eq 0} + {limit 0} +} + +do_xbestindex_test 1.3 { + SELECT * FROM t1 WHERE a=? AND (b+1)=? LIMIT 10 +} { + {eq 0} +} + +proc error_function {args} { error "not a function!" } +db function even error_function + +do_xbestindex_test 1.4 { + SELECT * FROM t1 WHERE even(a, ?) +} { + {152 0} +} + +do_xbestindex_test 1.5 { + SELECT * FROM t1 WHERE b=10 AND even(a, ?) +} { + {eq 1} + {152 0} +} + +do_xbestindex_test 1.6 { + SELECT * FROM t1 WHERE b=10 LIMIT 10 +} { + {eq 1} + {limit 0} +} + +do_xbestindex_test 1.7 { + SELECT * FROM t1 WHERE even(b,?) LIMIT 10 +} { + {152 1} + {limit 0} +} + +do_xbestindex_test 1.8 { + SELECT * FROM t1 WHERE b!=? LIMIT 10 +} { + {ne 1} + {limit 0} +} + +do_xbestindex_test 1.9 { + SELECT * FROM t1 WHERE ?=a LIMIT 10 +} { + {eq 0} + {limit 0} +} + + +finish_test + + +