1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-07 02:42:48 +03:00

Clean up and clarify code in test8.c. (CVS 3289)

FossilOrigin-Name: 4acf7594a6c47142e7112d2cd9766a563401879b
This commit is contained in:
danielk1977
2006-06-24 06:36:11 +00:00
parent f2a3e686dd
commit cc013f891c
4 changed files with 244 additions and 125 deletions

View File

@@ -1,5 +1,5 @@
C Modify\sthe\stest\scases\sin\stkt1444.test\sthat\swere\sfailing.\sI\sam\sconvinced\sthat\sthe\stest\scases\swere\sincorrect.\s(CVS\s3288) C Clean\sup\sand\sclarify\scode\sin\stest8.c.\s(CVS\s3289)
D 2006-06-23T14:43:30 D 2006-06-24T06:36:11
F Makefile.in f839b470345d3cb4b0644068474623fe2464b5d3 F Makefile.in f839b470345d3cb4b0644068474623fe2464b5d3
F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935 F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935
F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@@ -84,7 +84,7 @@ F src/test4.c 8b784cd82de158a2317cb4ac4bc86f91ad315e25
F src/test5.c 7162f8526affb771c4ed256826eee7bb9eca265f F src/test5.c 7162f8526affb771c4ed256826eee7bb9eca265f
F src/test6.c 60a02961ceb7b3edc25f5dc5c1ac2556622a76de F src/test6.c 60a02961ceb7b3edc25f5dc5c1ac2556622a76de
F src/test7.c 03fa8d787f6aebc6d1f72504d52f33013ad2c8e3 F src/test7.c 03fa8d787f6aebc6d1f72504d52f33013ad2c8e3
F src/test8.c 054989bf4b6f10ffac090e24ce6843662ebb9b91 F src/test8.c c7aa1d069087935f3d4eecd685c80a8d4426ece0
F src/test_async.c e3deaedd4d86a56391b81808fde9e44fbd92f1d3 F src/test_async.c e3deaedd4d86a56391b81808fde9e44fbd92f1d3
F src/test_loadext.c 22065d601a18878e5542191001f0eaa5d77c0ed8 F src/test_loadext.c 22065d601a18878e5542191001f0eaa5d77c0ed8
F src/test_md5.c 6c42bc0a3c0b54be34623ff77a0eec32b2fa96e3 F src/test_md5.c 6c42bc0a3c0b54be34623ff77a0eec32b2fa96e3
@@ -99,7 +99,7 @@ F src/util.c ca6ee72772c0f5dc04d2e0ab1973fd3b6a9bf79d
F src/vacuum.c 5b37d0f436f8e1ffacd17934e44720b38d2247f9 F src/vacuum.c 5b37d0f436f8e1ffacd17934e44720b38d2247f9
F src/vdbe.c 294e2f35fbd81f5b1e4bd918d2d0bb38313d4b11 F src/vdbe.c 294e2f35fbd81f5b1e4bd918d2d0bb38313d4b11
F src/vdbe.h 258b5d1c0aaa72192f09ff0568ce42b383f156fa F src/vdbe.h 258b5d1c0aaa72192f09ff0568ce42b383f156fa
F src/vdbeInt.h be3396aa33e7587c96f8c8c264100623a071a3d0 F src/vdbeInt.h 37d74cc5651547d76c11682c67286bdf4099b54b
F src/vdbeapi.c 6af0e7160af260052a7a4500464221a03dada75f F src/vdbeapi.c 6af0e7160af260052a7a4500464221a03dada75f
F src/vdbeaux.c 1144cee0a5644c26f63e7fa34574dcd9349ac799 F src/vdbeaux.c 1144cee0a5644c26f63e7fa34574dcd9349ac799
F src/vdbefifo.c 9efb94c8c3f4c979ebd0028219483f88e57584f5 F src/vdbefifo.c 9efb94c8c3f4c979ebd0028219483f88e57584f5
@@ -373,7 +373,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513 F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513
P a56bfa560425a5dc9273229f8838471dfc402024 P 0534f6e15b84560124c3f1abd05f2967d10261c4
R e8bb3e94e8692c0623fd2ff8d33a84bc R 28bdb392366e183c1a405e5b76b10720
U danielk1977 U danielk1977
Z d9a57a4940bb8ae48fc84966242bf92b Z baad93a642a5a5dec8ca0ab524eb163c

View File

@@ -1 +1 @@
0534f6e15b84560124c3f1abd05f2967d10261c4 4acf7594a6c47142e7112d2cd9766a563401879b

View File

@@ -13,7 +13,7 @@
** is not included in the SQLite library. It is used for automated ** is not included in the SQLite library. It is used for automated
** testing of the SQLite library. ** testing of the SQLite library.
** **
** $Id: test8.c,v 1.34 2006/06/23 14:32:08 danielk1977 Exp $ ** $Id: test8.c,v 1.35 2006/06/24 06:36:11 danielk1977 Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
#include "tcl.h" #include "tcl.h"
@@ -21,6 +21,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#ifndef SQLITE_OMIT_VIRTUALTABLE
typedef struct echo_vtab echo_vtab; typedef struct echo_vtab echo_vtab;
typedef struct echo_cursor echo_cursor; typedef struct echo_cursor echo_cursor;
@@ -30,6 +32,15 @@ typedef struct echo_cursor echo_cursor;
** **
** $::echo_module ** $::echo_module
** $::echo_module_sync_fail ** $::echo_module_sync_fail
**
** The variable ::echo_module is a list. Each time one of the following
** methods is called, one or more elements are appended to the list.
** This is used for automated testing of virtual table modules.
**
** The ::echo_module_sync_fail variable is set by test scripts and read
** by code in this file. If it is set to the name of a real table in the
** the database, then all xSync operations on echo virtual tables that
** use the named table as a backing store will fail.
*/ */
/* /*
@@ -45,8 +56,8 @@ typedef struct echo_cursor echo_cursor;
*/ */
struct echo_vtab { struct echo_vtab {
sqlite3_vtab base; sqlite3_vtab base;
Tcl_Interp *interp; Tcl_Interp *interp; /* Tcl interpreter containing debug variables */
sqlite3 *db; sqlite3 *db; /* Database connection */
char *zTableName; /* Name of the real table */ char *zTableName; /* Name of the real table */
char *zLogName; /* Name of the log table */ char *zLogName; /* Name of the log table */
@@ -59,9 +70,18 @@ struct echo_vtab {
struct echo_cursor { struct echo_cursor {
sqlite3_vtab_cursor base; sqlite3_vtab_cursor base;
sqlite3_stmt *pStmt; sqlite3_stmt *pStmt;
int errcode; /* Error code */
}; };
/*
** Retrieve the column names for the table named zTab via database
** connection db. SQLITE_OK is returned on success, or an sqlite error
** code otherwise.
**
** If successful, the number of columns is written to *pnCol. *paCol is
** set to point at sqliteMalloc()'d space containing the array of
** nCol column names. The caller is responsible for calling sqliteFree
** on *paCol.
*/
static int getColumnNames( static int getColumnNames(
sqlite3 *db, sqlite3 *db,
const char *zTab, const char *zTab,
@@ -69,75 +89,114 @@ static int getColumnNames(
int *pnCol int *pnCol
){ ){
char **aCol = 0; char **aCol = 0;
char zBuf[1024]; char *zSql;
sqlite3_stmt *pStmt = 0; sqlite3_stmt *pStmt = 0;
int rc = SQLITE_OK; int rc = SQLITE_OK;
int nCol = 0; int nCol = 0;
sprintf(zBuf, "SELECT * FROM %s", zTab); /* Prepare the statement "SELECT * FROM <tbl>". The column names
rc = sqlite3_prepare(db, zBuf, -1, &pStmt, 0); ** of the result set of the compiled SELECT will be the same as
** the column names of table <tbl>.
*/
zSql = sqlite3MPrintf("SELECT * FROM %Q", zTab);
if( !zSql ){
rc = SQLITE_NOMEM;
goto out;
}
rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
sqliteFree(zSql);
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
int ii; int ii;
int nBytes;
char *zSpace;
nCol = sqlite3_column_count(pStmt); nCol = sqlite3_column_count(pStmt);
aCol = sqliteMalloc(sizeof(char *) * nCol);
/* Figure out how much space to allocate for the array of column names
** (including space for the strings themselves). Then allocate it.
*/
nBytes = sizeof(char *) * nCol;
for(ii=0; ii<nCol; ii++){
nBytes += (strlen(sqlite3_column_name(pStmt, ii)) + 1);
}
aCol = (char **)sqliteMalloc(nBytes);
if( !aCol ){ if( !aCol ){
rc = SQLITE_NOMEM; rc = SQLITE_NOMEM;
goto fail; goto out;
} }
/* Copy the column names into the allocated space and set up the
** pointers in the aCol[] array.
*/
zSpace = (char *)(&aCol[nCol]);
for(ii=0; ii<nCol; ii++){ for(ii=0; ii<nCol; ii++){
aCol[ii] = sqlite3StrDup(sqlite3_column_name(pStmt, ii)); aCol[ii] = zSpace;
if( !aCol[ii] ){ zSpace += sprintf(zSpace, "%s", sqlite3_column_name(pStmt, ii));
rc = SQLITE_NOMEM; zSpace++;
goto fail;
}
} }
assert( (zSpace-nBytes)==(char *)aCol );
} }
*paCol = aCol; *paCol = aCol;
*pnCol = nCol; *pnCol = nCol;
fail: out:
sqlite3_finalize(pStmt); sqlite3_finalize(pStmt);
if( rc!=SQLITE_OK && aCol ){
int ii;
for(ii=0; ii<nCol; ii++){
sqliteFree(aCol[ii]);
}
sqliteFree(aCol);
}
return rc; return rc;
} }
static int getIndexArray(sqlite3 *db, const char *zTab, int **paIndex){ /*
char zBuf[1024]; ** Parameter zTab is the name of a table in database db with nCol
** columns. This function allocates an array of integers nCol in
** size and populates it according to any implicit or explicit
** indices on table zTab.
**
** If successful, SQLITE_OK is returned and *paIndex set to point
** at the allocated array. Otherwise, an error code is returned.
**
** See comments associated with the member variable aIndex above
** "struct echo_vtab" for details of the contents of the array.
*/
static int getIndexArray(
sqlite3 *db, /* Database connection */
const char *zTab, /* Name of table in database db */
int nCol,
int **paIndex
){
sqlite3_stmt *pStmt = 0; sqlite3_stmt *pStmt = 0;
int nCol;
int *aIndex = 0; int *aIndex = 0;
int rc; int rc;
char *zSql;
sprintf(zBuf, "SELECT * FROM %s", zTab); /* Allocate space for the index array */
rc = sqlite3_prepare(db, zBuf, -1, &pStmt, 0);
nCol = sqlite3_column_count(pStmt);
sqlite3_finalize(pStmt);
pStmt = 0;
if( rc!=SQLITE_OK ){
goto get_index_array_out;
}
aIndex = (int *)sqliteMalloc(sizeof(int) * nCol); aIndex = (int *)sqliteMalloc(sizeof(int) * nCol);
if( !aIndex ){ if( !aIndex ){
rc = SQLITE_NOMEM; rc = SQLITE_NOMEM;
goto get_index_array_out; goto get_index_array_out;
} }
sprintf(zBuf, "PRAGMA index_list(%s)", zTab); /* Compile an sqlite pragma to loop through all indices on table zTab */
rc = sqlite3_prepare(db, zBuf, -1, &pStmt, 0); zSql = sqlite3MPrintf("PRAGMA index_list(%s)", zTab);
if( !zSql ){
rc = SQLITE_NOMEM;
goto get_index_array_out;
}
rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
sqliteFree(zSql);
/* For each index, figure out the left-most column and set the
** corresponding entry in aIndex[] to 1.
*/
while( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){ while( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
const char *zIdx = sqlite3_column_text(pStmt, 1);
sqlite3_stmt *pStmt2 = 0; sqlite3_stmt *pStmt2 = 0;
sprintf(zBuf, "PRAGMA index_info(%s)", sqlite3_column_text(pStmt, 1)); zSql = sqlite3MPrintf("PRAGMA index_info(%s)", zIdx);
rc = sqlite3_prepare(db, zBuf, -1, &pStmt2, 0); if( !zSql ){
rc = SQLITE_NOMEM;
goto get_index_array_out;
}
rc = sqlite3_prepare(db, zSql, -1, &pStmt2, 0);
sqliteFree(zSql);
if( pStmt2 && sqlite3_step(pStmt2)==SQLITE_ROW ){ if( pStmt2 && sqlite3_step(pStmt2)==SQLITE_ROW ){
int cid = sqlite3_column_int(pStmt2, 1); int cid = sqlite3_column_int(pStmt2, 1);
assert( cid>=0 && cid<nCol ); assert( cid>=0 && cid<nCol );
@@ -147,16 +206,18 @@ static int getIndexArray(sqlite3 *db, const char *zTab, int **paIndex){
rc = sqlite3_finalize(pStmt2); rc = sqlite3_finalize(pStmt2);
} }
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
sqlite3_finalize(pStmt);
goto get_index_array_out; goto get_index_array_out;
} }
} }
if( pStmt ){
rc = sqlite3_finalize(pStmt);
}
get_index_array_out: get_index_array_out:
if( pStmt ){
int rc2 = sqlite3_finalize(pStmt);
if( rc==SQLITE_OK ){
rc = rc2;
}
}
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
sqliteFree(aIndex); sqliteFree(aIndex);
aIndex = 0; aIndex = 0;
@@ -207,9 +268,7 @@ static int echoDeclareVtab(
sqlite3_bind_text(pStmt, 1, argv[3], -1, 0); sqlite3_bind_text(pStmt, 1, argv[3], -1, 0);
if( sqlite3_step(pStmt)==SQLITE_ROW ){ if( sqlite3_step(pStmt)==SQLITE_ROW ){
const char *zCreateTable = sqlite3_column_text(pStmt, 0); const char *zCreateTable = sqlite3_column_text(pStmt, 0);
#ifndef SQLITE_OMIT_VIRTUALTABLE
sqlite3_declare_vtab(db, zCreateTable); sqlite3_declare_vtab(db, zCreateTable);
#endif
rc = sqlite3_finalize(pStmt); rc = sqlite3_finalize(pStmt);
} else { } else {
rc = sqlite3_finalize(pStmt); rc = sqlite3_finalize(pStmt);
@@ -219,23 +278,23 @@ static int echoDeclareVtab(
} }
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
rc = getIndexArray(db, argv[3], &pVtab->aIndex); rc = getColumnNames(db, argv[3], &pVtab->aCol, &pVtab->nCol);
} }
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
rc = getColumnNames(db, argv[3], &pVtab->aCol, &pVtab->nCol); rc = getIndexArray(db, argv[3], pVtab->nCol, &pVtab->aIndex);
} }
} }
return rc; return rc;
} }
/*
** This function frees all runtime structures associated with the virtual
** table pVtab.
*/
static int echoDestructor(sqlite3_vtab *pVtab){ static int echoDestructor(sqlite3_vtab *pVtab){
int ii;
echo_vtab *p = (echo_vtab*)pVtab; echo_vtab *p = (echo_vtab*)pVtab;
sqliteFree(p->aIndex); sqliteFree(p->aIndex);
for(ii=0; ii<p->nCol; ii++){
sqliteFree(p->aCol[ii]);
}
sqliteFree(p->aCol); sqliteFree(p->aCol);
sqliteFree(p->zTableName); sqliteFree(p->zTableName);
sqliteFree(p->zLogName); sqliteFree(p->zLogName);
@@ -243,6 +302,11 @@ static int echoDestructor(sqlite3_vtab *pVtab){
return 0; return 0;
} }
/*
** This function is called to do the work of the xConnect() method -
** to allocate the required in-memory structures for a newly connected
** virtual table.
*/
static int echoConstructor( static int echoConstructor(
sqlite3 *db, sqlite3 *db,
void *pAux, void *pAux,
@@ -252,32 +316,43 @@ static int echoConstructor(
int i; int i;
echo_vtab *pVtab; echo_vtab *pVtab;
/* Allocate the sqlite3_vtab/echo_vtab structure itself */
pVtab = sqliteMalloc( sizeof(*pVtab) ); pVtab = sqliteMalloc( sizeof(*pVtab) );
if( !pVtab ){ if( !pVtab ){
return SQLITE_NOMEM; return SQLITE_NOMEM;
} }
pVtab->interp = (Tcl_Interp *)pAux; pVtab->interp = (Tcl_Interp *)pAux;
pVtab->db = db; pVtab->db = db;
/* Allocate echo_vtab.zTableName */
pVtab->zTableName = sqlite3MPrintf("%s", argv[3]); pVtab->zTableName = sqlite3MPrintf("%s", argv[3]);
if( !pVtab->zTableName ){ if( !pVtab->zTableName ){
echoDestructor((sqlite3_vtab *)pVtab); echoDestructor((sqlite3_vtab *)pVtab);
return SQLITE_NOMEM; return SQLITE_NOMEM;
} }
/* Log the arguments to this function to Tcl var ::echo_module */
for(i=0; i<argc; i++){ for(i=0; i<argc; i++){
appendToEchoModule(pVtab->interp, argv[i]); appendToEchoModule(pVtab->interp, argv[i]);
} }
/* Invoke sqlite3_declare_vtab and set up other members of the echo_vtab
** structure. If an error occurs, delete the sqlite3_vtab structure and
** return an error code.
*/
if( echoDeclareVtab(pVtab, db, argc, argv) ){ if( echoDeclareVtab(pVtab, db, argc, argv) ){
echoDestructor((sqlite3_vtab *)pVtab); echoDestructor((sqlite3_vtab *)pVtab);
return SQLITE_ERROR; return SQLITE_ERROR;
} }
/* Success. Set *ppVtab and return */
*ppVtab = &pVtab->base; *ppVtab = &pVtab->base;
return SQLITE_OK; return SQLITE_OK;
} }
/* Methods for the echo module */ /*
** Echo virtual table module xCreate method.
*/
static int echoCreate( static int echoCreate(
sqlite3 *db, sqlite3 *db,
void *pAux, void *pAux,
@@ -287,7 +362,17 @@ static int echoCreate(
int rc = SQLITE_OK; int rc = SQLITE_OK;
appendToEchoModule((Tcl_Interp *)(pAux), "xCreate"); appendToEchoModule((Tcl_Interp *)(pAux), "xCreate");
rc = echoConstructor(db, pAux, argc, argv, ppVtab); rc = echoConstructor(db, pAux, argc, argv, ppVtab);
#if 1
/* If there were two arguments passed to the module at the SQL level
** (i.e. "CREATE VIRTUAL TABLE tbl USING echo(arg1, arg2)"), then
** the second argument is used as a table name. Attempt to create
** such a table with a single column, "logmsg". This table will
** be used to log calls to the xUpdate method. It will be deleted
** when the virtual table is DROPed.
**
** Note: The main point of this is to test that we can drop tables
** from within an xDestroy method call.
*/
if( rc==SQLITE_OK && argc==5 ){ if( rc==SQLITE_OK && argc==5 ){
char *zSql; char *zSql;
echo_vtab *pVtab = *(echo_vtab **)ppVtab; echo_vtab *pVtab = *(echo_vtab **)ppVtab;
@@ -296,9 +381,13 @@ static int echoCreate(
rc = sqlite3_exec(db, zSql, 0, 0, 0); rc = sqlite3_exec(db, zSql, 0, 0, 0);
sqliteFree(zSql); sqliteFree(zSql);
} }
#endif
return rc; return rc;
} }
/*
** Echo virtual table module xConnect method.
*/
static int echoConnect( static int echoConnect(
sqlite3 *db, sqlite3 *db,
void *pAux, void *pAux,
@@ -309,28 +398,39 @@ static int echoConnect(
return echoConstructor(db, pAux, argc, argv, ppVtab); return echoConstructor(db, pAux, argc, argv, ppVtab);
} }
/*
** Echo virtual table module xDisconnect method.
*/
static int echoDisconnect(sqlite3_vtab *pVtab){ static int echoDisconnect(sqlite3_vtab *pVtab){
appendToEchoModule(((echo_vtab *)pVtab)->interp, "xDisconnect"); appendToEchoModule(((echo_vtab *)pVtab)->interp, "xDisconnect");
return echoDestructor(pVtab); return echoDestructor(pVtab);
} }
/*
** Echo virtual table module xDestroy method.
*/
static int echoDestroy(sqlite3_vtab *pVtab){ static int echoDestroy(sqlite3_vtab *pVtab){
int rc = SQLITE_OK; int rc = SQLITE_OK;
echo_vtab *p = (echo_vtab *)pVtab; echo_vtab *p = (echo_vtab *)pVtab;
appendToEchoModule(((echo_vtab *)pVtab)->interp, "xDestroy"); appendToEchoModule(((echo_vtab *)pVtab)->interp, "xDestroy");
#if 1
/* Drop the "log" table, if one exists (see echoCreate() for details) */
if( p && p->zLogName ){ if( p && p->zLogName ){
char *zSql; char *zSql;
zSql = sqlite3MPrintf("DROP TABLE %Q", p->zLogName); zSql = sqlite3MPrintf("DROP TABLE %Q", p->zLogName);
rc = sqlite3_exec(p->db, zSql, 0, 0, 0); rc = sqlite3_exec(p->db, zSql, 0, 0, 0);
sqliteFree(zSql); sqliteFree(zSql);
} }
#endif
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
rc = echoDestructor(pVtab); rc = echoDestructor(pVtab);
} }
return rc; return rc;
} }
/*
** Echo virtual table module xOpen method.
*/
static int echoOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ static int echoOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
echo_cursor *pCur; echo_cursor *pCur;
pCur = sqliteMalloc(sizeof(echo_cursor)); pCur = sqliteMalloc(sizeof(echo_cursor));
@@ -338,6 +438,9 @@ static int echoOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
return (pCur ? SQLITE_OK : SQLITE_NOMEM); return (pCur ? SQLITE_OK : SQLITE_NOMEM);
} }
/*
** Echo virtual table module xClose method.
*/
static int echoClose(sqlite3_vtab_cursor *cur){ static int echoClose(sqlite3_vtab_cursor *cur){
int rc; int rc;
echo_cursor *pCur = (echo_cursor *)cur; echo_cursor *pCur = (echo_cursor *)cur;
@@ -356,10 +459,12 @@ static int echoEof(sqlite3_vtab_cursor *cur){
return (((echo_cursor *)cur)->pStmt ? 0 : 1); return (((echo_cursor *)cur)->pStmt ? 0 : 1);
} }
/*
** Echo virtual table module xNext method.
*/
static int echoNext(sqlite3_vtab_cursor *cur){ static int echoNext(sqlite3_vtab_cursor *cur){
int rc; int rc;
echo_cursor *pCur = (echo_cursor *)cur; echo_cursor *pCur = (echo_cursor *)cur;
sqlite3_stmt *pStmt = pCur->pStmt;
rc = sqlite3_step(pCur->pStmt); rc = sqlite3_step(pCur->pStmt);
if( rc==SQLITE_ROW ){ if( rc==SQLITE_ROW ){
@@ -372,12 +477,12 @@ static int echoNext(sqlite3_vtab_cursor *cur){
return rc; return rc;
} }
/*
** Echo virtual table module xColumn method.
*/
static int echoColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){ static int echoColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
int iCol = i + 1; int iCol = i + 1;
sqlite3_stmt *pStmt = ((echo_cursor *)cur)->pStmt; sqlite3_stmt *pStmt = ((echo_cursor *)cur)->pStmt;
if( ((echo_cursor *)cur)->errcode ){
return ((echo_cursor *)cur)->errcode;
}
if( !pStmt ){ if( !pStmt ){
sqlite3_result_null(ctx); sqlite3_result_null(ctx);
}else{ }else{
@@ -387,6 +492,9 @@ static int echoColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
return SQLITE_OK; return SQLITE_OK;
} }
/*
** Echo virtual table module xRowid method.
*/
static int echoRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ static int echoRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
sqlite3_stmt *pStmt = ((echo_cursor *)cur)->pStmt; sqlite3_stmt *pStmt = ((echo_cursor *)cur)->pStmt;
*pRowid = sqlite3_column_int64(pStmt, 0); *pRowid = sqlite3_column_int64(pStmt, 0);
@@ -411,7 +519,9 @@ static int hashString(const char *zString){
return val; return val;
} }
/*
** Echo virtual table module xFilter method.
*/
static int echoFilter( static int echoFilter(
sqlite3_vtab_cursor *pVtabCursor, sqlite3_vtab_cursor *pVtabCursor,
int idxNum, const char *idxStr, int idxNum, const char *idxStr,
@@ -424,52 +534,65 @@ static int echoFilter(
echo_vtab *pVtab = (echo_vtab *)pVtabCursor->pVtab; echo_vtab *pVtab = (echo_vtab *)pVtabCursor->pVtab;
sqlite3 *db = pVtab->db; sqlite3 *db = pVtab->db;
/* Check that idxNum matches idxStr */
assert( idxNum==hashString(idxStr) );
/* Log arguments to the ::echo_module Tcl variable */
appendToEchoModule(pVtab->interp, "xFilter"); appendToEchoModule(pVtab->interp, "xFilter");
appendToEchoModule(pVtab->interp, idxStr); appendToEchoModule(pVtab->interp, idxStr);
for(i=0; i<argc; i++){ for(i=0; i<argc; i++){
appendToEchoModule(pVtab->interp, sqlite3_value_text(argv[i])); appendToEchoModule(pVtab->interp, sqlite3_value_text(argv[i]));
} }
assert( idxNum==hashString(idxStr) );
sqlite3_finalize(pCur->pStmt); sqlite3_finalize(pCur->pStmt);
pCur->pStmt = 0; pCur->pStmt = 0;
/* Prepare the SQL statement created by echoBestIndex and bind the
** runtime parameters passed to this function to it.
*/
rc = sqlite3_prepare(db, idxStr, -1, &pCur->pStmt, 0); rc = sqlite3_prepare(db, idxStr, -1, &pCur->pStmt, 0);
assert( pCur->pStmt || rc!=SQLITE_OK ); assert( pCur->pStmt || rc!=SQLITE_OK );
for(i=0; rc==SQLITE_OK && i<argc; i++){ for(i=0; rc==SQLITE_OK && i<argc; i++){
switch( sqlite3_value_type(argv[i]) ){ sqlite3_bind_value(pCur->pStmt, i+1, argv[i]);
case SQLITE_INTEGER: {
sqlite3_bind_int64(pCur->pStmt, i+1, sqlite3_value_int64(argv[i]));
break;
}
case SQLITE_FLOAT: {
sqlite3_bind_double(pCur->pStmt, i+1, sqlite3_value_double(argv[i]));
break;
}
case SQLITE_NULL: {
sqlite3_bind_null(pCur->pStmt, i+1);
break;
}
case SQLITE_TEXT: {
sqlite3_bind_text(pCur->pStmt, i+1, sqlite3_value_text(argv[i]),
sqlite3_value_bytes(argv[i]), SQLITE_TRANSIENT);
break;
}
case SQLITE_BLOB: {
sqlite3_bind_blob(pCur->pStmt, i+1, sqlite3_value_blob(argv[i]),
sqlite3_value_bytes(argv[i]), SQLITE_TRANSIENT);
break;
}
}
} }
/* If everything was successful, advance to the first row of the scan */
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
rc = echoNext(pVtabCursor); rc = echoNext(pVtabCursor);
}else{
assert( !pCur->pStmt );
} }
return rc; return rc;
} }
/*
** A helper function used by echoUpdate() and echoBestIndex() for
** manipulating strings in concert with the sqlite3_mprintf() function.
**
** Parameter pzStr points to a pointer to a string allocated with
** sqlite3_mprintf. The second parameter, zAppend, points to another
** string. The two strings are concatenated together and *pzStr
** set to point at the result. The initial buffer pointed to by *pzStr
** is deallocated via sqlite3_free().
**
** If the third argument, doFree, is true, then sqlite3_free() is
** also called to free the buffer pointed to by zAppend.
*/
static void string_concat(char **pzStr, char *zAppend, int doFree){
char *zIn = *pzStr;
if( zIn ){
char *zTemp = zIn;
zIn = sqlite3_mprintf("%s%s", zIn, zAppend);
sqlite3_free(zTemp);
}else{
zIn = sqlite3_mprintf("%s", zAppend);
}
*pzStr = zIn;
if( doFree ){
sqlite3_free(zAppend);
}
}
/* /*
** The echo module implements the subset of query constraints and sort ** The echo module implements the subset of query constraints and sort
** orders that may take advantage of SQLite indices on the underlying ** orders that may take advantage of SQLite indices on the underlying
@@ -481,6 +604,16 @@ static int echoFilter(
** then the echo module handles WHERE or ORDER BY clauses that refer ** then the echo module handles WHERE or ORDER BY clauses that refer
** to the column "b", but not "a" or "c". If a multi-column index is ** to the column "b", but not "a" or "c". If a multi-column index is
** present, only it's left most column is considered. ** present, only it's left most column is considered.
**
** This xBestIndex method encodes the proposed search strategy as
** an SQL query on the real table underlying the virtual echo module
** table and stores the query in sqlite3_index_info.idxStr. The SQL
** statement is of the form:
**
** SELECT rowid, * FROM <real-table> ?<where-clause>? ?<order-by-clause>?
**
** where the <where-clause> and <order-by-clause> are determined
** by the contents of the structure pointed to by the pIdxInfo argument.
*/ */
static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
int ii; int ii;
@@ -543,13 +676,13 @@ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
zOp = "LIKE"; break; zOp = "LIKE"; break;
} }
if( zOp[0]=='L' ){ if( zOp[0]=='L' ){
zNew = sqlite3_mprintf("%s %s %s LIKE (SELECT '%%'||?||'%%')", zNew = sqlite3_mprintf(" %s %s LIKE (SELECT '%%'||?||'%%')",
zQuery, zSep, zCol); zSep, zCol);
} else { } else {
zNew = sqlite3_mprintf("%s %s %s %s ?", zQuery, zSep, zCol, zOp); zNew = sqlite3_mprintf(" %s %s %s ?", zSep, zCol, zOp);
} }
sqlite3_free(zQuery); string_concat(&zQuery, zNew, 1);
zQuery = zNew;
zSep = "AND"; zSep = "AND";
pUsage->argvIndex = ++nArg; pUsage->argvIndex = ++nArg;
pUsage->omit = 1; pUsage->omit = 1;
@@ -563,9 +696,8 @@ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
if( pIdxInfo->nOrderBy==1 && pVtab->aIndex[pIdxInfo->aOrderBy->iColumn] ){ if( pIdxInfo->nOrderBy==1 && pVtab->aIndex[pIdxInfo->aOrderBy->iColumn] ){
char *zCol = pVtab->aCol[pIdxInfo->aOrderBy->iColumn]; char *zCol = pVtab->aCol[pIdxInfo->aOrderBy->iColumn];
char *zDir = pIdxInfo->aOrderBy->desc?"DESC":"ASC"; char *zDir = pIdxInfo->aOrderBy->desc?"DESC":"ASC";
zNew = sqlite3_mprintf("%s ORDER BY %s %s", zQuery, zCol, zDir); zNew = sqlite3_mprintf(" ORDER BY %s %s", zCol, zDir);
sqlite3_free(zQuery); string_concat(&zQuery, zNew, 1);
zQuery = zNew;
pIdxInfo->orderByConsumed = 1; pIdxInfo->orderByConsumed = 1;
} }
@@ -588,22 +720,9 @@ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
return rc; return rc;
} }
static void string_concat(char **pzStr, char *zAppend, int doFree){
char *zIn = *pzStr;
if( zIn ){
char *zTemp = zIn;
zIn = sqlite3_mprintf("%s%s", zIn, zAppend);
sqlite3_free(zTemp);
}else{
zIn = sqlite3_mprintf("%s", zAppend);
}
*pzStr = zIn;
if( doFree ){
sqlite3_free(zAppend);
}
}
/* /*
** The xUpdate method for echo module virtual tables.
**
** apData[0] apData[1] apData[2..] ** apData[0] apData[1] apData[2..]
** **
** INTEGER DELETE ** INTEGER DELETE
@@ -759,8 +878,8 @@ static int echoRollback(sqlite3_vtab *tab){
} }
/* /*
** A virtual table module that merely echos method calls into TCL ** A virtual table module that merely "echos" the contents of another
** variables. ** table (like an SQL VIEW).
*/ */
static sqlite3_module echoModule = { static sqlite3_module echoModule = {
0, /* iVersion */ 0, /* iVersion */
@@ -792,7 +911,6 @@ static int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb){
return TCL_OK; return TCL_OK;
} }
/* /*
** Register the echo virtual table module. ** Register the echo virtual table module.
*/ */
@@ -808,12 +926,11 @@ static int register_echo_module(
return TCL_ERROR; return TCL_ERROR;
} }
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
#ifndef SQLITE_OMIT_VIRTUALTABLE
sqlite3_create_module(db, "echo", &echoModule, (void *)interp); sqlite3_create_module(db, "echo", &echoModule, (void *)interp);
#endif
return TCL_OK; return TCL_OK;
} }
#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
/* /*
** Register commands with the TCL interpreter. ** Register commands with the TCL interpreter.
@@ -824,7 +941,9 @@ int Sqlitetest8_Init(Tcl_Interp *interp){
Tcl_ObjCmdProc *xProc; Tcl_ObjCmdProc *xProc;
void *clientData; void *clientData;
} aObjCmd[] = { } aObjCmd[] = {
#ifndef SQLITE_OMIT_VIRTUALTABLE
{ "register_echo_module", register_echo_module, 0 }, { "register_echo_module", register_echo_module, 0 },
#endif
}; };
int i; int i;
for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){

View File

@@ -85,7 +85,7 @@ struct Cursor {
i64 seqCount; /* Sequence counter */ i64 seqCount; /* Sequence counter */
#ifndef SQLITE_OMIT_VIRTUALTABLE #ifndef SQLITE_OMIT_VIRTUALTABLE
sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */ sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */
sqlite3_module *pModule; /* Module for cursor pVtabCursor */ const sqlite3_module *pModule; /* Module for cursor pVtabCursor */
#endif #endif
/* Cached information about the header for the data record that the /* Cached information about the header for the data record that the