1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +03:00

Initial implementation of the sqlite3_vtab_rhs_value() interface and the

qpvtab extension used for testing the virtual table interface.

FossilOrigin-Name: 0873c76b9b96b66fa9d13ddc8bca126d575ea3352349c7fd648f0c2f75d770f5
This commit is contained in:
drh
2022-01-20 17:10:59 +00:00
parent f208abdd99
commit 82801a5b72
11 changed files with 446 additions and 21 deletions

View 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 \

View 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 \

338
ext/misc/qpvtab.c Normal file
View File

@ -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;
}

View File

@ -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 \

View File

@ -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.

View File

@ -1 +1 @@
71bfd0b57ab197405606b8096b8521d784ff174c4eecf1d9804d38342c03cc80
0873c76b9b96b66fa9d13ddc8bca126d575ea3352349c7fd648f0c2f75d770f5

View 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

View 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}

View 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)

View 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 },

View File

@ -32,6 +32,9 @@ typedef struct HiddenIndexInfo HiddenIndexInfo;
struct HiddenIndexInfo {
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;
}