Skip to content

Commit 64b17b8

Browse files
committed
New sqlite3_module method: xPrepareSql()
This commit adds a new method named xPrepareSql to sqlite3_module and an associated opcode VPrepareSql. That opcode is emitted immediately before a VFilter opcode when querying virtual tables. xPrepareSql takes two arguments: a pointer to the virtual table's cursor and, missing from existing sqlite3_module methods, the SQL string being executed by the application. Prior to the introduction of this method, a virtual table that mirrored a remote table had no way to tell which columns of the remote server were requested by the application. Therefore, such an implementation would typically `SELECT *` from the remote table and let callbacks such as xColumn() and xNext() decide which data to send back to the application. The problem with this approach is that queries that SELECT just a subset of the remote columns trigger the transfer of data over the network even though they won't be used. xPrepareSql gives an opportunity for the virtual table implementation to inspect the query string and selectively choose which columns from the remote server to pull and cache locally. Because this change introduces a new method to sqlite3_module, new modules wishing to implement a callback for xPrepareSql must declare a module version (iVersion) 4 or above.
1 parent fc32062 commit 64b17b8

5 files changed

Lines changed: 93 additions & 2 deletions

File tree

src/sqlite.h.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7076,6 +7076,8 @@ struct sqlite3_module {
70767076
/* The methods above are in versions 1 and 2 of the sqlite_module object.
70777077
** Those below are for version 3 and greater. */
70787078
int (*xShadowName)(const char*);
7079+
/** The methods below are for version 4 and greater. */
7080+
int (*xPrepareSql)(sqlite3_vtab_cursor*, const char*);
70797081
};
70807082

70817083
/*

src/test8.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,6 +1293,17 @@ static int echoRollbackTo(sqlite3_vtab *pVTab, int iSavepoint){
12931293
return SQLITE_OK;
12941294
}
12951295

1296+
static int echoShadowName(const char *name){
1297+
assert( name );
1298+
return SQLITE_OK;
1299+
}
1300+
1301+
static int echoPrepareSql(sqlite3_vtab_cursor *cur, const char *sql){
1302+
assert( cur );
1303+
assert( sql );
1304+
return SQLITE_OK;
1305+
}
1306+
12961307
/*
12971308
** A virtual table module that merely "echos" the contents of another
12981309
** table (like an SQL VIEW).
@@ -1321,7 +1332,7 @@ static sqlite3_module echoModule = {
13211332
};
13221333

13231334
static sqlite3_module echoModuleV2 = {
1324-
2, /* iVersion */
1335+
4, /* iVersion */
13251336
echoCreate,
13261337
echoConnect,
13271338
echoBestIndex,
@@ -1343,7 +1354,9 @@ static sqlite3_module echoModuleV2 = {
13431354
echoRename, /* xRename - rename the table */
13441355
echoSavepoint,
13451356
echoRelease,
1346-
echoRollbackTo
1357+
echoRollbackTo,
1358+
echoShadowName,
1359+
echoPrepareSql,
13471360
};
13481361

13491362
/*

src/vdbe.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8072,6 +8072,40 @@ case OP_VInitIn: { /* out2 */
80728072
}
80738073
#endif /* SQLITE_OMIT_VIRTUALTABLE */
80748074

8075+
#ifndef SQLITE_OMIT_VIRTUALTABLE
8076+
/* Opcode: VPrepareSql P1 * * * *
8077+
**
8078+
** P1 is a cursor opened using VOpen.
8079+
**
8080+
** This opcode invokes the xPrepareSql method on the virtual table specified
8081+
** by P1. The SQL text parameter to xPrepareSql is obtained from Vdbe* `p`.
8082+
**
8083+
*/
8084+
case OP_VPrepareSql: {
8085+
const sqlite3_module *pModule;
8086+
sqlite3_vtab_cursor *pVCur;
8087+
sqlite3_vtab *pVtab;
8088+
VdbeCursor *pCur;
8089+
8090+
pCur = p->apCsr[pOp->p1];
8091+
assert( pCur!=0 );
8092+
assert( pCur->eCurType==CURTYPE_VTAB );
8093+
pVCur = pCur->uc.pVCur;
8094+
pVtab = pVCur->pVtab;
8095+
pModule = pVtab->pModule;
8096+
8097+
/* Invoke the xPrepareSql method */
8098+
if( pModule->iVersion>=4 ){
8099+
if( pModule->xPrepareSql && p->zSql ){
8100+
rc = pModule->xPrepareSql(pVCur, p->zSql);
8101+
if( rc ) goto abort_due_to_error; // TODO set and test error msg
8102+
}
8103+
}
8104+
8105+
if( rc ) goto abort_due_to_error;
8106+
break;
8107+
}
8108+
#endif /* SQLITE_OMIT_VIRTUALTABLE */
80758109

80768110
#ifndef SQLITE_OMIT_VIRTUALTABLE
80778111
/* Opcode: VFilter P1 P2 P3 P4 *

src/wherecode.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1415,6 +1415,8 @@ Bitmask sqlite3WhereCodeOneLoopStart(
14151415
int addrNotFound;
14161416
int nConstraint = pLoop->nLTerm;
14171417

1418+
sqlite3VdbeAddOp1(v, OP_VPrepareSql, iCur);
1419+
14181420
iReg = sqlite3GetTempRange(pParse, nConstraint+2);
14191421
addrNotFound = pLevel->addrBrk;
14201422
for(j=0; j<nConstraint; j++){

test/rowvaluevtab.test

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ ifcapable !vtab {
2222

2323
register_echo_module db
2424

25+
#############
26+
# Test echo
27+
#############
28+
2529
do_execsql_test 1.0 {
2630
CREATE TABLE t1(a, b, c);
2731
CREATE INDEX t1b ON t1(b);
@@ -92,4 +96,40 @@ do_vfilter4_test 1.4f {
9296
SELECT a FROM e1 WHERE (b, c) IN ( VALUES(2, 2) )
9397
} {{SELECT rowid, a, b, c FROM 't1' WHERE b = ?}}
9498

99+
######################################################################
100+
# Test echo_v2. We simply want to ensure that OP_VPrepareSql executes
101+
######################################################################
102+
103+
do_execsql_test 2.0 {
104+
CREATE TABLE t2(a, b, c);
105+
CREATE INDEX t2b ON t2(b);
106+
INSERT INTO t2 VALUES('one', 1, 1);
107+
INSERT INTO t2 VALUES('two', 1, 2);
108+
INSERT INTO t2 VALUES('three', 1, 3);
109+
110+
WITH s(i) AS (
111+
SELECT 1 UNION ALL SELECT i+1 FROM s WHERE i<10000
112+
) INSERT INTO t2 SELECT NULL, NULL, NULL FROM s;
113+
CREATE VIRTUAL TABLE e2 USING echo_v2(t2);
114+
}
115+
116+
proc do_vpreparesql1_test {tn sql expected} {
117+
set rc -1
118+
db eval "explain $sql" {
119+
if {$opcode=="VPrepareSql"} {
120+
set rc 0
121+
}
122+
}
123+
if {$rc != $expected} {
124+
error "Unexpected result $rc, was hoping for $expected"
125+
}
126+
}
127+
128+
do_execsql_test 2.1 {
129+
SELECT a FROM e2 WHERE (b, c) IN ( VALUES(1, 3) )
130+
} {three}
131+
do_vpreparesql1_test 2.1f {
132+
SELECT a FROM e2 WHERE (b, c) IN ( VALUES(2, 2) )
133+
} {0}
134+
95135
finish_test

0 commit comments

Comments
 (0)