]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Ensure that LIMIT clauses may be passed through to virtual table implementations...
authordan <Dan Kennedy>
Wed, 4 Jan 2023 17:46:29 +0000 (17:46 +0000)
committerdan <Dan Kennedy>
Wed, 4 Jan 2023 17:46:29 +0000 (17:46 +0000)
FossilOrigin-Name: f38caab23bcef1df02618376de22d208a3333d023628cde310345505933329f1

manifest
manifest.uuid
src/test_bestindex.c
src/whereexpr.c
test/bestindexA.test [new file with mode: 0644]

index a4ec59a2daf531b9b4f471ad2c5b582ec1dae7db..86115e2546dc04fb17982e769b5c88a9ee14d311 100644 (file)
--- 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.
index ca60e49c78102f9a50d05057cc3880a516f668ba..d2c2a2a983e0e5794aa01dd01a43a257ce90d48f 100644 (file)
@@ -1 +1 @@
-869635fb81e0868dee80ce77653a2c1d2af41b3ffbf0a3f7b788ac99dc808887
\ No newline at end of file
+f38caab23bcef1df02618376de22d208a3333d023628cde310345505933329f1
\ No newline at end of file
index 67a0c8258d66c931039c2ed65050cc9f08bfe31d..65f063e50d4cc5ef8bba1027872d8de4df45007b 100644 (file)
 
 #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; ii<pIdxInfo->nConstraint; 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; ii<nArg; ii++){
+    const char *zArg = (const char*)sqlite3_value_text(apArg[ii]);
+    Tcl_ListObjAppendElement(interp, pScript,
+        (zArg ? Tcl_NewStringObj(zArg, -1) : Tcl_NewObj())
+    );
+  }
+  Tcl_EvalObjEx(interp, pScript, TCL_EVAL_GLOBAL);
+  Tcl_DecrRefCount(pScript);
+
+  pRet = Tcl_GetObjResult(interp);
+  sqlite3_result_text(pCtx, Tcl_GetString(pRet), -1, SQLITE_TRANSIENT);
+}
+
+static int tclFindFunction(
+  sqlite3_vtab *tab, 
+  int nArg, 
+  const char *zName,
+  void (**pxFunc)(sqlite3_context*,int,sqlite3_value**),   /* OUT */
+  void **ppArg                                             /* OUT */
+){
+  int iRet = 0;
+  tcl_vtab *pTab = (tcl_vtab*)tab;
+  Tcl_Interp *interp = pTab->interp;
+  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 */
 };
 
index 24246c1aaf4fc785318a96d187da3da9e4c475d4..b466c2da2e1108a895d5511537c2df09e4e25e32 100644 (file)
@@ -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 (file)
index 0000000..650404e
--- /dev/null
@@ -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
+
+
+