Clean up and clarify code in test8.c. (CVS 3289)
FossilOrigin-Name: 4acf7594a6c47142e7112d2cd9766a563401879b
This commit is contained in:
parent
f2a3e686dd
commit
cc013f891c
14
manifest
14
manifest
@ -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)
|
||||
D 2006-06-23T14:43:30
|
||||
C Clean\sup\sand\sclarify\scode\sin\stest8.c.\s(CVS\s3289)
|
||||
D 2006-06-24T06:36:11
|
||||
F Makefile.in f839b470345d3cb4b0644068474623fe2464b5d3
|
||||
F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935
|
||||
F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
|
||||
@ -84,7 +84,7 @@ F src/test4.c 8b784cd82de158a2317cb4ac4bc86f91ad315e25
|
||||
F src/test5.c 7162f8526affb771c4ed256826eee7bb9eca265f
|
||||
F src/test6.c 60a02961ceb7b3edc25f5dc5c1ac2556622a76de
|
||||
F src/test7.c 03fa8d787f6aebc6d1f72504d52f33013ad2c8e3
|
||||
F src/test8.c 054989bf4b6f10ffac090e24ce6843662ebb9b91
|
||||
F src/test8.c c7aa1d069087935f3d4eecd685c80a8d4426ece0
|
||||
F src/test_async.c e3deaedd4d86a56391b81808fde9e44fbd92f1d3
|
||||
F src/test_loadext.c 22065d601a18878e5542191001f0eaa5d77c0ed8
|
||||
F src/test_md5.c 6c42bc0a3c0b54be34623ff77a0eec32b2fa96e3
|
||||
@ -99,7 +99,7 @@ F src/util.c ca6ee72772c0f5dc04d2e0ab1973fd3b6a9bf79d
|
||||
F src/vacuum.c 5b37d0f436f8e1ffacd17934e44720b38d2247f9
|
||||
F src/vdbe.c 294e2f35fbd81f5b1e4bd918d2d0bb38313d4b11
|
||||
F src/vdbe.h 258b5d1c0aaa72192f09ff0568ce42b383f156fa
|
||||
F src/vdbeInt.h be3396aa33e7587c96f8c8c264100623a071a3d0
|
||||
F src/vdbeInt.h 37d74cc5651547d76c11682c67286bdf4099b54b
|
||||
F src/vdbeapi.c 6af0e7160af260052a7a4500464221a03dada75f
|
||||
F src/vdbeaux.c 1144cee0a5644c26f63e7fa34574dcd9349ac799
|
||||
F src/vdbefifo.c 9efb94c8c3f4c979ebd0028219483f88e57584f5
|
||||
@ -373,7 +373,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9
|
||||
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
|
||||
F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
|
||||
F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513
|
||||
P a56bfa560425a5dc9273229f8838471dfc402024
|
||||
R e8bb3e94e8692c0623fd2ff8d33a84bc
|
||||
P 0534f6e15b84560124c3f1abd05f2967d10261c4
|
||||
R 28bdb392366e183c1a405e5b76b10720
|
||||
U danielk1977
|
||||
Z d9a57a4940bb8ae48fc84966242bf92b
|
||||
Z baad93a642a5a5dec8ca0ab524eb163c
|
||||
|
@ -1 +1 @@
|
||||
0534f6e15b84560124c3f1abd05f2967d10261c4
|
||||
4acf7594a6c47142e7112d2cd9766a563401879b
|
351
src/test8.c
351
src/test8.c
@ -13,7 +13,7 @@
|
||||
** is not included in the SQLite library. It is used for automated
|
||||
** 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 "tcl.h"
|
||||
@ -21,6 +21,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
|
||||
typedef struct echo_vtab echo_vtab;
|
||||
typedef struct echo_cursor echo_cursor;
|
||||
|
||||
@ -30,6 +32,15 @@ typedef struct echo_cursor echo_cursor;
|
||||
**
|
||||
** $::echo_module
|
||||
** $::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 {
|
||||
sqlite3_vtab base;
|
||||
Tcl_Interp *interp;
|
||||
sqlite3 *db;
|
||||
Tcl_Interp *interp; /* Tcl interpreter containing debug variables */
|
||||
sqlite3 *db; /* Database connection */
|
||||
|
||||
char *zTableName; /* Name of the real table */
|
||||
char *zLogName; /* Name of the log table */
|
||||
@ -59,9 +70,18 @@ struct echo_vtab {
|
||||
struct echo_cursor {
|
||||
sqlite3_vtab_cursor base;
|
||||
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(
|
||||
sqlite3 *db,
|
||||
const char *zTab,
|
||||
@ -69,75 +89,114 @@ static int getColumnNames(
|
||||
int *pnCol
|
||||
){
|
||||
char **aCol = 0;
|
||||
char zBuf[1024];
|
||||
char *zSql;
|
||||
sqlite3_stmt *pStmt = 0;
|
||||
int rc = SQLITE_OK;
|
||||
int nCol = 0;
|
||||
|
||||
sprintf(zBuf, "SELECT * FROM %s", zTab);
|
||||
rc = sqlite3_prepare(db, zBuf, -1, &pStmt, 0);
|
||||
/* Prepare the statement "SELECT * FROM <tbl>". The column names
|
||||
** 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 ){
|
||||
int ii;
|
||||
int nBytes;
|
||||
char *zSpace;
|
||||
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 ){
|
||||
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++){
|
||||
aCol[ii] = sqlite3StrDup(sqlite3_column_name(pStmt, ii));
|
||||
if( !aCol[ii] ){
|
||||
rc = SQLITE_NOMEM;
|
||||
goto fail;
|
||||
}
|
||||
aCol[ii] = zSpace;
|
||||
zSpace += sprintf(zSpace, "%s", sqlite3_column_name(pStmt, ii));
|
||||
zSpace++;
|
||||
}
|
||||
assert( (zSpace-nBytes)==(char *)aCol );
|
||||
}
|
||||
|
||||
*paCol = aCol;
|
||||
*pnCol = nCol;
|
||||
|
||||
fail:
|
||||
out:
|
||||
sqlite3_finalize(pStmt);
|
||||
if( rc!=SQLITE_OK && aCol ){
|
||||
int ii;
|
||||
for(ii=0; ii<nCol; ii++){
|
||||
sqliteFree(aCol[ii]);
|
||||
}
|
||||
sqliteFree(aCol);
|
||||
}
|
||||
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;
|
||||
int nCol;
|
||||
int *aIndex = 0;
|
||||
int rc;
|
||||
char *zSql;
|
||||
|
||||
sprintf(zBuf, "SELECT * FROM %s", zTab);
|
||||
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;
|
||||
}
|
||||
|
||||
/* Allocate space for the index array */
|
||||
aIndex = (int *)sqliteMalloc(sizeof(int) * nCol);
|
||||
if( !aIndex ){
|
||||
rc = SQLITE_NOMEM;
|
||||
goto get_index_array_out;
|
||||
}
|
||||
|
||||
sprintf(zBuf, "PRAGMA index_list(%s)", zTab);
|
||||
rc = sqlite3_prepare(db, zBuf, -1, &pStmt, 0);
|
||||
/* Compile an sqlite pragma to loop through all indices on table zTab */
|
||||
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 ){
|
||||
const char *zIdx = sqlite3_column_text(pStmt, 1);
|
||||
sqlite3_stmt *pStmt2 = 0;
|
||||
sprintf(zBuf, "PRAGMA index_info(%s)", sqlite3_column_text(pStmt, 1));
|
||||
rc = sqlite3_prepare(db, zBuf, -1, &pStmt2, 0);
|
||||
zSql = sqlite3MPrintf("PRAGMA index_info(%s)", zIdx);
|
||||
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 ){
|
||||
int cid = sqlite3_column_int(pStmt2, 1);
|
||||
assert( cid>=0 && cid<nCol );
|
||||
@ -147,16 +206,18 @@ static int getIndexArray(sqlite3 *db, const char *zTab, int **paIndex){
|
||||
rc = sqlite3_finalize(pStmt2);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3_finalize(pStmt);
|
||||
goto get_index_array_out;
|
||||
}
|
||||
}
|
||||
|
||||
if( pStmt ){
|
||||
rc = sqlite3_finalize(pStmt);
|
||||
}
|
||||
|
||||
get_index_array_out:
|
||||
if( pStmt ){
|
||||
int rc2 = sqlite3_finalize(pStmt);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = rc2;
|
||||
}
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqliteFree(aIndex);
|
||||
aIndex = 0;
|
||||
@ -207,9 +268,7 @@ static int echoDeclareVtab(
|
||||
sqlite3_bind_text(pStmt, 1, argv[3], -1, 0);
|
||||
if( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
const char *zCreateTable = sqlite3_column_text(pStmt, 0);
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
sqlite3_declare_vtab(db, zCreateTable);
|
||||
#endif
|
||||
rc = sqlite3_finalize(pStmt);
|
||||
} else {
|
||||
rc = sqlite3_finalize(pStmt);
|
||||
@ -219,23 +278,23 @@ static int echoDeclareVtab(
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = getIndexArray(db, argv[3], &pVtab->aIndex);
|
||||
rc = getColumnNames(db, argv[3], &pVtab->aCol, &pVtab->nCol);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = getColumnNames(db, argv[3], &pVtab->aCol, &pVtab->nCol);
|
||||
rc = getIndexArray(db, argv[3], pVtab->nCol, &pVtab->aIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function frees all runtime structures associated with the virtual
|
||||
** table pVtab.
|
||||
*/
|
||||
static int echoDestructor(sqlite3_vtab *pVtab){
|
||||
int ii;
|
||||
echo_vtab *p = (echo_vtab*)pVtab;
|
||||
sqliteFree(p->aIndex);
|
||||
for(ii=0; ii<p->nCol; ii++){
|
||||
sqliteFree(p->aCol[ii]);
|
||||
}
|
||||
sqliteFree(p->aCol);
|
||||
sqliteFree(p->zTableName);
|
||||
sqliteFree(p->zLogName);
|
||||
@ -243,6 +302,11 @@ static int echoDestructor(sqlite3_vtab *pVtab){
|
||||
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(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
@ -252,32 +316,43 @@ static int echoConstructor(
|
||||
int i;
|
||||
echo_vtab *pVtab;
|
||||
|
||||
/* Allocate the sqlite3_vtab/echo_vtab structure itself */
|
||||
pVtab = sqliteMalloc( sizeof(*pVtab) );
|
||||
if( !pVtab ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
pVtab->interp = (Tcl_Interp *)pAux;
|
||||
pVtab->db = db;
|
||||
|
||||
/* Allocate echo_vtab.zTableName */
|
||||
pVtab->zTableName = sqlite3MPrintf("%s", argv[3]);
|
||||
if( !pVtab->zTableName ){
|
||||
echoDestructor((sqlite3_vtab *)pVtab);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
|
||||
/* Log the arguments to this function to Tcl var ::echo_module */
|
||||
for(i=0; i<argc; 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) ){
|
||||
echoDestructor((sqlite3_vtab *)pVtab);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
/* Success. Set *ppVtab and return */
|
||||
*ppVtab = &pVtab->base;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/* Methods for the echo module */
|
||||
/*
|
||||
** Echo virtual table module xCreate method.
|
||||
*/
|
||||
static int echoCreate(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
@ -287,7 +362,17 @@ static int echoCreate(
|
||||
int rc = SQLITE_OK;
|
||||
appendToEchoModule((Tcl_Interp *)(pAux), "xCreate");
|
||||
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 ){
|
||||
char *zSql;
|
||||
echo_vtab *pVtab = *(echo_vtab **)ppVtab;
|
||||
@ -296,9 +381,13 @@ static int echoCreate(
|
||||
rc = sqlite3_exec(db, zSql, 0, 0, 0);
|
||||
sqliteFree(zSql);
|
||||
}
|
||||
#endif
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Echo virtual table module xConnect method.
|
||||
*/
|
||||
static int echoConnect(
|
||||
sqlite3 *db,
|
||||
void *pAux,
|
||||
@ -309,28 +398,39 @@ static int echoConnect(
|
||||
return echoConstructor(db, pAux, argc, argv, ppVtab);
|
||||
}
|
||||
|
||||
/*
|
||||
** Echo virtual table module xDisconnect method.
|
||||
*/
|
||||
static int echoDisconnect(sqlite3_vtab *pVtab){
|
||||
appendToEchoModule(((echo_vtab *)pVtab)->interp, "xDisconnect");
|
||||
return echoDestructor(pVtab);
|
||||
}
|
||||
|
||||
/*
|
||||
** Echo virtual table module xDestroy method.
|
||||
*/
|
||||
static int echoDestroy(sqlite3_vtab *pVtab){
|
||||
int rc = SQLITE_OK;
|
||||
echo_vtab *p = (echo_vtab *)pVtab;
|
||||
appendToEchoModule(((echo_vtab *)pVtab)->interp, "xDestroy");
|
||||
#if 1
|
||||
|
||||
/* Drop the "log" table, if one exists (see echoCreate() for details) */
|
||||
if( p && p->zLogName ){
|
||||
char *zSql;
|
||||
zSql = sqlite3MPrintf("DROP TABLE %Q", p->zLogName);
|
||||
rc = sqlite3_exec(p->db, zSql, 0, 0, 0);
|
||||
sqliteFree(zSql);
|
||||
}
|
||||
#endif
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = echoDestructor(pVtab);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Echo virtual table module xOpen method.
|
||||
*/
|
||||
static int echoOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
|
||||
echo_cursor *pCur;
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
** Echo virtual table module xClose method.
|
||||
*/
|
||||
static int echoClose(sqlite3_vtab_cursor *cur){
|
||||
int rc;
|
||||
echo_cursor *pCur = (echo_cursor *)cur;
|
||||
@ -356,10 +459,12 @@ static int echoEof(sqlite3_vtab_cursor *cur){
|
||||
return (((echo_cursor *)cur)->pStmt ? 0 : 1);
|
||||
}
|
||||
|
||||
/*
|
||||
** Echo virtual table module xNext method.
|
||||
*/
|
||||
static int echoNext(sqlite3_vtab_cursor *cur){
|
||||
int rc;
|
||||
echo_cursor *pCur = (echo_cursor *)cur;
|
||||
sqlite3_stmt *pStmt = pCur->pStmt;
|
||||
rc = sqlite3_step(pCur->pStmt);
|
||||
|
||||
if( rc==SQLITE_ROW ){
|
||||
@ -372,12 +477,12 @@ static int echoNext(sqlite3_vtab_cursor *cur){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Echo virtual table module xColumn method.
|
||||
*/
|
||||
static int echoColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
|
||||
int iCol = i + 1;
|
||||
sqlite3_stmt *pStmt = ((echo_cursor *)cur)->pStmt;
|
||||
if( ((echo_cursor *)cur)->errcode ){
|
||||
return ((echo_cursor *)cur)->errcode;
|
||||
}
|
||||
if( !pStmt ){
|
||||
sqlite3_result_null(ctx);
|
||||
}else{
|
||||
@ -387,6 +492,9 @@ static int echoColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Echo virtual table module xRowid method.
|
||||
*/
|
||||
static int echoRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
|
||||
sqlite3_stmt *pStmt = ((echo_cursor *)cur)->pStmt;
|
||||
*pRowid = sqlite3_column_int64(pStmt, 0);
|
||||
@ -411,7 +519,9 @@ static int hashString(const char *zString){
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Echo virtual table module xFilter method.
|
||||
*/
|
||||
static int echoFilter(
|
||||
sqlite3_vtab_cursor *pVtabCursor,
|
||||
int idxNum, const char *idxStr,
|
||||
@ -424,52 +534,65 @@ static int echoFilter(
|
||||
echo_vtab *pVtab = (echo_vtab *)pVtabCursor->pVtab;
|
||||
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, idxStr);
|
||||
for(i=0; i<argc; i++){
|
||||
appendToEchoModule(pVtab->interp, sqlite3_value_text(argv[i]));
|
||||
}
|
||||
|
||||
assert( idxNum==hashString(idxStr) );
|
||||
sqlite3_finalize(pCur->pStmt);
|
||||
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);
|
||||
assert( pCur->pStmt || rc!=SQLITE_OK );
|
||||
for(i=0; rc==SQLITE_OK && i<argc; i++){
|
||||
switch( sqlite3_value_type(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;
|
||||
}
|
||||
}
|
||||
sqlite3_bind_value(pCur->pStmt, i+1, argv[i]);
|
||||
}
|
||||
|
||||
/* If everything was successful, advance to the first row of the scan */
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = echoNext(pVtabCursor);
|
||||
}else{
|
||||
assert( !pCur->pStmt );
|
||||
}
|
||||
|
||||
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
|
||||
** 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
|
||||
** to the column "b", but not "a" or "c". If a multi-column index is
|
||||
** 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){
|
||||
int ii;
|
||||
@ -543,13 +676,13 @@ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
zOp = "LIKE"; break;
|
||||
}
|
||||
if( zOp[0]=='L' ){
|
||||
zNew = sqlite3_mprintf("%s %s %s LIKE (SELECT '%%'||?||'%%')",
|
||||
zQuery, zSep, zCol);
|
||||
zNew = sqlite3_mprintf(" %s %s LIKE (SELECT '%%'||?||'%%')",
|
||||
zSep, zCol);
|
||||
} else {
|
||||
zNew = sqlite3_mprintf("%s %s %s %s ?", zQuery, zSep, zCol, zOp);
|
||||
zNew = sqlite3_mprintf(" %s %s %s ?", zSep, zCol, zOp);
|
||||
}
|
||||
sqlite3_free(zQuery);
|
||||
zQuery = zNew;
|
||||
string_concat(&zQuery, zNew, 1);
|
||||
|
||||
zSep = "AND";
|
||||
pUsage->argvIndex = ++nArg;
|
||||
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] ){
|
||||
char *zCol = pVtab->aCol[pIdxInfo->aOrderBy->iColumn];
|
||||
char *zDir = pIdxInfo->aOrderBy->desc?"DESC":"ASC";
|
||||
zNew = sqlite3_mprintf("%s ORDER BY %s %s", zQuery, zCol, zDir);
|
||||
sqlite3_free(zQuery);
|
||||
zQuery = zNew;
|
||||
zNew = sqlite3_mprintf(" ORDER BY %s %s", zCol, zDir);
|
||||
string_concat(&zQuery, zNew, 1);
|
||||
pIdxInfo->orderByConsumed = 1;
|
||||
}
|
||||
|
||||
@ -588,22 +720,9 @@ static int echoBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
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..]
|
||||
**
|
||||
** INTEGER DELETE
|
||||
@ -759,8 +878,8 @@ static int echoRollback(sqlite3_vtab *tab){
|
||||
}
|
||||
|
||||
/*
|
||||
** A virtual table module that merely echos method calls into TCL
|
||||
** variables.
|
||||
** A virtual table module that merely "echos" the contents of another
|
||||
** table (like an SQL VIEW).
|
||||
*/
|
||||
static sqlite3_module echoModule = {
|
||||
0, /* iVersion */
|
||||
@ -792,7 +911,6 @@ static int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite3 **ppDb){
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Register the echo virtual table module.
|
||||
*/
|
||||
@ -808,12 +926,11 @@ static int register_echo_module(
|
||||
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);
|
||||
#endif
|
||||
return TCL_OK;
|
||||
}
|
||||
|
||||
#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
|
||||
|
||||
/*
|
||||
** Register commands with the TCL interpreter.
|
||||
@ -824,7 +941,9 @@ int Sqlitetest8_Init(Tcl_Interp *interp){
|
||||
Tcl_ObjCmdProc *xProc;
|
||||
void *clientData;
|
||||
} aObjCmd[] = {
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
{ "register_echo_module", register_echo_module, 0 },
|
||||
#endif
|
||||
};
|
||||
int i;
|
||||
for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
|
||||
|
@ -85,7 +85,7 @@ struct Cursor {
|
||||
i64 seqCount; /* Sequence counter */
|
||||
#ifndef SQLITE_OMIT_VIRTUALTABLE
|
||||
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
|
||||
|
||||
/* Cached information about the header for the data record that the
|
||||
|
Loading…
Reference in New Issue
Block a user