]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Clean up and clarify code in test8.c. (CVS 3289)
authordanielk1977 <danielk1977@noemail.net>
Sat, 24 Jun 2006 06:36:11 +0000 (06:36 +0000)
committerdanielk1977 <danielk1977@noemail.net>
Sat, 24 Jun 2006 06:36:11 +0000 (06:36 +0000)
FossilOrigin-Name: 4acf7594a6c47142e7112d2cd9766a563401879b

manifest
manifest.uuid
src/test8.c
src/vdbeInt.h

index 74005e23209a96e87520c5418329cbf3f83870a3..3b92deab88f087a9074e679bc04c800075c49a8c 100644 (file)
--- a/manifest
+++ b/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
index efd49bbe1205d3903f30767dd2296cca7d3409e9..0d80e580d273deb7fc4d75def22b30c91254ebb4 100644 (file)
@@ -1 +1 @@
-0534f6e15b84560124c3f1abd05f2967d10261c4
\ No newline at end of file
+4acf7594a6c47142e7112d2cd9766a563401879b
\ No newline at end of file
index 6bcf8a94ac53ae0ba014b6e87704dc7cf924500e..be4327b435dc5ce981e69907b69131f6b6db3e99 100644 (file)
@@ -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++){
index 2a27607ae6940eb4d0ece240b109748a4ebd2c41..cdbc312cb922765e7228e0febd7367bd436d624f 100644 (file)
@@ -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