From: drh <> Date: Thu, 20 Jan 2022 17:10:59 +0000 (+0000) Subject: Initial implementation of the sqlite3_vtab_rhs_value() interface and the X-Git-Tag: version-3.38.0~100^2~3 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=82801a5b72809b57f2ced68c239151daa8a1ee44;p=thirdparty%2Fsqlite.git Initial implementation of the sqlite3_vtab_rhs_value() interface and the qpvtab extension used for testing the virtual table interface. FossilOrigin-Name: 0873c76b9b96b66fa9d13ddc8bca126d575ea3352349c7fd648f0c2f75d770f5 --- diff --git a/Makefile.in b/Makefile.in index 9d37f883e9..b70e2d5167 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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 \ diff --git a/Makefile.msc b/Makefile.msc index ef78eae62f..a77d8b0447 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -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 index 0000000000..fb22b61d68 --- /dev/null +++ b/ext/misc/qpvtab.c @@ -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 +#include + +#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->iRowidnData && 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->iRowidnData ){ + int j; + for(j=pCur->iRowid; jnData && 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; inConstraint); + for(i=0; inConstraint; 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 e09505aee2..514776f64b 100644 --- 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 \ diff --git a/manifest b/manifest index 9cea17bdca..426d57cde5 100644 --- 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. diff --git a/manifest.uuid b/manifest.uuid index 9abd24dbef..87e9323d0e 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -71bfd0b57ab197405606b8096b8521d784ff174c4eecf1d9804d38342c03cc80 \ No newline at end of file +0873c76b9b96b66fa9d13ddc8bca126d575ea3352349c7fd648f0c2f75d770f5 \ No newline at end of file diff --git a/src/loadext.c b/src/loadext.c index 681e12c4e7..c917e11c8e 100644 --- a/src/loadext.c +++ b/src/loadext.c @@ -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 diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 3aad3690a2..43a9c8eca7 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -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} diff --git a/src/sqlite3ext.h b/src/sqlite3ext.h index 5b82346227..580078b6d0 100644 --- a/src/sqlite3ext.h +++ b/src/sqlite3ext.h @@ -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) diff --git a/src/test1.c b/src/test1.c index b7fdd2a4bf..b5787f0874 100644 --- a/src/test1.c +++ b/src/test1.c @@ -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 }, diff --git a/src/where.c b/src/where.c index 35bad3f696..4084fd80c8 100644 --- a/src/where.c +++ b/src/where.c @@ -30,8 +30,11 @@ */ 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; inConstraint; 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; }