]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Initial implementation of the sqlite3_vtab_rhs_value() interface and the
authordrh <>
Thu, 20 Jan 2022 17:10:59 +0000 (17:10 +0000)
committerdrh <>
Thu, 20 Jan 2022 17:10:59 +0000 (17:10 +0000)
qpvtab extension used for testing the virtual table interface.

FossilOrigin-Name: 0873c76b9b96b66fa9d13ddc8bca126d575ea3352349c7fd648f0c2f75d770f5

Makefile.in
Makefile.msc
ext/misc/qpvtab.c [new file with mode: 0644]
main.mk
manifest
manifest.uuid
src/loadext.c
src/sqlite.h.in
src/sqlite3ext.h
src/test1.c
src/where.c

index 9d37f883e9bdf2cadef326ce55b6a82eed31e998..b70e2d51677d0a62bc079cb0da144c4c81af24bd 100644 (file)
@@ -460,6 +460,7 @@ TESTSRC += \
   $(TOP)/ext/misc/normalize.c \
   $(TOP)/ext/misc/percentile.c \
   $(TOP)/ext/misc/prefixes.c \
+  $(TOP)/ext/misc/qpvtab.c \
   $(TOP)/ext/misc/regexp.c \
   $(TOP)/ext/misc/remember.c \
   $(TOP)/ext/misc/series.c \
index ef78eae62f17cd66a3c2697cb00f134fea94b4c3..a77d8b04477df649a2dfc9c2094388f270ca23f3 100644 (file)
@@ -1579,6 +1579,7 @@ TESTEXT = \
   $(TOP)\ext\misc\normalize.c \
   $(TOP)\ext\misc\percentile.c \
   $(TOP)\ext\misc\prefixes.c \
+  $(TOP)\ext\misc\qpvtab.c \
   $(TOP)\ext\misc\regexp.c \
   $(TOP)\ext\misc\remember.c \
   $(TOP)\ext\misc\series.c \
diff --git a/ext/misc/qpvtab.c b/ext/misc/qpvtab.c
new file mode 100644 (file)
index 0000000..fb22b61
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+** 2022-01-19
+**
+** 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.
+**
+*************************************************************************
+**
+** This file implements a virtual-table that returns information about
+** how the query planner called the xBestIndex method.  This virtual table
+** is intended for testing and debugging only.
+**
+** The schema of the virtual table is this:
+**
+**    CREATE TABLE qpvtab(a,b,c,d,e, f,g,h,i,j, k,l,m,n,o, p,q,r,s,t);
+**
+** There is also a HIDDEN column "flags".
+**
+** All columns except column "a" have a value that is either TEXT that
+** is there name, or INTEGER which is their index (b==1).  TEXT is the
+** default, but INTEGER is used of there is a constraint on flags where the
+** right-hand side is an integer that includes the 1 bit.
+**
+** The "a" column returns text that describes one of the parameters that
+** xBestIndex was called with.  A completely query of the table should 
+** show all details of how xBestIndex was called.
+*/
+#if !defined(SQLITEINT_H)
+#include "sqlite3ext.h"
+#endif
+SQLITE_EXTENSION_INIT1
+#include <string.h>
+#include <assert.h>
+
+#if !defined(SQLITE_OMIT_VIRTUALTABLE)
+
+/* qpvtab_vtab is a subclass of sqlite3_vtab which is
+** underlying representation of the virtual table
+*/
+typedef struct qpvtab_vtab qpvtab_vtab;
+struct qpvtab_vtab {
+  sqlite3_vtab base;  /* Base class - must be first */
+};
+
+/* qpvtab_cursor is a subclass of sqlite3_vtab_cursor which will
+** serve as the underlying representation of a cursor that scans
+** over rows of the result
+*/
+typedef struct qpvtab_cursor qpvtab_cursor;
+struct qpvtab_cursor {
+  sqlite3_vtab_cursor base;  /* Base class - must be first */
+  sqlite3_int64 iRowid;      /* The rowid */
+  const char *zData;         /* Data to return */
+  int nData;                 /* Number of bytes of data */
+  int flags;                 /* Flags value */
+};
+
+/*
+** The qpvtabConnect() method is invoked to create a new
+** qpvtab virtual table.
+*/
+static int qpvtabConnect(
+  sqlite3 *db,
+  void *pAux,
+  int argc, const char *const*argv,
+  sqlite3_vtab **ppVtab,
+  char **pzErr
+){
+  qpvtab_vtab *pNew;
+  int rc;
+
+  rc = sqlite3_declare_vtab(db,
+         "CREATE TABLE x(a,b,c,d,e, f,g,h,i,j, k,l,m,n,o, p,q,r,s,t,"
+         " flags HIDDEN)"
+       );
+#define QPVTAB_A       0
+#define QPVTAB_B       1
+#define QPVTAB_T       19
+#define QPVTAB_FLAGS   20
+  if( rc==SQLITE_OK ){
+    pNew = sqlite3_malloc( sizeof(*pNew) );
+    *ppVtab = (sqlite3_vtab*)pNew;
+    if( pNew==0 ) return SQLITE_NOMEM;
+    memset(pNew, 0, sizeof(*pNew));
+  }
+  return rc;
+}
+
+/*
+** This method is the destructor for qpvtab_vtab objects.
+*/
+static int qpvtabDisconnect(sqlite3_vtab *pVtab){
+  qpvtab_vtab *p = (qpvtab_vtab*)pVtab;
+  sqlite3_free(p);
+  return SQLITE_OK;
+}
+
+/*
+** Constructor for a new qpvtab_cursor object.
+*/
+static int qpvtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
+  qpvtab_cursor *pCur;
+  pCur = sqlite3_malloc( sizeof(*pCur) );
+  if( pCur==0 ) return SQLITE_NOMEM;
+  memset(pCur, 0, sizeof(*pCur));
+  *ppCursor = &pCur->base;
+  return SQLITE_OK;
+}
+
+/*
+** Destructor for a qpvtab_cursor.
+*/
+static int qpvtabClose(sqlite3_vtab_cursor *cur){
+  qpvtab_cursor *pCur = (qpvtab_cursor*)cur;
+  sqlite3_free(pCur);
+  return SQLITE_OK;
+}
+
+
+/*
+** Advance a qpvtab_cursor to its next row of output.
+*/
+static int qpvtabNext(sqlite3_vtab_cursor *cur){
+  qpvtab_cursor *pCur = (qpvtab_cursor*)cur;
+  while( pCur->iRowid<pCur->nData && pCur->zData[pCur->iRowid]!='\n' ){
+    pCur->iRowid++;
+  }
+  if( pCur->zData[pCur->iRowid]=='\n' ) pCur->iRowid++;
+  return SQLITE_OK;
+}
+
+/*
+** Return values of columns for the row at which the qpvtab_cursor
+** is currently pointing.
+*/
+static int qpvtabColumn(
+  sqlite3_vtab_cursor *cur,   /* The cursor */
+  sqlite3_context *ctx,       /* First argument to sqlite3_result_...() */
+  int i                       /* Which column to return */
+){
+  qpvtab_cursor *pCur = (qpvtab_cursor*)cur;
+  if( i==0 && pCur->iRowid<pCur->nData ){
+    int j;
+    for(j=pCur->iRowid; j<pCur->nData && pCur->zData[j]!='\n'; j++){}
+    sqlite3_result_text64(ctx, &pCur->zData[pCur->iRowid], j-pCur->iRowid,
+                          SQLITE_TRANSIENT, SQLITE_UTF8);
+  }else if( i>=QPVTAB_B && i<=QPVTAB_T ){
+    if( pCur->flags & 1 ){
+      sqlite3_result_int(ctx, i);
+    }else{
+      char x = 'a'+i;
+      sqlite3_result_text64(ctx, &x, 1, SQLITE_TRANSIENT, SQLITE_UTF8);
+    }
+  }else if( i==QPVTAB_FLAGS ){
+    sqlite3_result_int(ctx, pCur->flags);
+  }
+  return SQLITE_OK;
+}
+
+/*
+** Return the rowid for the current row.  In this implementation, the
+** rowid is the same as the output value.
+*/
+static int qpvtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
+  qpvtab_cursor *pCur = (qpvtab_cursor*)cur;
+  *pRowid = pCur->iRowid;
+  return SQLITE_OK;
+}
+
+/*
+** Return TRUE if the cursor has been moved off of the last
+** row of output.
+*/
+static int qpvtabEof(sqlite3_vtab_cursor *cur){
+  qpvtab_cursor *pCur = (qpvtab_cursor*)cur;
+  return pCur->iRowid>=pCur->nData;
+}
+
+/*
+** This method is called to "rewind" the qpvtab_cursor object back
+** to the first row of output.  This method is always called at least
+** once prior to any call to qpvtabColumn() or qpvtabRowid() or 
+** qpvtabEof().
+*/
+static int qpvtabFilter(
+  sqlite3_vtab_cursor *pVtabCursor, 
+  int idxNum, const char *idxStr,
+  int argc, sqlite3_value **argv
+){
+  qpvtab_cursor *pCur = (qpvtab_cursor *)pVtabCursor;
+  pCur->iRowid = 0;
+  pCur->zData = idxStr;
+  pCur->nData = (int)strlen(idxStr);
+  pCur->flags = idxNum;
+  return SQLITE_OK;
+}
+
+/*
+** Append the text of a value to pStr
+*/
+static void qpvtabStrAppendValue(
+  sqlite3_str *pStr,
+  sqlite3_value *pVal
+){
+  switch( sqlite3_value_type(pVal) ){
+    case SQLITE_NULL:
+      sqlite3_str_appendf(pStr, "NULL");
+      break;
+    case SQLITE_INTEGER:
+      sqlite3_str_appendf(pStr, "%lld", sqlite3_value_int64(pVal));
+      break;
+    case SQLITE_FLOAT:
+      sqlite3_str_appendf(pStr, "%f", sqlite3_value_double(pVal));
+      break;
+    case SQLITE_TEXT:
+      sqlite3_str_appendf(pStr, "%Q", sqlite3_value_text(pVal));
+      break;
+    case SQLITE_BLOB: {
+      int i;
+      const unsigned char *a = sqlite3_value_blob(pVal);
+      int n = sqlite3_value_bytes(pVal);
+      sqlite3_str_append(pStr, "x'", 2);
+      for(i=0; i<n; i++){
+        sqlite3_str_appendf(pStr, "%02x", a[i]);
+      }
+      sqlite3_str_append(pStr, "'", 1);
+      break;
+    }
+  }
+}
+
+/*
+** SQLite will invoke this method one or more times while planning a query
+** that uses the virtual table.  This routine needs to create
+** a query plan for each invocation and compute an estimated cost for that
+** plan.
+*/
+static int qpvtabBestIndex(
+  sqlite3_vtab *tab,
+  sqlite3_index_info *pIdxInfo
+){
+  sqlite3_str *pStr = sqlite3_str_new(0);
+  int i, k = 0;
+  sqlite3_str_appendf(pStr, "nConstraint=%d\n", pIdxInfo->nConstraint);
+  for(i=0; i<pIdxInfo->nConstraint; i++){
+    sqlite3_value *pVal;
+    int iCol = pIdxInfo->aConstraint[i].iColumn;
+    char zCol[8];
+    if( iCol==QPVTAB_FLAGS ){
+      strcpy(zCol, "flags");
+      if( pIdxInfo->aConstraint[i].usable ){
+        pVal = 0;
+        sqlite3_vtab_rhs_value(pIdxInfo, i, &pVal);
+        if( pVal ){
+          pIdxInfo->idxNum = sqlite3_value_int(pVal);
+        }
+      }
+    }else{
+      zCol[0] = iCol+'a';
+      zCol[1] = 0;
+    }
+    sqlite3_str_appendf(pStr,"aConstraint[%d]: iColumn=%s op=%d usable=%d",
+       i,
+       zCol,
+       pIdxInfo->aConstraint[i].op,
+       pIdxInfo->aConstraint[i].usable);
+    pVal = 0;
+    sqlite3_vtab_rhs_value(pIdxInfo, i, &pVal);
+    if( pVal ){
+      sqlite3_str_appendf(pStr, " value=");
+      qpvtabStrAppendValue(pStr, pVal);
+    }
+    sqlite3_str_append(pStr, "\n", 1);
+    if( pIdxInfo->aConstraint[i].usable ){
+      pIdxInfo->aConstraintUsage[i].argvIndex = ++k;   
+      pIdxInfo->aConstraintUsage[i].omit = 1;
+    }
+  }
+  pIdxInfo->estimatedCost = (double)10;
+  pIdxInfo->estimatedRows = 10;
+  sqlite3_str_appendf(pStr, "idxNum=%d\n", pIdxInfo->idxNum);
+  pIdxInfo->idxStr = sqlite3_str_finish(pStr);
+  pIdxInfo->needToFreeIdxStr = 1;
+  return SQLITE_OK;
+}
+
+/*
+** This following structure defines all the methods for the 
+** virtual table.
+*/
+static sqlite3_module qpvtabModule = {
+  /* iVersion    */ 0,
+  /* xCreate     */ 0,
+  /* xConnect    */ qpvtabConnect,
+  /* xBestIndex  */ qpvtabBestIndex,
+  /* xDisconnect */ qpvtabDisconnect,
+  /* xDestroy    */ 0,
+  /* xOpen       */ qpvtabOpen,
+  /* xClose      */ qpvtabClose,
+  /* xFilter     */ qpvtabFilter,
+  /* xNext       */ qpvtabNext,
+  /* xEof        */ qpvtabEof,
+  /* xColumn     */ qpvtabColumn,
+  /* xRowid      */ qpvtabRowid,
+  /* xUpdate     */ 0,
+  /* xBegin      */ 0,
+  /* xSync       */ 0,
+  /* xCommit     */ 0,
+  /* xRollback   */ 0,
+  /* xFindMethod */ 0,
+  /* xRename     */ 0,
+  /* xSavepoint  */ 0,
+  /* xRelease    */ 0,
+  /* xRollbackTo */ 0,
+  /* xShadowName */ 0
+};
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
+
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int sqlite3_qpvtab_init(
+  sqlite3 *db, 
+  char **pzErrMsg, 
+  const sqlite3_api_routines *pApi
+){
+  int rc = SQLITE_OK;
+  SQLITE_EXTENSION_INIT2(pApi);
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+  rc = sqlite3_create_module(db, "qpvtab", &qpvtabModule, 0);
+#endif
+  return rc;
+}
diff --git a/main.mk b/main.mk
index e09505aee2daeedbb027f6efbe02beefb6c6d8c1..514776f64bc6af58168a54bf6f2bc3dfa90b8109 100644 (file)
--- a/main.mk
+++ b/main.mk
@@ -377,6 +377,7 @@ TESTSRC += \
   $(TOP)/ext/misc/normalize.c \
   $(TOP)/ext/misc/percentile.c \
   $(TOP)/ext/misc/prefixes.c \
+  $(TOP)/ext/misc/qpvtab.c \
   $(TOP)/ext/misc/regexp.c \
   $(TOP)/ext/misc/remember.c \
   $(TOP)/ext/misc/series.c \
index 9cea17bdcad68cde6fbc08cf12c7fae984cf6061..426d57cde57df66f069ab16f89125eda58f3b228 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,11 +1,11 @@
-C A\sbetter\sand\smore\srobust\sfix\sfor\sthe\sproblem\sof\sreading\sa\sread-only\sWAL\nmode\sdatabase\swith\sexisting\s-wal\sand\s-shm\sfiles,\sreplacing\s[f426874e005e3c23].
-D 2022-01-20T14:40:34.203
+C Initial\simplementation\sof\sthe\ssqlite3_vtab_rhs_value()\sinterface\sand\sthe\nqpvtab\sextension\sused\sfor\stesting\sthe\svirtual\stable\sinterface.
+D 2022-01-20T17:10:59.757
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
-F Makefile.in fd537743957bfe87997dc5727783d8eec82098921e15eab984d6711cd46f001b
+F Makefile.in 3271f3cffa0fb1e214816bbffbdb8a367f8d3b8415eda5346839cf427a138c70
 F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241
-F Makefile.msc 22ce0007874c61c8eb51fc22b84f72af175ce2d7431c242253bdffa39c163da4
+F Makefile.msc eeb45109d245d1bc5d6ce78da818d62848307419f950b0de0a00ab0672b15081
 F README.md 2dd87a5c1d108b224921f3dd47dea567973f706e1f6959386282a626f459a70c
 F VERSION 392c2f83569705069415a5d98b1c138ec8fe8a56a663a0d94cea019e806537b2
 F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
@@ -316,6 +316,7 @@ F ext/misc/noop.c 81efe4cad9ec740e64388b14281cb983e6e2c223fed43eb77ab3e34946e0c1
 F ext/misc/normalize.c bd84355c118e297522aba74de34a4fd286fc775524e0499b14473918d09ea61f
 F ext/misc/percentile.c b9086e223d583bdaf8cb73c98a6539d501a2fc4282654adbfea576453d82e691
 F ext/misc/prefixes.c 0f4f8cff5aebc00a7e3ac4021fd59cfe1a8e17c800ceaf592859ecb9cbc38196
+F ext/misc/qpvtab.c 0f6e3f4081f6ad0104d016bf6e21de8a7f5e3f14b984a2158940a1809741fd96
 F ext/misc/regexp.c b267fd05ff8d38b22f4c2809d7b7a2c61d522e9faf2feb928dbb9662e4a3a386
 F ext/misc/remember.c add730f0f7e7436cd15ea3fd6a90fd83c3f706ab44169f7f048438b7d6baa69c
 F ext/misc/rot13.c 51ac5f51e9d5fd811db58a9c23c628ad5f333c173f1fc53c8491a3603d38556c
@@ -472,7 +473,7 @@ F ext/userauth/userauth.c 7f00cded7dcaa5d47f54539b290a43d2e59f4b1eb5f447545fa865
 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
 F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
 F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
-F main.mk 32e8c752386520016933873a23a9c649f1a9cfd5c75c218614e622f0510b8f42
+F main.mk 3de4bca45fee4b843aaf74df8ffc639aa8ae3c52af997bbbd6927add30154fda
 F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
 F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
 F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
@@ -514,7 +515,7 @@ F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
 F src/insert.c e528416ff5d86fc5d656ea6a26f03fde39836b6175f93048c32a03cb2ee16743
 F src/json.c 78fdec9af3a8bfb5ae685707b2701276fec1942b8f5f26689b2701debe32bcd2
 F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
-F src/loadext.c 95db1fe62c5973f1c5d9c53f6083e21a73ece14cdd47eeca0639691332e85c4d
+F src/loadext.c 9a693eb89575af5d54b025c1e927b2ea8114bbeee3db346e8abc81676dd82160
 F src/main.c 2b6b0dbfeb14d4bb57e368604b0736b2aa42b51b00339d399b01d6b1fc9b4960
 F src/malloc.c ef796bcc0e81d845d59a469f1cf235056caf9024172fd524e32136e65593647b
 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
@@ -553,15 +554,15 @@ F src/resolve.c 359bc0e445d427583d2ab6110433a5dc777f64a0ecdf8d24826d8b475233ead9
 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
 F src/select.c ab5717255420972e69b9b9ce4d1c4730fe82cfbdc14b7743e389a8bdb79ca027
 F src/shell.c.in 4690f216dc4da0c104a8fd9f9e12bec0483242e630324aa7a3ccd155922e346e
-F src/sqlite.h.in a5e0d6bd47e67aabf1475986d36bdcc7bfa9e06566790ebf8e3aa7fa551c9f99
+F src/sqlite.h.in 9257b85dd160fda70e12727881e6c48be0f21ab0d149fa27446505fec5fa4fca
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
-F src/sqlite3ext.h 01eb85e4f2759a5ee79c183f4b2877889d4ffdc49d27ae74529c9579e3c8c0ef
+F src/sqlite3ext.h 234b5ff5c20512a116b14d6d08e23caeb68667749f8a94117779a9d38afc7e5c
 F src/sqliteInt.h 21a31abf60222f50c1d654cdc27ad9d4040249f0341129dd8286b8b5b32bcd30
 F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
 F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
 F src/tclsqlite.c 48f291e1a7e672a7204884d4c164a8ed3a522ff087c361ada2991f5d54e987f6
-F src/test1.c f13fe747afc7d9af189ce0cdaaf641252c5803db2a32bd3525eec2905c7b4f37
+F src/test1.c 9287559cc1f7c5a25f927aa172e69778237f0e037960dbcdb4257d0bea500114
 F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5
 F src/test3.c 61798bb0d38b915067a8c8e03f5a534b431181f802659a6616f9b4ff7d872644
 F src/test4.c 7c4420e01c577b5c4add2cb03119743b1a357543d347773b9e717195ea967159
@@ -638,7 +639,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
 F src/wal.c b9df133a705093da8977da5eb202eaadb844839f1c7297c08d33471f5491843d
 F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
 F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
-F src/where.c eedf0311d59095bcd8523bd13bf25865e1caf1fab9beddbff9093340a1a409c7
+F src/where.c 9f8a9c1c18ab37bbb32ea82c322b09c72e237a89e9005b30089273c6efa5d406
 F src/whereInt.h 91865afa4a3540bb3bd643619acc56fbceff7defeb8f249b8e157fd5325d88be
 F src/wherecode.c 6a594ed25bfbeb60d455868b7be62637575e4f1949152de4336e4825e0c54ba6
 F src/whereexpr.c 9f64c39e53070584e99e4d20c1dd3397e125fabbae8fd414ffec574c410ac7d3
@@ -1938,8 +1939,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P ab160e8bae3a4fc2067d73fe33542f261652985390fe9b0390a4f9c33a1990bf
-R 23c453292071b3de4a1e0dc51f1f1bde
+P 71bfd0b57ab197405606b8096b8521d784ff174c4eecf1d9804d38342c03cc80
+R 2d7b697c8fff979e242ffc760548c840
+T *branch * sqlite3_vtab_rhs_value
+T *sym-sqlite3_vtab_rhs_value *
+T -sym-trunk *
 U drh
-Z 048e904f267f4777d2809de2936b5439
+Z 906d4e9a8d8e3be5e145d8027611963e
 # Remove this line to create a well-formed Fossil manifest.
index 9abd24dbef04422f7b58e43901cb8d0db45aae89..87e9323d0e583ba612e30c18413a6686ea6836d7 100644 (file)
@@ -1 +1 @@
-71bfd0b57ab197405606b8096b8521d784ff174c4eecf1d9804d38342c03cc80
\ No newline at end of file
+0873c76b9b96b66fa9d13ddc8bca126d575ea3352349c7fd648f0c2f75d770f5
\ No newline at end of file
index 681e12c4e798525296e7e4e18da61cfc53849044..c917e11c8eef4accf3d69e8cdc68ad05eaed7100 100644 (file)
@@ -487,6 +487,7 @@ static const sqlite3_api_routines sqlite3Apis = {
   sqlite3_autovacuum_pages,
   /* Version 3.38.0 and later */
   sqlite3_error_offset,
+  sqlite3_vtab_rhs_value,
 };
 
 /* True if x is the directory separator character
index 3aad3690a25be87815504fecd659228b57894d8f..43a9c8eca7ca49e62e6d8b8b4477e4e14c9b5d7f 100644 (file)
@@ -9503,6 +9503,29 @@ int sqlite3_vtab_nochange(sqlite3_context*);
 */
 SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_info*,int);
 
+/*
+** CAPI3REF: Constraint values in xBestIndex()
+** METHOD: sqlite3_index_info
+**
+** This API may only be used from within an xBestIndex() callback. The
+** results of calling it from outside of an xBestIndex() callback are
+** undefined and probably harmful.
+**
+** When the sqlite3_vtab_rhs_value(P,J,V) interface is invoked from within
+** the [xBestIndex] method of a [virtual table] implementation, with P being
+** a copy of the sqlite3_index_info object pointer passed into xBestIndex and
+** J being a 0-based index of one of the constraints, then this routine
+** attempts to set *V to be the value on the right-hand side of
+** that constraint if the right-hand side is a known constant.  If the
+** right-hand side of the constraint is not known, then *V is set to a NULL
+** pointer.
+**
+** The sqlite3_value object returned in *V remains valid for the duration of
+** the xBestIndex method code.  When xBestIndex returns, the sqlite3_value
+** object returned by sqlite3_vtab_rhs_value() is automatically deallocated.
+*/
+int sqlite3_vtab_rhs_value(sqlite3_index_info*, int, sqlite3_value **ppVal);
+
 /*
 ** CAPI3REF: Conflict resolution modes
 ** KEYWORDS: {conflict resolution mode}
index 5b8234622733d6fef62c9f54bd264f4d06f56c12..580078b6d08e19b28ccbcc790e3e137d040e99c9 100644 (file)
@@ -346,6 +346,7 @@ struct sqlite3_api_routines {
      void*, void(*)(void*));
   /* Version 3.38.0 and later */
   int (*error_offset)(sqlite3*);
+  int (*vtab_rhs_value)(sqlite3_index_info*,int,sqlite3_value**);
 };
 
 /*
@@ -659,6 +660,7 @@ typedef int (*sqlite3_loadext_entry)(
 #define sqlite3_autovacuum_pages       sqlite3_api->autovacuum_pages
 /* Version 3.38.0 and later */
 #define sqlite3_error_offset           sqlite3_api->error_offset
+#define sqlite3_vtab_rhs_value         sqlite3_api->vtab_rhs_value
 #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
 
 #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
index b7fdd2a4bfcdede9165ad67fda4f9d7c3077fe47..b5787f087472930771924708968310e79b6e9f9f 100644 (file)
@@ -7568,6 +7568,7 @@ static int SQLITE_TCLAPI tclLoadStaticExtensionCmd(
 #ifndef SQLITE_OMIT_VIRTUALTABLE
   extern int sqlite3_prefixes_init(sqlite3*,char**,const sqlite3_api_routines*);
 #endif
+  extern int sqlite3_qpvtab_init(sqlite3*,char**,const sqlite3_api_routines*);
   extern int sqlite3_regexp_init(sqlite3*,char**,const sqlite3_api_routines*);
   extern int sqlite3_remember_init(sqlite3*,char**,const sqlite3_api_routines*);
   extern int sqlite3_series_init(sqlite3*,char**,const sqlite3_api_routines*);
@@ -7598,6 +7599,7 @@ static int SQLITE_TCLAPI tclLoadStaticExtensionCmd(
 #ifndef SQLITE_OMIT_VIRTUALTABLE
     { "prefixes",              sqlite3_prefixes_init             },
 #endif
+    { "qpvtab",                sqlite3_qpvtab_init               },
     { "regexp",                sqlite3_regexp_init               },
     { "remember",              sqlite3_remember_init             },
     { "series",                sqlite3_series_init               },
index 35bad3f696590e3adae4d0b3df11eed5f89ffeaa..4084fd80c86ae6b344e11ec083647a32002d8967 100644 (file)
 */
 typedef struct HiddenIndexInfo HiddenIndexInfo;
 struct HiddenIndexInfo {
-  WhereClause *pWC;   /* The Where clause being analyzed */
-  Parse *pParse;      /* The parsing context */
+  WhereClause *pWC;        /* The Where clause being analyzed */
+  Parse *pParse;           /* The parsing context */
+  sqlite3_value *aRhs[1];  /* RHS values for constraints. MUST BE LAST
+                           ** because extra space is allocated to hold up
+                           ** to nTerm such values */
 };
 
 /* Forward declaration of methods */
@@ -1095,7 +1098,7 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter(
 /*
 ** Allocate and populate an sqlite3_index_info structure. It is the 
 ** responsibility of the caller to eventually release the structure
-** by passing the pointer returned by this function to sqlite3_free().
+** by passing the pointer returned by this function to freeIndexInfo().
 */
 static sqlite3_index_info *allocateIndexInfo(
   Parse *pParse,                  /* The parsing context */
@@ -1207,13 +1210,14 @@ static sqlite3_index_info *allocateIndexInfo(
   */
   pIdxInfo = sqlite3DbMallocZero(pParse->db, sizeof(*pIdxInfo)
                            + (sizeof(*pIdxCons) + sizeof(*pUsage))*nTerm
-                           + sizeof(*pIdxOrderBy)*nOrderBy + sizeof(*pHidden) );
+                           + sizeof(*pIdxOrderBy)*nOrderBy + sizeof(*pHidden)
+                           + sizeof(sqlite3_value*)*nTerm );
   if( pIdxInfo==0 ){
     sqlite3ErrorMsg(pParse, "out of memory");
     return 0;
   }
   pHidden = (struct HiddenIndexInfo*)&pIdxInfo[1];
-  pIdxCons = (struct sqlite3_index_constraint*)&pHidden[1];
+  pIdxCons = (struct sqlite3_index_constraint*)&pHidden->aRhs[nTerm];
   pIdxOrderBy = (struct sqlite3_index_orderby*)&pIdxCons[nTerm];
   pUsage = (struct sqlite3_index_constraint_usage*)&pIdxOrderBy[nOrderBy];
   pIdxInfo->aConstraint = pIdxCons;
@@ -1278,6 +1282,24 @@ static sqlite3_index_info *allocateIndexInfo(
   return pIdxInfo;
 }
 
+/*
+** Free an sqlite3_index_info structure allocated by allocateIndexInfo()
+** and possibly modified by xBestIndex methods.
+*/
+static void freeIndexInfo(sqlite3 *db, sqlite3_index_info *pIdxInfo){
+  HiddenIndexInfo *pHidden;
+  int i;
+  assert( pIdxInfo!=0 );
+  pHidden = (HiddenIndexInfo*)&pIdxInfo[1];
+  assert( pHidden->pParse!=0 );
+  assert( pHidden->pParse->db==db );
+  for(i=0; i<pIdxInfo->nConstraint; i++){
+    sqlite3ValueFree(pHidden->aRhs[i]);
+    pHidden->aRhs[i] = 0;
+  }
+  sqlite3DbFree(db, pIdxInfo);
+}
+
 /*
 ** The table object reference passed as the second argument to this function
 ** must represent a virtual table. This function invokes the xBestIndex()
@@ -3633,6 +3655,36 @@ const char *sqlite3_vtab_collation(sqlite3_index_info *pIdxInfo, int iCons){
   return zRet;
 }
 
+/*
+** This interface is callable from within the xBestIndex callback only.
+**
+** If possible, set (*ppVal) to point to an object containing the value 
+** on the right-hand-side of constraint iCons.
+*/
+int sqlite3_vtab_rhs_value(
+  sqlite3_index_info *pIdxInfo,   /* Copy of first argument to xBestIndex */
+  int iCons,                      /* Constraint for which RHS is wanted */
+  sqlite3_value **ppVal           /* Write value extracted here */
+){
+  HiddenIndexInfo *pH = (HiddenIndexInfo*)&pIdxInfo[1];
+  sqlite3_value *pVal = 0;
+  int rc = SQLITE_OK;
+  if( iCons<0 || iCons>=pIdxInfo->nConstraint ){
+    rc = SQLITE_MISUSE;
+  }else{
+    if( pH->aRhs[iCons]==0 ){
+      WhereTerm *pTerm = &pH->pWC->a[pIdxInfo->aConstraint[iCons].iTermOffset];
+      rc = sqlite3ValueFromExpr(
+          pH->pParse->db, pTerm->pExpr->pRight, ENC(pH->pParse->db),
+          SQLITE_AFF_BLOB, &pH->aRhs[iCons]
+      );
+    }
+    pVal = pH->aRhs[iCons];
+  }
+  *ppVal = pVal;
+  return rc;
+}
+
 /*
 ** Add all WhereLoop objects for a table of the join identified by
 ** pBuilder->pNew->iTab.  That table is guaranteed to be a virtual table.
@@ -3691,7 +3743,7 @@ static int whereLoopAddVirtual(
   pNew->u.vtab.needFree = 0;
   nConstraint = p->nConstraint;
   if( whereLoopResize(pParse->db, pNew, nConstraint) ){
-    sqlite3DbFree(pParse->db, p);
+    freeIndexInfo(pParse->db, p);
     return SQLITE_NOMEM_BKPT;
   }
 
@@ -3771,7 +3823,7 @@ static int whereLoopAddVirtual(
   }
 
   if( p->needToFreeIdxStr ) sqlite3_free(p->idxStr);
-  sqlite3DbFreeNN(pParse->db, p);
+  freeIndexInfo(pParse->db, p);
   WHERETRACE(0x800, ("END %s.addVirtual(), rc=%d\n", pSrc->pTab->zName, rc));
   return rc;
 }