]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Modify the Tcl interface to use sqlite3_prepare_v2(). (CVS 4542)
authordanielk1977 <danielk1977@noemail.net>
Tue, 13 Nov 2007 10:30:24 +0000 (10:30 +0000)
committerdanielk1977 <danielk1977@noemail.net>
Tue, 13 Nov 2007 10:30:24 +0000 (10:30 +0000)
FossilOrigin-Name: 7bb00c7df672cb8988121f30e3bbdea9ad951603

manifest
manifest.uuid
src/pragma.c
src/prepare.c
src/tclsqlite.c
src/vdbeapi.c
src/vdbeaux.c
test/alter2.test
test/auth.test

index 9b8f2425eec14cc91dded4e4cabbdde6c220a1a5..547d93a03e3e258674506b19ed75a03c0cea013a 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Various\schanges\sto\senable\scompilation\sof\sSQLite\slibrary\sand\scommand\nline\sshell\swith\sthe\sWindows\sCE\scross\scompiler\sfrom\scegcc.sourceforge.net\s(CVS\s4541)
-D 2007-11-12T21:09:11
+C Modify\sthe\sTcl\sinterface\sto\suse\ssqlite3_prepare_v2().\s(CVS\s4542)
+D 2007-11-13T10:30:25
 F Makefile.arm-wince-mingw32ce-gcc ac5f7b2cef0cd850d6f755ba6ee4ab961b1fadf7
 F Makefile.in 30c7e3ba426ddb253b8ef037d1873425da6009a8
 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -125,8 +125,8 @@ F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b
 F src/pager.c c5ffa55c299663b579fbcb430752c1e79d302c5b
 F src/pager.h d783e7f184afdc33adff37ba58d4e029bd8793b3
 F src/parse.y 9640f5b930b1d0bfd05d7428da6f762305567cbb
-F src/pragma.c 363e548dafb52327face8d99757ab56a7b1c1b26
-F src/prepare.c 663284f62a73a48b89f5f4e769e155fefba60384
+F src/pragma.c cb1486e76dbcad757968afc4083d3472032e62b5
+F src/prepare.c ab2374102d8367ad336d160a06cef9a8c96d8964
 F src/printf.c 96c8d55315a13fc53cb3754cb15046f3ff891ea2
 F src/random.c 4a22746501bf36b0a088c66e38dde5daba6a35da
 F src/select.c aef87a179e287c4ab864cb927c1e95f5ee66bf45
@@ -137,7 +137,7 @@ F src/sqlite3ext.h a93f59cdee3638dc0c9c086f80df743a4e68c3cb
 F src/sqliteInt.h 4e6fdeb5630ead97bcec60b941e7a72203c64b9e
 F src/sqliteLimit.h 15ffe2116746c27ace2b428a26a4fcd6dba6fa65
 F src/table.c 1aeb9eab57b4235db86fe15a35dec76fb445a9c4
-F src/tclsqlite.c 8658bb91d32397e289b69445609ffd42d0a922ea
+F src/tclsqlite.c c5953aedd61f0c0cb9f75dcd371e2ae10f899813
 F src/test1.c 0e6fe5449ef9e7289bcaf904e9e3a2ea20650b32
 F src/test2.c 77b34303883b9d722c65a6879bb0163a400e3789
 F src/test3.c 73c1fd55d1ece61f295a6b9204fd97a139de86ae
@@ -169,8 +169,8 @@ F src/vacuum.c a5e51c77370c1a6445e86d42abfc43867cdd482d
 F src/vdbe.c 791d056da2c264c2cfed6e2150852926845875e5
 F src/vdbe.h 856360de71d4bdda71fbb534087668667924e41a
 F src/vdbeInt.h 630145b9bfaa19190ab491f52658a7db550f2247
-F src/vdbeapi.c 1795d8412b6464a59dc4c940803cf53f3d21259c
-F src/vdbeaux.c 7bb91382da8f4e904650ed4a393f7ae83bee8669
+F src/vdbeapi.c eecea7fa87e20664acf270f17c6f397421e9344b
+F src/vdbeaux.c 4b6fb200f3f35fb5983f39ee8527025a372aaa88
 F src/vdbeblob.c 82f51cdf9b0c0af729732fde48c824e498c0a1ca
 F src/vdbefifo.c 334c838c8f42d61a94813d136019ee566b5dc2f6
 F src/vdbemem.c 123994fcd344993d2fb050a83b91b341bbbd08b4
@@ -180,7 +180,7 @@ F tclinstaller.tcl 4356d9d94d2b5ed5e68f9f0c80c4df3048dd7617
 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
 F test/all.test b59d1bd8b0c1d4a08b845e8af48fd43926f01f11
 F test/alter.test a87b7933d41c713c53341abe4eb014d0e273119e
-F test/alter2.test 489140038c13f4f9c76a0a8243eb1249f44d4ce1
+F test/alter2.test 4e0c502214daa710a0a9d7ec9689a0326b1a4e2a
 F test/alter3.test 8ce6b9c5605b3cfe7b901f454ecaf174c4f93e31
 F test/altermalloc.test 29d4a8400277efb4ba8ffe90804c6dc2fdfbf063
 F test/analyze.test 2f55535aa335785db1a2f97d3f3831c16c09f8b0
@@ -191,7 +191,7 @@ F test/attach.test 72529edb04115675894a7399609983ea46b73ba6
 F test/attach2.test 099e46a9a753035ff1b8ec0954e18ea50a934df3
 F test/attach3.test 7b92dc8e40c1ebca9732ca6f2d3fefbd46f196df
 F test/attachmalloc.test 56c5e55563dba6d64641ef2f70ce06900df16912
-F test/auth.test 6d98da67f40475ff9eba99b4a74954c123ba1792
+F test/auth.test 74b49b669cb6ca022124838ff957e1e11b1a8002
 F test/auth2.test 65ac294b8d52cbdd463f61e77ad0165268373126
 F test/autoinc.test 0e67964f4855081e3a325e484adfebaab41f23a1
 F test/autovacuum.test 4339e66003b9cf813dd667a83aed2dee27c4c36d
@@ -587,7 +587,7 @@ F www/tclsqlite.tcl 8be95ee6dba05eabcd27a9d91331c803f2ce2130
 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
 F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5
-P 68a43c99f1b02b8a93bfdcd5c8426b2b4199d68f
-R 15eebd2e25d598175fdc22da0e8e2af5
-U chw
-Z 30b89c8b53d433ead58916dedc043066
+P 8ad0ab8cb374bd34e47af9e71b2aad9dd9af0d1b
+R 7f804fc953f25759b567beb020ffc09d
+U danielk1977
+Z de3fed23e51ed80655b9fa7a173fd027
index a8b086d2dcc0889d3e2669aaf8beadf622504ab9..d55f803d4bc4205ff5b31edd1011515ae5a28031 100644 (file)
@@ -1 +1 @@
-8ad0ab8cb374bd34e47af9e71b2aad9dd9af0d1b
\ No newline at end of file
+7bb00c7df672cb8988121f30e3bbdea9ad951603
\ No newline at end of file
index b4d9774c1accde13e3da5b3633b1f8c180623b07..8f503218a0ef7866d75fa0afe0e4c38fd4749bdc 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** This file contains code used to implement the PRAGMA command.
 **
-** $Id: pragma.c,v 1.149 2007/08/31 18:34:59 drh Exp $
+** $Id: pragma.c,v 1.150 2007/11/13 10:30:25 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -204,8 +204,15 @@ static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){
           }else{
             db->flags &= ~p->mask;
           }
+
+          /* Many of the flag-pragmas modify the code generated by the SQL 
+          ** compiler (eg. count_changes). So add an opcode to expire all
+          ** compiled SQL statements after modifying a pragma value.
+          */
+          sqlite3VdbeAddOp(v, OP_Expire, 0, 0);
         }
       }
+
       return 1;
     }
   }
index 350e0a3087b343ff2d7180045a5da248d357fa2f..ea6a4f42f6af86dc62c04f97edd7146d3b8d3ef4 100644 (file)
@@ -13,7 +13,7 @@
 ** interface, and routines that contribute to loading the database schema
 ** from disk.
 **
-** $Id: prepare.c,v 1.62 2007/10/12 20:42:30 drh Exp $
+** $Id: prepare.c,v 1.63 2007/11/13 10:30:26 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -637,6 +637,9 @@ int sqlite3Reprepare(Vdbe *p){
   assert( sqlite3_mutex_held(db->mutex) );
   rc = sqlite3LockAndPrepare(db, zSql, -1, 0, &pNew, 0);
   if( rc ){
+    if( rc==SQLITE_NOMEM ){
+      db->mallocFailed = 1;
+    }
     assert( pNew==0 );
     return 0;
   }else{
index df7f440bd551939e7f1df9c191b03c146949ee8a..6a9434131e41c517435c2cde9244c2f82efd3ad4 100644 (file)
@@ -12,7 +12,7 @@
 ** A TCL Interface to SQLite.  Append this file to sqlite3.c and
 ** compile the whole thing to build a TCL-enabled version of SQLite.
 **
-** $Id: tclsqlite.c,v 1.205 2007/11/12 17:56:43 drh Exp $
+** $Id: tclsqlite.c,v 1.206 2007/11/13 10:30:26 danielk1977 Exp $
 */
 #include "tcl.h"
 #include <errno.h>
@@ -886,6 +886,67 @@ static char *local_getline(char *zPrompt, FILE *in){
   return zLine;
 }
 
+
+/*
+** Figure out the column names for the data returned by the statement
+** passed as the second argument.
+**
+** If parameter papColName is not NULL, then *papColName is set to point
+** at an array allocated using Tcl_Alloc(). It is the callers responsibility
+** to free this array using Tcl_Free(), and to decrement the reference
+** count of each Tcl_Obj* member of the array.
+**
+** The return value of this function is the number of columns of data
+** returned by pStmt (and hence the size of the *papColName array).
+**
+** If pArray is not NULL, then it contains the name of a Tcl array
+** variable. The "*" member of this array is set to a list containing
+** the names of the columns returned by the statement, in order from
+** left to right. e.g. if the names of the returned columns are a, b and
+** c, it does the equivalent of the tcl command:
+**
+**     set ${pArray}(*) {a b c}
+*/
+static int
+computeColumnNames(
+  Tcl_Interp *interp, 
+  sqlite3_stmt *pStmt,              /* SQL statement */
+  Tcl_Obj ***papColName,            /* OUT: Array of column names */
+  Tcl_Obj *pArray                   /* Name of array variable (may be null) */
+){
+  int nCol;
+
+  /* Compute column names */
+  nCol = sqlite3_column_count(pStmt);
+  if( papColName ){
+    int i;
+    Tcl_Obj **apColName = (Tcl_Obj**)Tcl_Alloc( sizeof(Tcl_Obj*)*nCol );
+    for(i=0; i<nCol; i++){
+      apColName[i] = dbTextToObj(sqlite3_column_name(pStmt,i));
+      Tcl_IncrRefCount(apColName[i]);
+    }
+
+    /* If results are being stored in an array variable, then create
+    ** the array(*) entry for that array
+    */
+    if( pArray ){
+      Tcl_Obj *pColList = Tcl_NewObj();
+      Tcl_Obj *pStar = Tcl_NewStringObj("*", -1);
+      Tcl_IncrRefCount(pColList);
+      for(i=0; i<nCol; i++){
+        Tcl_ListObjAppendElement(interp, pColList, apColName[i]);
+      }
+      Tcl_IncrRefCount(pStar);
+      Tcl_ObjSetVar2(interp, pArray, pStar, pColList,0);
+      Tcl_DecrRefCount(pColList);
+      Tcl_DecrRefCount(pStar);
+    }
+    *papColName = apColName;
+  }
+
+  return nCol;
+}
+
 /*
 ** The "sqlite" command below creates a new Tcl command for each
 ** connection it opens to an SQLite database.  This routine is invoked
@@ -1506,7 +1567,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
     while( rc==TCL_OK && zSql[0] ){
       int i;                     /* Loop counter */
       int nVar;                  /* Number of bind parameters in the pStmt */
-      int nCol;                  /* Number of columns in the result set */
+      int nCol = -1;             /* Number of columns in the result set */
       Tcl_Obj **apColName = 0;   /* Array of column names */
       int len;                   /* String length of zSql */
   
@@ -1514,13 +1575,8 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
       ** which matches the next sequence of SQL.
       */
       pStmt = 0;
-      pPreStmt = pDb->stmtList;
       len = strlen(zSql);
-      if( pPreStmt && sqlite3_expired(pPreStmt->pStmt) ){
-        flushStmtCache(pDb);
-        pPreStmt = 0;
-      }
-      for(; pPreStmt; pPreStmt=pPreStmt->pNext){
+      for(pPreStmt = pDb->stmtList; pPreStmt; pPreStmt=pPreStmt->pNext){
         int n = pPreStmt->nSql;
         if( len>=n 
             && memcmp(pPreStmt->zSql, zSql, n)==0
@@ -1551,7 +1607,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
       /* If no prepared statement was found.  Compile the SQL text
       */
       if( pStmt==0 ){
-        if( SQLITE_OK!=sqlite3_prepare(pDb->db, zSql, -1, &pStmt, &zLeft) ){
+        if( SQLITE_OK!=sqlite3_prepare_v2(pDb->db, zSql, -1, &pStmt, &zLeft) ){
           Tcl_SetObjResult(interp, dbTextToObj(sqlite3_errmsg(pDb->db)));
           rc = TCL_ERROR;
           break;
@@ -1625,36 +1681,20 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
         }
       }
 
-      /* Compute column names */
-      nCol = sqlite3_column_count(pStmt);
-      if( pScript ){
-        apColName = (Tcl_Obj**)Tcl_Alloc( sizeof(Tcl_Obj*)*nCol );
-        if( apColName==0 ) break;
-        for(i=0; i<nCol; i++){
-          apColName[i] = dbTextToObj(sqlite3_column_name(pStmt,i));
-          Tcl_IncrRefCount(apColName[i]);
-        }
-      }
-
-      /* If results are being stored in an array variable, then create
-      ** the array(*) entry for that array
-      */
-      if( pArray ){
-        Tcl_Obj *pColList = Tcl_NewObj();
-        Tcl_Obj *pStar = Tcl_NewStringObj("*", -1);
-        Tcl_IncrRefCount(pColList);
-        for(i=0; i<nCol; i++){
-          Tcl_ListObjAppendElement(interp, pColList, apColName[i]);
-        }
-        Tcl_IncrRefCount(pStar);
-        Tcl_ObjSetVar2(interp, pArray, pStar, pColList,0);
-        Tcl_DecrRefCount(pColList);
-        Tcl_DecrRefCount(pStar);
-      }
-
       /* Execute the SQL
       */
       while( rc==TCL_OK && pStmt && SQLITE_ROW==sqlite3_step(pStmt) ){
+
+       /* Compute column names. This must be done after the first successful
+       ** call to sqlite3_step(), in case the query is recompiled and the
+        ** number or names of the returned columns changes. 
+        */
+        assert(!pArray||pScript);
+        if (nCol < 0) {
+          Tcl_Obj ***ap = (pScript?&apColName:0);
+          nCol = computeColumnNames(interp, pStmt, ap, pArray);
+        }
+
         for(i=0; i<nCol; i++){
           Tcl_Obj *pVal;
           
@@ -1727,6 +1767,14 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
 
       /* Free the column name objects */
       if( pScript ){
+        /* If the query returned no rows, but an array variable was 
+        ** specified, call computeColumnNames() now to populate the 
+        ** arrayname(*) variable.
+        */
+        if (pArray && nCol < 0) {
+          Tcl_Obj ***ap = (pScript?&apColName:0);
+          nCol = computeColumnNames(interp, pStmt, ap, pArray);
+        }
         for(i=0; i<nCol; i++){
           Tcl_DecrRefCount(apColName[i]);
         }
@@ -1745,15 +1793,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
       ** flush the statement cache and try the statement again.
       */
       rc2 = sqlite3_reset(pStmt);
-      if( SQLITE_SCHEMA==rc2 ){
-        /* After a schema change, flush the cache and try to run the
-        ** statement again
-        */
-        flushStmtCache( pDb );
-        sqlite3_finalize(pStmt);
-        if( pPreStmt ) Tcl_Free((char*)pPreStmt);
-        continue;
-      }else if( SQLITE_OK!=rc2 ){
+      if( SQLITE_OK!=rc2 ){
         /* If a run-time error occurs, report the error and stop reading
         ** the SQL
         */
@@ -1854,9 +1894,6 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
     if( rc!=SQLITE_OK ){
       rc = TCL_ERROR;
       Tcl_SetResult(interp, (char *)sqlite3_errmsg(pDb->db), TCL_VOLATILE);
-    }else{
-      /* Must flush any cached statements */
-      flushStmtCache( pDb );
     }
     break;
   }
index 89ac82518c04cd76705e72825bd95f820fe5df47..f4f831448f9781be6df70ab45bb0bf90264b54a1 100644 (file)
@@ -386,14 +386,34 @@ int sqlite3_step(sqlite3_stmt *pStmt){
   int cnt = 0;
   int rc;
   Vdbe *v = (Vdbe*)pStmt;
-  sqlite3_mutex_enter(v->db->mutex);
+  sqlite3 *db = v->db;
+  sqlite3_mutex_enter(db->mutex);
   while( (rc = sqlite3Step(v))==SQLITE_SCHEMA
          && cnt++ < 5
          && sqlite3Reprepare(v) ){
     sqlite3_reset(pStmt);
     v->expired = 0;
   }
-  sqlite3_mutex_leave(v->db->mutex);
+  if( rc==SQLITE_SCHEMA && v->zSql && db->pErr ){
+    /* This case occurs after failing to recompile an sql statement. 
+    ** The error message from the SQL compiler has already been loaded 
+    ** into the database handle. This block copies the error message 
+    ** from the database handle into the statement and sets the statement
+    ** program counter to 0 to ensure that when the statement is 
+    ** finalized or reset the parser error message is available via
+    ** sqlite3_errmsg() and sqlite3_errcode().
+    */
+    const char *zErr = (const char *)sqlite3_value_text(db->pErr); 
+    sqlite3_free(v->zErrMsg);
+    if( !db->mallocFailed ){
+      v->zErrMsg = sqlite3DbStrDup(db, zErr);
+    } else {
+      v->zErrMsg = 0;
+      v->rc = SQLITE_NOMEM;
+    }
+  }
+  rc = sqlite3ApiExit(db, rc);
+  sqlite3_mutex_leave(db->mutex);
   return rc;
 }
 #endif
index 22215b207056ca597c1574bcf0d836ef3d14402f..14f6637755acd3902cbc9c9975586053db83a37f 100644 (file)
@@ -1586,6 +1586,8 @@ int sqlite3VdbeReset(Vdbe *p){
     ** called), set the database error in this case as well.
     */
     sqlite3Error(db, p->rc, 0);
+    sqlite3ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE_UTF8, sqlite3_free);
+    p->zErrMsg = 0;
   }
 
   /* Reclaim all memory used by the VDBE
index e0c667e44f2787de86daefb4be3f6d70e2ebe51b..3f7142d9d190e414c829622f2faa5296b6e59931 100644 (file)
@@ -13,7 +13,7 @@
 # file format change that may be used in the future to implement
 # "ALTER TABLE ... ADD COLUMN".
 #
-# $Id: alter2.test,v 1.10 2007/10/09 08:29:32 danielk1977 Exp $
+# $Id: alter2.test,v 1.11 2007/11/13 10:30:26 danielk1977 Exp $
 #
 
 set testdir [file dirname $argv0]
@@ -239,9 +239,20 @@ do_test alter2-4.1 {
   set_file_format 5
 } {}
 do_test alter2-4.2 {
-  catchsql {
-    SELECT * FROM sqlite_master;
-  }
+  # We have to run two queries here because the Tcl interface uses
+  # sqlite3_prepare_v2(). In this case, the first query encounters an 
+  # SQLITE_SCHEMA error. Then, when trying to recompile the statement, the
+  # "unsupported file format" error is encountered. So the error code
+  # returned is SQLITE_SCHEMA, not SQLITE_ERROR as required by the following
+  # test case.
+  #
+  # When the query is attempted a second time, the same error message is
+  # returned but the error code is SQLITE_ERROR, because the unsupported
+  # file format was detected during a call to sqlite3_prepare(), not
+  # sqlite3_step().
+  #
+  catchsql { SELECT * FROM sqlite_master; }
+  catchsql { SELECT * FROM sqlite_master; }
 } {1 {unsupported file format}}
 do_test alter2-4.3 {
   sqlite3_errcode $::DB
index 2614d9a7bf19e30440560ac9213ff4f987fd3be6..5e43fbe1bb36a6849deb0554221a2cc5587e8b05 100644 (file)
@@ -12,7 +12,7 @@
 # focus of this script is testing the sqlite3_set_authorizer() API
 # and related functionality.
 #
-# $Id: auth.test,v 1.38 2007/10/09 08:29:32 danielk1977 Exp $
+# $Id: auth.test,v 1.39 2007/11/13 10:30:26 danielk1977 Exp $
 #
 
 set testdir [file dirname $argv0]
@@ -2121,6 +2121,16 @@ do_test auth-2.8 {
   catchsql {SELECT ROWID,b,c FROM t2}
 } {0 {{} 2 33 {} 8 9}}
 do_test auth-2.9.1 {
+  # We have to flush the cache here in case the Tcl interface tries to
+  # reuse a statement compiled with sqlite3_prepare_v2(). In this case,
+  # the first error encountered is an SQLITE_SCHEMA error. Then, when
+  # trying to recompile the statement, the authorization error is encountered.
+  # If we do not flush the cache, the correct error message is returned, but
+  # the error code is SQLITE_SCHEMA, not SQLITE_ERROR as required by the test
+  # case after this one.
+  #
+  db cache flush
+
   proc auth {code arg1 arg2 arg3 arg4} {
     if {$code=="SQLITE_READ" && $arg1=="t2" && $arg2=="ROWID"} {
       return bogus