]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Test cases for sqlite3_vtab_in() and sqlite3_vtab_distinct().
authordrh <>
Wed, 2 Feb 2022 19:15:53 +0000 (19:15 +0000)
committerdrh <>
Wed, 2 Feb 2022 19:15:53 +0000 (19:15 +0000)
FossilOrigin-Name: 21afb81d0a73af39aacd9329b1441faa2b535a52a52036daec89fd303a8b344f

12 files changed:
manifest
manifest.uuid
src/test_bestindex.c
test/bestindex1.test
test/bestindex2.test
test/bestindex3.test
test/bestindex4.test
test/bestindex5.test
test/bestindex6.test
test/bestindex7.test
test/bestindex8.test [new file with mode: 0644]
test/rowvalue5.test

index 1e9ca0f15e7333f925749b6da0b91c552f695f6b..95ed26d2415ba4955eb3681627cf8adde8244894 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Improved\sdocumentation\sfor\ssqlite3_vtab_in().\s\sNo\scode\schanges.
-D 2022-02-02T18:47:56.749
+C Test\scases\sfor\ssqlite3_vtab_in()\sand\ssqlite3_vtab_distinct().
+D 2022-02-02T19:15:53.331
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -574,7 +574,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 ba0314d3bca8b00fa0daaf113a314986f73ea8ad85a7562f983638d09a29045c
+F src/test_bestindex.c 8294d8223b7f18a3ddb7f9a0e30815dcca4e61681f78b538c870f7d934f88b81
 F src/test_blob.c ae4a0620b478548afb67963095a7417cd06a4ec0a56adb453542203bfdcb31ce
 F src/test_btree.c 8b2dc8b8848cf3a4db93f11578f075e82252a274
 F src/test_config.c 284c29912736f68b0a583a920bf63fd8f9125dffb8a75cb0676e58502b2f7908
@@ -724,13 +724,14 @@ F test/backup_malloc.test 0c9abdf74c51e7bedb66d504cd684f28d4bd4027
 F test/badutf.test d5360fc31f643d37a973ab0d8b4fb85799c3169f
 F test/badutf2.test f310fd3b24a491b6b77bccdf14923b85d6ebcce751068c180d93a6b8ff854399
 F test/bc_common.tcl b5e42d80305be95697e6370e015af571e5333a1c
-F test/bestindex1.test 7cc626f1f4a7483bb6b38487d467db4477083be5cd93958aeda5d5127640dc81
-F test/bestindex2.test 60266e2854055788459cbfd86cef575601eabe74a2c61faba72601739fea4398
-F test/bestindex3.test e061a6ed0f519beee037ba7e7a4c37f80c8a7e4a303e2559ed1f760e4b0235eb
-F test/bestindex4.test 82250e7dcc6d5b15244edc9d6554b1760583af1b8548c2a255a1c4f28e744c0e
-F test/bestindex5.test 67c1166131bb59f9e47c00118f7d432ca5491e6cae6ca3f87ca9db20103a78f9
-F test/bestindex6.test d856a9bb63d927493575823eed44053bc36251e241aa364e54d0f2a2d302e1d4
-F test/bestindex7.test a11348824aed0de2bb9030f092636929000cd72882bdf919adacc3792f67ccbd
+F test/bestindex1.test 856a453dff8c68b4568601eed5a8b5e20b4763af9229f3947c215729ed878db0
+F test/bestindex2.test 394ff8fbf34703391247116d6a44e1c50ee7282236ee77909044573cefc37bc0
+F test/bestindex3.test 34bea272b0e0f835651b16a3931dbe7ac927039be6b2e1cb617bbe1d584b492b
+F test/bestindex4.test 3039894f2dad50f3a68443dffad1b44c9b067ac03870102df1ce3d9a46ea602e
+F test/bestindex5.test a0c90b2dad7836e80a01379e200e5f8ec9476d49b349af02c0dbff2fb75dc98d
+F test/bestindex6.test 16942535b551273f3ad9df8d7cc4b7f22b1fcd8882714358859eb049a6f99dd4
+F test/bestindex7.test f094c669a6400777f4d2ddc3ed28e39169f1adb5be3d59b55f22ccf8c414b71e
+F test/bestindex8.test 025477dd9bdb462f4faef3dd8838306f4e230620677778f4fe27e6736789e1b8
 F test/between.test b9a65fb065391980119e8a781a7409d3fcf059d89968279c750e190a9a1d5263
 F test/bigfile.test aa74f4e5db51c8e54a1d9de9fa65d01d1eb20b59
 F test/bigfile2.test 1b489a3a39ae90c7f027b79110d6b4e1dbc71bfc
@@ -1327,7 +1328,7 @@ F test/rowvalue.test 02214016f747854ef636e64ff204778649937aa801ca78e2495a960f8e0
 F test/rowvalue2.test 060d238b7e5639a7c5630cb5e63e311b44efef2b
 F test/rowvalue3.test 3068f508753af69884b12125995f023da0dbb256
 F test/rowvalue4.test 441e7e366ac6d939a3a95a574031c56ec2a854077a91d66eee5ff1d86cb5be58
-F test/rowvalue5.test c81c7d8cf36711ab37675ad7376084ae2a359cb6
+F test/rowvalue5.test 00740304ea6a53a8704640c7405690f0045d5d2a6b4b04dde7bccc14c3068ea7
 F test/rowvalue6.test d19b54feb604d5601f8614b15e214e0774c01087
 F test/rowvalue7.test c1cbdbf407029db01f87764097c6ac02a1c5a37efd2776eff32a9cdfdf6f2dba
 F test/rowvalue8.test 5900eddad9e2c3c2e26f1a95f74aafc1232ee5e0
@@ -1942,8 +1943,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 3bf2153440dce0e8c0572c4fd39e6b9f34ead75ccab2cea80a646d4ff9d19146
-R f229e1cc04b78384226f7f6c44d75044
+P c99df4ab5db2c32b044366c5b0ac70fd8887d1456d53323e75fede23cc61c236
+R 73bbb4581fa3d24d1ee5b28ca166750f
 U drh
-Z 5557e45c47098a153d05816f88cff23e
+Z 0537364a2100dee8e5b21a6d334bfbef
 # Remove this line to create a well-formed Fossil manifest.
index 472918b7435664561f8ab049f32193c56d046823..ef067148571d3e72e4efb835196d455686168ae9 100644 (file)
@@ -1 +1 @@
-c99df4ab5db2c32b044366c5b0ac70fd8887d1456d53323e75fede23cc61c236
\ No newline at end of file
+21afb81d0a73af39aacd9329b1441faa2b535a52a52036daec89fd303a8b344f
\ No newline at end of file
index 6cda889e5e238a9594334b3f74abc9df8332fb08..67a0c8258d66c931039c2ed65050cc9f08bfe31d 100644 (file)
@@ -299,7 +299,21 @@ static int tclFilter(
     const char *zVal = (const char*)sqlite3_value_text(argv[ii]);
     Tcl_Obj *pVal;
     if( zVal==0 ){
+      sqlite3_value *pMem;
       pVal = Tcl_NewObj();
+      for(rc=sqlite3_vtab_in_first(argv[ii], &pMem); 
+          rc==SQLITE_OK && pMem;
+          rc=sqlite3_vtab_in_next(argv[ii], &pMem)
+      ){
+        Tcl_Obj *pVal2 = 0;
+        zVal = (const char*)sqlite3_value_text(pMem);
+        if( zVal ){
+          pVal2 = Tcl_NewStringObj(zVal, -1);
+        }else{
+          pVal2 = Tcl_NewObj();
+        }
+        Tcl_ListObjAppendElement(interp, pVal, pVal2);
+      }
     }else{
       pVal = Tcl_NewStringObj(zVal, -1);
     }
@@ -374,20 +388,13 @@ static int tclEof(sqlite3_vtab_cursor *pVtabCursor){
   return (pCsr->pStmt==0);
 }
 
-static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
-  tcl_vtab *pTab = (tcl_vtab*)tab;
-  Tcl_Interp *interp = pTab->interp;
-  Tcl_Obj *pArg;
-  Tcl_Obj *pScript;
+static void testBestIndexObjConstraints(
+  Tcl_Interp *interp, 
+  sqlite3_index_info *pIdxInfo
+){
   int ii;
-  int rc = SQLITE_OK;
-
-  pScript = Tcl_DuplicateObj(pTab->pCmd);
-  Tcl_IncrRefCount(pScript);
-  Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("xBestIndex", -1));
-
-  pArg = Tcl_NewObj();
-  Tcl_IncrRefCount(pArg);
+  Tcl_Obj *pRes = Tcl_NewObj();
+  Tcl_IncrRefCount(pRes);
   for(ii=0; ii<pIdxInfo->nConstraint; ii++){
     struct sqlite3_index_constraint const *pCons = &pIdxInfo->aConstraint[ii];
     Tcl_Obj *pElem = Tcl_NewObj();
@@ -437,15 +444,21 @@ static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
     Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("usable", -1));
     Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pCons->usable));
 
-    Tcl_ListObjAppendElement(0, pArg, pElem);
+    Tcl_ListObjAppendElement(0, pRes, pElem);
     Tcl_DecrRefCount(pElem);
   }
 
-  Tcl_ListObjAppendElement(0, pScript, pArg);
-  Tcl_DecrRefCount(pArg);
+  Tcl_SetObjResult(interp, pRes);
+  Tcl_DecrRefCount(pRes);
+}
 
-  pArg = Tcl_NewObj();
-  Tcl_IncrRefCount(pArg);
+static void testBestIndexObjOrderby(
+  Tcl_Interp *interp, 
+  sqlite3_index_info *pIdxInfo
+){
+  int ii;
+  Tcl_Obj *pRes = Tcl_NewObj();
+  Tcl_IncrRefCount(pRes);
   for(ii=0; ii<pIdxInfo->nOrderBy; ii++){
     struct sqlite3_index_orderby const *pOrder = &pIdxInfo->aOrderBy[ii];
     Tcl_Obj *pElem = Tcl_NewObj();
@@ -456,17 +469,150 @@ static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
     Tcl_ListObjAppendElement(0, pElem, Tcl_NewStringObj("desc", -1));
     Tcl_ListObjAppendElement(0, pElem, Tcl_NewIntObj(pOrder->desc));
 
-    Tcl_ListObjAppendElement(0, pArg, pElem);
+    Tcl_ListObjAppendElement(0, pRes, pElem);
     Tcl_DecrRefCount(pElem);
   }
 
-  Tcl_ListObjAppendElement(0, pScript, pArg);
-  Tcl_DecrRefCount(pArg);
+  Tcl_SetObjResult(interp, pRes);
+  Tcl_DecrRefCount(pRes);
+}
 
-  Tcl_ListObjAppendElement(0, pScript, Tcl_NewWideIntObj(pIdxInfo->colUsed));
+/*
+** Implementation of the handle passed to each xBestIndex callback. This
+** object features the following sub-commands:
+**
+**    $hdl constraints
+**    $hdl orderby
+**    $hdl mask
+**
+**    $hdl distinct
+**      Return the result (an integer) of calling sqlite3_vtab_distinct()
+**      on the index-info structure.
+**
+**    $hdl in IDX BOOLEAN
+**      Wrapper around sqlite3_vtab_in(). Returns an integer.
+**
+**    $hdl rhs_value IDX ?DEFAULT?
+**      Wrapper around sqlite3_vtab_rhs_value().
+*/
+static int SQLITE_TCLAPI testBestIndexObj(
+  ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int objc,              /* Number of arguments */
+  Tcl_Obj *CONST objv[]  /* Command arguments */
+){
+  const char *azSub[] = {
+    "constraints",                /* 0 */
+    "orderby",                    /* 1 */
+    "mask",                       /* 2 */
+    "distinct",                   /* 3 */
+    "in",                         /* 4 */
+    "rhs_value",                  /* 5 */
+    0
+  };
+  int ii;
+  sqlite3_index_info *pIdxInfo = (sqlite3_index_info*)clientData;
 
+  if( objc<2 ){
+    Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND");
+    return TCL_ERROR;
+  }
+  if( Tcl_GetIndexFromObj(interp, objv[1], azSub, "sub-command", 0, &ii) ){
+    return TCL_ERROR;
+  }
+
+  if( ii<4 && objc!=2 ){
+    Tcl_WrongNumArgs(interp, 2, objv, "");
+    return TCL_ERROR;
+  }
+  if( ii==4 && objc!=4 ){
+    Tcl_WrongNumArgs(interp, 2, objv, "INDEX BOOLEAN");
+    return TCL_ERROR;
+  }
+  if( ii==5 && objc!=3 && objc!=4 ){
+    Tcl_WrongNumArgs(interp, 2, objv, "INDEX ?DEFAULT?");
+    return TCL_ERROR;
+  }
+
+  switch( ii ){
+    case 0: assert( sqlite3_stricmp(azSub[ii], "constraints")==0 );
+      testBestIndexObjConstraints(interp, pIdxInfo);
+      break;
+
+    case 1: assert( sqlite3_stricmp(azSub[ii], "orderby")==0 );
+      testBestIndexObjOrderby(interp, pIdxInfo);
+      break;
+
+    case 2: assert( sqlite3_stricmp(azSub[ii], "mask")==0 );
+      Tcl_SetObjResult(interp, Tcl_NewWideIntObj(pIdxInfo->colUsed));
+      break;
+
+    case 3: assert( sqlite3_stricmp(azSub[ii], "distinct")==0 ); {
+      int bDistinct = sqlite3_vtab_distinct(pIdxInfo);
+      Tcl_SetObjResult(interp, Tcl_NewIntObj(bDistinct));
+      break;
+    }
+
+    case 4: assert( sqlite3_stricmp(azSub[ii], "in")==0 ); {
+      int iCons;
+      int bHandle;
+      if( Tcl_GetIntFromObj(interp, objv[2], &iCons) 
+       || Tcl_GetBooleanFromObj(interp, objv[3], &bHandle) 
+      ){
+        return TCL_ERROR;
+      }
+      Tcl_SetObjResult(interp, 
+          Tcl_NewIntObj(sqlite3_vtab_in(pIdxInfo, iCons, bHandle))
+      );
+      break;
+    }
+
+    case 5: assert( sqlite3_stricmp(azSub[ii], "rhs_value")==0 ); {
+      int iCons = 0;
+      int rc;
+      sqlite3_value *pVal = 0;
+      const char *zVal = "";
+      if( Tcl_GetIntFromObj(interp, objv[2], &iCons) ){
+        return TCL_ERROR;
+      }
+      rc = sqlite3_vtab_rhs_value(pIdxInfo, iCons, &pVal);
+      if( rc!=SQLITE_OK && rc!=SQLITE_NOTFOUND ){
+        Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
+        return TCL_ERROR;
+      }
+      if( pVal ){
+        zVal = (const char*)sqlite3_value_text(pVal);
+      }else if( objc==4 ){
+        zVal = Tcl_GetString(objv[3]);
+      }
+      Tcl_SetObjResult(interp, Tcl_NewStringObj(zVal, -1));
+      break;
+    }
+  }
+
+  return TCL_OK;
+}
+
+static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
+  tcl_vtab *pTab = (tcl_vtab*)tab;
+  Tcl_Interp *interp = pTab->interp;
+  int rc = SQLITE_OK;
+
+  static int iNext = 43;
+  char zHdl[24];
+  Tcl_Obj *pScript;
+
+  pScript = Tcl_DuplicateObj(pTab->pCmd);
+  Tcl_IncrRefCount(pScript);
+  Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj("xBestIndex", -1));
+
+  sqlite3_snprintf(sizeof(zHdl), zHdl, "bestindex%d", iNext++);
+  Tcl_CreateObjCommand(interp, zHdl, testBestIndexObj, pIdxInfo, 0);
+  Tcl_ListObjAppendElement(interp, pScript, Tcl_NewStringObj(zHdl, -1));
   rc = Tcl_EvalObjEx(interp, pScript, TCL_EVAL_GLOBAL);
+  Tcl_DeleteCommand(interp, zHdl);
   Tcl_DecrRefCount(pScript);
+
   if( rc!=TCL_OK ){
     const char *zErr = Tcl_GetStringResult(interp);
     rc = SQLITE_ERROR;
@@ -493,6 +639,7 @@ static int tclBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
       rc = SQLITE_ERROR;
       pTab->base.zErrMsg = sqlite3_mprintf("%s", zErr);
     }else{
+      int ii;
       int iArgv = 1;
       for(ii=0; rc==SQLITE_OK && ii<nElem; ii+=2){
         const char *zCmd = Tcl_GetString(apElem[ii]);
index e93d7349ddb57180f693350114c14c38659da811..e505a8bc4c45512b9f00572dda88c91333d038ea 100644 (file)
@@ -29,7 +29,10 @@ proc vtab_command {method args} {
     }
 
     xBestIndex {
-      set clist [lindex $args 0]
+      set hdl [lindex $args 0]
+      set clist [$hdl constraints]
+      set orderby [$hdl orderby]
+      
       if {[llength $clist]!=1} { error "unexpected constraint list" }
       catch { array unset C }
       array set C [lindex $clist 0]
@@ -76,10 +79,13 @@ proc t1_vtab {mode method args} {
     }
 
     xBestIndex {
+      set hdl [lindex $args 0]
+      set clist [$hdl constraints]
+      set orderby [$hdl orderby]
+
       set SQL_FILTER {SELECT * FROM t1x WHERE a='%1%'}
       set SQL_SCAN   {SELECT * FROM t1x}
 
-      set clist [lindex $args 0]
       set idx 0
       for {set idx 0} {$idx < [llength $clist]} {incr idx} {
         array unset C
@@ -177,7 +183,10 @@ proc vtab_command {method args} {
     }
 
     xBestIndex {
-      set clist [lindex $args 0]
+      set hdl [lindex $args 0]
+      set clist [$hdl constraints]
+      set orderby [$hdl orderby]
+      
       #puts $clist
       set W [list]
       set U [list]
@@ -287,12 +296,17 @@ proc vtab_command {method args} {
     }
 
     xBestIndex {
-      set clist [lindex $args 0]
+      set hdl [lindex $args 0]
+      set clist [$hdl constraints]
+      set orderby [$hdl orderby]
+      
       lappend ::bestindex_calls $clist
       set ret "cost 1000000 idxnum 555"
       for {set i 0} {$i < [llength $clist]} {incr i} {
         array set C [lindex $clist $i]
-        if {$C(usable)} { lappend ret use $i }
+        if {$C(usable)} { 
+          lappend ret use $i 
+        }
       }
       return $ret
     }
index 81ed9ebf61e8c5c275725f6878679629d03dc29d..c47a721cf6ac81fd5e9868513ef5cb09b5ea5d89 100644 (file)
@@ -47,7 +47,10 @@ proc vtab_cmd {tbl cols method args} {
       return "CREATE TABLE $tbl ([join $cols ,])"
     }
     xBestIndex {
-      foreach {clist orderby mask} $args {}
+      set hdl [lindex $args 0]
+      set clist [$hdl constraints]
+      set orderby [$hdl orderby]
+      set mask [$hdl mask]
 
       set cons [list]
       set used [list]
index 1ee3975f8bb0a8f3c22c4efd19701774440d3bb1..6aa3321c7b56bf57f9ec669143459886e2ae2700 100644 (file)
@@ -34,7 +34,10 @@ proc vtab_cmd {bOmit method args} {
     }
 
     xBestIndex {
-      foreach {clist orderby mask} $args {}
+      set hdl [lindex $args 0]
+      set clist [$hdl constraints]
+      set orderby [$hdl orderby]
+      set mask [$hdl mask]
 
       set ret [list]
       set use use
index f90bf41e94f55fb27a53bb84d71d603df53e8f6c..483656d3fc23d51bc332e61a3eadb5def974a3cc 100644 (file)
@@ -48,7 +48,10 @@ proc vtab_cmd {param method args} {
     }
 
     xBestIndex {
-      foreach {clist orderby mask} $args {}
+      set hdl [lindex $args 0]
+      set clist [$hdl constraints]
+      set orderby [$hdl orderby]
+      set mask [$hdl mask]
 
       set ret [list]
 
@@ -135,7 +138,11 @@ proc vtab_command {method args} {
     }
 
     xBestIndex {
-      set clist [lindex $args 0]
+      set hdl [lindex $args 0]
+      set clist [$hdl constraints]
+      set orderby [$hdl orderby]
+      set mask [$hdl mask]
+      
       if {[llength $clist]!=1} { error "unexpected constraint list" }
       catch { array unset C }
       array set C [lindex $clist 0]
index f7334653dc428872329ac41253cbad530d9b1ea8..35df58a98e6001f3ad8b88b12c3c302cb208d441 100644 (file)
@@ -14,7 +14,7 @@
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
-set testprefix bestindex4
+set testprefix bestindex5
 
 ifcapable !vtab {
   finish_test
@@ -44,7 +44,10 @@ proc vtab_cmd {method args} {
     }
 
     xBestIndex {
-      foreach {clist orderby mask} $args {}
+      set hdl [lindex $args 0]
+      set clist [$hdl constraints]
+      set orderby [$hdl orderby]
+      set mask [$hdl mask]
 
       set cost 1000000.0
       set ret [list]
index 8396d07ce01052d8776989e1d654480ce820ace2..8926ec4a1110c854509447ee6c0d5991aa7a0287 100644 (file)
@@ -28,7 +28,11 @@ proc vtab_command {src method args} {
     }
 
     xBestIndex {
-      set clist [lindex $args 0]
+      set hdl [lindex $args 0]
+      set clist [$hdl constraints]
+      set orderby [$hdl orderby]
+      set mask [$hdl mask]
+
       set wlist 1
 
       set iCons 0
index f8d42b2b006c6334de6569f5118c2f7bbdf8fe49..a84b9efe9e42c73d711cb1379698e3c04dcbd1d3 100644 (file)
@@ -28,7 +28,11 @@ proc vtab_command {src method args} {
     }
 
     xBestIndex {
-      set clist [lindex $args 0]
+      set hdl [lindex $args 0]
+      set clist [$hdl constraints]
+      set orderby [$hdl orderby]
+      set mask [$hdl mask]
+      
       set iCons 0
       set ret [list]
       foreach cons $clist {
diff --git a/test/bestindex8.test b/test/bestindex8.test
new file mode 100644 (file)
index 0000000..a9cc56a
--- /dev/null
@@ -0,0 +1,460 @@
+# 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 bestindex8
+
+ifcapable !vtab {
+  finish_test
+  return
+}
+
+register_tcl_module db
+
+proc vtab_command {src method args} {
+  switch -- $method {
+    xConnect {
+      return "CREATE TABLE xxx(a, b)"
+    }
+
+    xBestIndex {
+      set hdl [lindex $args 0]
+      set clist [$hdl constraints]
+      set orderby [$hdl orderby]
+      lappend ::lBestIndexDistinct [$hdl distinct]
+
+      #puts "ORDERBY: $orderby" 
+      set iCons 0
+      set ret [list]
+      foreach cons $clist {
+        catch { array unset C }
+        array set C $cons
+        if {$C(usable)} {
+          lappend ret use $iCons
+        }
+        incr iCons
+      }
+      if {$orderby=="{column 0 desc 0} {column 1 desc 0}"
+       || $orderby=="{column 0 desc 0}"
+      } {
+        lappend ret orderby 1
+        lappend ret idxnum 1
+        #puts "ORDER-BY-CONSUMED"
+      }
+      return $ret
+    }
+
+    xFilter {
+      set idxnum [lindex $args 0]
+      if {$idxnum} {
+        return [list sql "SELECT rowid, a, b FROM $src order by 2, 3"]
+      }
+      return [list sql "SELECT rowid, a, b FROM $src"]
+    }
+
+  }
+
+  return {}
+}
+
+do_execsql_test 1.0 {
+  CREATE TABLE t1(a, b);
+  CREATE INDEX i1 ON t1(a, b);
+  INSERT INTO t1 VALUES('a', 'b'), ('c', 'd');
+  INSERT INTO t1 VALUES('a', 'b'), ('c', 'd');
+  CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1);
+
+  CREATE TABLE t0(c0);
+  INSERT INTO t0(c0) VALUES (1), (0);
+}
+
+foreach {tn sql bDistinct idxinsert res} {
+  1 "SELECT a, b FROM vt1"                              0 0 {a b c d a b c d}
+  2 "SELECT DISTINCT a, b FROM vt1"                     2 1 {a b c d}
+  3 "SELECT DISTINCT a FROM vt1"                        2 1 {a c}
+  4 "SELECT DISTINCT b FROM vt1"                        2 1 {b d}
+  5 "SELECT DISTINCT b FROM vt1 ORDER BY a"             0 1 {b d}
+  6 "SELECT DISTINCT t0.c0 FROM vt1, t0 ORDER BY vt1.a" 0 1 {1 0}
+  7 "SELECT DISTINCT a, b FROM vt1 ORDER BY a, b"       1 0 {a b c d}
+  8 "SELECT DISTINCT a, b FROM vt1 ORDER BY a"          0 1 {a b c d}
+  9 "SELECT DISTINCT a FROM vt1 ORDER BY a, b"          0 1 {a c}
+
+ 10 "SELECT DISTINCT a, b FROM vt1 WHERE b='b'"         2 1 {a b}
+ 11 "SELECT DISTINCT a, b FROM vt1 WHERE +b='b'"        2 1 {a b}
+} {
+  set ::lBestIndexDistinct ""
+if {$tn==10} breakpoint
+  do_execsql_test 1.$tn.1 $sql $res
+  do_test 1.$tn.2 {
+    set ::lBestIndexDistinct
+  } $bDistinct
+  do_test 1.$tn.3 {
+    expr {[lsearch [execsql "explain $sql"] IdxInsert]>=0}
+  } $idxinsert
+}
+
+#-------------------------------------------------------------------------
+reset_db
+register_tcl_module db
+
+proc vtab_command {src method args} {
+  switch -- $method {
+    xConnect {
+      return "CREATE TABLE xxx(a, b)"
+    }
+
+    xBestIndex {
+      set hdl [lindex $args 0]
+      set ret [list]
+
+      set iCons 0
+      foreach cons [$hdl constraints] {
+        array set C $cons
+        if {($C(op)=="limit" || $C(op)=="offset") && $C(usable)} {
+          lappend ret use $iCons
+        }
+        incr iCons
+      }
+
+      return $ret
+    }
+
+    xFilter {
+      lappend ::lFilterArgs [lindex $args 2]
+      return [list sql "SELECT rowid, a, b FROM $src"]
+    }
+
+  }
+
+  return {}
+}
+
+do_execsql_test 2.0 {
+  CREATE TABLE t1(a, b);
+  CREATE INDEX i1 ON t1(a, b);
+  CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1);
+}
+
+do_test 2.1 {
+  set ::lFilterArgs [list]
+  execsql { SELECT * FROM vt1 LIMIT 10 }
+  set ::lFilterArgs
+} {10}
+
+do_test 2.2 {
+  set ::lFilterArgs [list]
+  execsql { SELECT * FROM vt1 LIMIT 5 OFFSET 50 }
+  set ::lFilterArgs
+} {{5 50}}
+
+do_test 2.3 {
+  set ::lFilterArgs [list]
+  execsql { SELECT * FROM vt1 ORDER BY a, b LIMIT 1 OFFSET 1 }
+  set ::lFilterArgs
+} {{1 1}}
+
+do_test 2.4 {
+  set ::lFilterArgs [list]
+  execsql { SELECT * FROM vt1 ORDER BY a, +b LIMIT 1 OFFSET 1 }
+  set ::lFilterArgs
+} {{}}
+
+#-------------------------------------------------------------------------
+reset_db
+register_tcl_module db
+
+proc vtab_command {src method args} {
+  switch -- $method {
+    xConnect {
+      return "CREATE TABLE xxx(a, b)"
+    }
+
+    xBestIndex {
+      set hdl [lindex $args 0]
+      set lCons [$hdl constraints]
+
+      set ret [list]
+      for {set i 0} {$i < [llength $lCons]} {incr i} {
+        array set C [lindex $lCons $i]
+        if {$C(usable)} {
+          lappend ret use $i
+          $hdl in $i 1
+        }
+      }
+      return $ret
+    }
+
+    xFilter {
+      set lArg [lindex $args 2]
+      lappend ::lFilterArg {*}$lArg
+      return [list sql "SELECT rowid, a, b FROM $src"]
+    }
+
+  }
+
+  return {}
+}
+
+do_execsql_test 3.0 {
+  CREATE TABLE t1(a, b);
+  CREATE INDEX i1 ON t1(a, b);
+  CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1);
+}
+
+foreach {tn sql lfa} {
+  1 "SELECT * FROM vt1 WHERE b IN (10, 20, 30)" {{10 20 30}}
+  2 "SELECT * FROM vt1 WHERE b IN ('abc', 'def')" {{abc def}}
+  3 "SELECT * FROM vt1 WHERE a IS NULL AND b IN ('abc', 'def')" {{} {abc def}}
+  4 "SELECT * FROM vt1 WHERE a IN (1,2,3) AND b IN ('abc', 'def')" 
+     {{1 2 3} {abc def}}
+
+  5 "SELECT * FROM vt1 
+     WHERE a IN (SELECT 1 UNION SELECT 2) AND b IN ('abc', 'def')"
+     {{1 2} {abc def}}
+
+  6 "SELECT * FROM vt1 
+     WHERE b IN ('abc', 'def') AND a IN (SELECT 1 UNION SELECT 2)"
+     {{abc def} {1 2}}
+} {
+  do_test 3.$tn {
+    set ::lFilterArg [list]
+    execsql $sql
+    set ::lFilterArg
+  } $lfa
+}
+
+#explain_i { SELECT * FROM vt1 WHERE b IN (10, 20, 30) }
+
+#-------------------------------------------------------------------------
+reset_db
+register_tcl_module db
+
+proc vtab_command {src method args} {
+  switch -- $method {
+    xConnect {
+      return "CREATE TABLE xxx(a, b, c)"
+    }
+
+    xBestIndex {
+      set hdl [lindex $args 0]
+      set lCons [$hdl constraints]
+
+      set ret [list]
+      for {set i 0} {$i < [llength $lCons]} {incr i} {
+        lappend ::lBestIndexRhs [$hdl rhs_value $i -]
+      }
+      return $ret
+    }
+
+    xFilter {
+      return [list sql "SELECT rowid, a, b, c FROM $src"]
+    }
+
+  }
+
+  return {}
+}
+
+do_execsql_test 4.0 {
+  CREATE TABLE t1(a, b, c);
+  CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1);
+}
+
+foreach {tn sql lbir} {
+  1 "SELECT * FROM vt1 WHERE b = 10" {10}
+  2 "SELECT * FROM vt1 WHERE a = 'abc' AND b < 30" {abc 30}
+  3 "SELECT * FROM vt1 WHERE a = 'abc' AND b < 30+2" {abc -}
+  4 "SELECT * FROM vt1 WHERE a IN (1,2,3) AND b < 30+2" {- -}
+  5 "SELECT * FROM vt1 WHERE a IS 111 AND b < 30+2" {111 -}
+} {
+  do_test 4.$tn {
+    set ::lBestIndexRhs [list]
+    execsql $sql
+    set ::lBestIndexRhs
+  } $lbir
+}
+
+#-------------------------------------------------------------------------
+reset_db
+db cache size 0
+register_tcl_module db
+
+set ::vtab_handle_in 1
+proc vtab_command {src method args} {
+  switch -- $method {
+    xConnect {
+      return "CREATE TABLE xxx(a, b, c)"
+    }
+
+    xBestIndex {
+      set lCols [list a b c]
+
+      set hdl [lindex $args 0]
+      set lCons [$hdl constraints]
+      set lOrder [$hdl order]
+
+      set L ""
+      set O ""
+      set W [list]
+      set a 0
+      for {set i 0} {$i < [llength $lCons]} {incr i} {
+        array set C [lindex $lCons $i]
+        if {$C(usable)} {
+          if { $C(op)=="eq" } {
+            set bIn 0
+            if {$::vtab_handle_in} { set bIn [$hdl in $i 1] }
+            if {$bIn} {
+              lappend W "[lindex $lCols $C(column)] IN (%I$a%)"
+            } else {
+              lappend W "[lindex $lCols $C(column)] = %$a%"
+            }
+            lappend ret omit $i
+          }
+          if { $C(op)=="limit"  } { set L " LIMIT %$a%"  ; lappend ret use $i }
+          if { $C(op)=="offset" } { set O " OFFSET %$a%" ; lappend ret use $i }
+          incr a
+        }
+      }
+
+      set order ""
+      set selectlist "rowid, a, b, c"
+      if {[llength $lOrder]} {
+        array set sl [list]
+        set lO [list]
+        foreach s $lOrder {
+          array set C $s
+          set ad ""
+          if {$C(desc)} { set ad " DESC" }
+          lappend lO "[lindex $lCols $C(column)]$ad"
+          set sl($C(column)) 1
+        }
+        if {[$hdl distinct]==2} {
+          set selectlist "DISTINCT 0"
+          foreach i {0 1 2} {
+            if {[info exists sl($i)]} {
+              append selectlist ", [lindex $lCols $i]"
+            } else {
+              append selectlist ", 0"
+            }
+          }
+        } else {
+          set order " ORDER BY [join $lO ,]"
+        }
+      }
+
+      set where ""
+      if {[llength $W]} { set where " WHERE [join $W { AND }]" }
+      set sql "SELECT $selectlist FROM $src$where$order$L$O"
+
+      lappend ret idxStr $sql
+      return $ret
+    }
+
+    xFilter {
+      foreach {idxnum idxstr lArg} $args {}
+      set ii 0
+      set sql $idxstr
+      foreach a $lArg {
+        set sql [string map [list %$ii% $a] $sql]
+        set sql [string map [list %I$ii% [join $a ,]] $sql]
+        incr ii
+      }
+      lappend ::lFilterSql $sql
+
+      if {[regexp {OFFSET (.*)$} $sql -> off]} {
+        set real_sql "
+          WITH c(i) AS ( SELECT 1 UNION ALL SELECT i+1 FROM c WHERE i<$off )
+          SELECT 0,0,0,0 FROM c
+          UNION ALL SELECT * FROM (
+            $sql
+          )
+        "
+      } else {
+        set real_sql $sql
+      }
+
+      return [list sql $real_sql]
+    }
+
+  }
+
+  return {}
+}
+
+do_execsql_test 5.0 {
+  CREATE TABLE t1(a, b, c);
+  CREATE VIRTUAL TABLE vt1 USING tcl(vtab_command t1);
+  INSERT INTO t1 VALUES(1, 2, 3);
+  INSERT INTO t1 VALUES(2, 3, 4);
+  INSERT INTO t1 VALUES(3, 4, 5);
+  INSERT INTO t1 VALUES(1, 5, 6);
+  INSERT INTO t1 VALUES(2, 6, 7);
+  INSERT INTO t1 VALUES(3, 7, 8);
+  INSERT INTO t1 VALUES(1, 8, 9);
+  INSERT INTO t1 VALUES(2, 9, 0);
+}
+
+proc do_vtab_test {tn sql vtsql {res {}}} {
+  set ::lFilterSql [list]
+  uplevel [list do_execsql_test $tn.1 $sql $res]
+  uplevel [list do_test $tn.2 {set ::lFilterSql} [list {*}$vtsql]]
+}
+
+do_vtab_test 5.1.1 {
+  SELECT DISTINCT a FROM vt1
+} {
+  {SELECT DISTINCT 0, a, 0, 0 FROM t1}
+} {1 2 3}
+
+do_vtab_test 5.1.2 {
+  SELECT DISTINCT a FROM vt1 ORDER BY a
+} {
+  {SELECT rowid, a, b, c FROM t1 ORDER BY a}
+} {1 2 3}
+
+do_vtab_test 5.1.3 {
+  SELECT DISTINCT a FROM vt1 WHERE c IN (4,5,6,7,8)
+} {
+  {SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c IN (4,5,6,7,8)}
+} {2 3 1}
+
+set ::vtab_handle_in 0
+do_vtab_test 5.1.4 {
+  SELECT  DISTINCT a FROM vt1 WHERE c IN (4,5,6,7,8)
+} {
+  {SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 4}
+  {SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 5}
+  {SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 6}
+  {SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 7}
+  {SELECT DISTINCT 0, a, 0, 0 FROM t1 WHERE c = 8}
+} {2 3 1}
+
+set ::vtab_handle_in 1
+do_vtab_test 5.1.5a {
+  SELECT a, b, c FROM vt1 WHERE c IN (4,5,6,7,8) LIMIT 2 OFFSET 2
+} {
+  {SELECT rowid, a, b, c FROM t1 WHERE c IN (4,5,6,7,8) LIMIT 2 OFFSET 2}
+} {1 5 6 2 6 7}
+
+set ::vtab_handle_in 0
+do_vtab_test 5.1.5b {
+  SELECT a, b, c FROM vt1 WHERE c IN (4,5,6,7,8) LIMIT 2 OFFSET 2
+} {
+  {SELECT rowid, a, b, c FROM t1 WHERE c = 4}
+  {SELECT rowid, a, b, c FROM t1 WHERE c = 5}
+  {SELECT rowid, a, b, c FROM t1 WHERE c = 6}
+  {SELECT rowid, a, b, c FROM t1 WHERE c = 7}
+} {1 5 6 2 6 7}
+set ::vtab_handle_in 1
+
+finish_test
index 58b39f2793dd7ce2a3b8b323c80fda6bc5495969..b91dfa49ac7995cc46188ad6da2f6d55c22f076c 100644 (file)
@@ -46,7 +46,9 @@ proc vtab_command {method args} {
       set OP(glob) GLOB
       set OP(regexp) REGEXP
 
-      set clist [lindex $args 0]
+      set hdl [lindex $args 0]
+      set clist [$hdl constraints]
+
       set ret [list]
       set elist [list]
       set i 0