]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
First cut at adding the sqlite3_prepare_v2() API. Test cases added, but
authordrh <drh@noemail.net>
Thu, 9 Nov 2006 00:24:53 +0000 (00:24 +0000)
committerdrh <drh@noemail.net>
Thu, 9 Nov 2006 00:24:53 +0000 (00:24 +0000)
more testing would be useful.  Still need to update the documentation. (CVS 3506)

FossilOrigin-Name: f1efae9224170c9155afcf17ab3ee769a557b874

13 files changed:
manifest
manifest.uuid
src/prepare.c
src/sqlite.h.in
src/sqliteInt.h
src/test1.c
src/vdbe.h
src/vdbeInt.h
src/vdbeapi.c
src/vdbeaux.c
test/capi3c.test [new file with mode: 0644]
test/quick.test
test/schema2.test [new file with mode: 0644]

index e110c5c422cb20ac694d971c5b1a16dc01330ca7..1ed733d1a6bf11d0d564a0e1449738e25163bb9b 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Make\sthe\s.exit\sand\s.quit\scommands\swork\sagain\sin\sthe\sshell.\s\sTicket\s#2056.\s(CVS\s3505)
-D 2006-11-08T12:25:43
+C First\scut\sat\sadding\sthe\ssqlite3_prepare_v2()\sAPI.\s\sTest\scases\sadded,\sbut\nmore\stesting\swould\sbe\suseful.\s\sStill\sneed\sto\supdate\sthe\sdocumentation.\s(CVS\s3506)
+D 2006-11-09T00:24:54
 F Makefile.in 8e14898d41a53033ecb687d93c9cd5d109fb9ae3
 F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935
 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@@ -89,18 +89,18 @@ F src/pager.c 3114c819a6cb86a2499396819c5fd6f4f8165546
 F src/pager.h 2e6d42f4ae004ae748a037b8468112b851c447a7
 F src/parse.y 8c79a1debbd92a4f5609511e9bf0222de78f5ecb
 F src/pragma.c 2ef4353448e202961a22312f34695128bbb6d69a
-F src/prepare.c 3d9a1bb0644e8bccb3b78cb0833d269719237f4e
+F src/prepare.c f4f45b4560defbb566cf8255763625d2c09a8023
 F src/printf.c b179b6ed12f793e028dd169e2e2e2b2a37eedc63
 F src/random.c d40f8d356cecbd351ccfab6eaedd7ec1b54f5261
 F src/select.c 6ba6d8ead43d0575ce1f8b418cc039f8f301389a
 F src/server.c 087b92a39d883e3fa113cae259d64e4c7438bc96
 F src/shell.c 41513f31dce4252198944c712b79ea4c92df2592
-F src/sqlite.h.in bf935004029631fd93d119bcf2f7259b9cb9ad5e
+F src/sqlite.h.in 2931f7ee2415e7a49fd12f386c23575046f0f540
 F src/sqlite3ext.h 2c2156cc32a158e2b7bd9042d42accf94bff2e40
-F src/sqliteInt.h 637ef229c3d8e0f98096ab31c496efdf5361d678
+F src/sqliteInt.h f6bac44ee7b8ee2614b046974551c22999ec674f
 F src/table.c 6d0da66dde26ee75614ed8f584a1996467088d06
 F src/tclsqlite.c e029f739bed90071789fe81a226d53e97a80a4d8
-F src/test1.c 20ca02cba62082b58a5e59f1278632dbd32024a1
+F src/test1.c 3d0cb8837eb13e6cd5fe2b76a37ba8d9c0ad77fc
 F src/test2.c ca74a1d8aeb7d9606e8f6b762c5daf85c1a3f92b
 F src/test3.c 85135c09560c48bdb0a23c9b890ab405486b8ec9
 F src/test4.c 8b784cd82de158a2317cb4ac4bc86f91ad315e25
@@ -122,10 +122,10 @@ F src/utf.c 67ecb1032bc0b42c105e88d65ef9d9f626eb0e1f
 F src/util.c 91d4cb189476906639ae611927d939691d1365f6
 F src/vacuum.c f6a7943f1f1002cb82ef2ea026cb1975a5b687cb
 F src/vdbe.c 3e31f2e49423955afb2ac905ceacf54c679b1f28
-F src/vdbe.h 258b5d1c0aaa72192f09ff0568ce42b383f156fa
-F src/vdbeInt.h e3eaab262b67b84474625cfc38aec1125c32834b
-F src/vdbeapi.c f1858a5edc3a5e32d038514dd9e7e9091400a782
-F src/vdbeaux.c 78c744f127df03ea63098ebb7dd7fe3eb303e284
+F src/vdbe.h 9720cae673359dc2bdcb106285ecf686b7d3ef24
+F src/vdbeInt.h 1ca07f2d7446c90230346aed7fbf990c032460bc
+F src/vdbeapi.c 2d1e6843af8705a1172e54a418d2a3d5febd1dd7
+F src/vdbeaux.c 05cc6f0f82b86dfb4c356e06ab07ec8cc83a2eda
 F src/vdbefifo.c 9efb94c8c3f4c979ebd0028219483f88e57584f5
 F src/vdbemem.c 26623176bf1c616aa478da958fac49502491a921
 F src/vtab.c aa30e940058ea56a1b7a9a7019ec21d307316fb4
@@ -169,6 +169,7 @@ F test/busy.test 0271c854738e23ad76e10d4096a698e5af29d211
 F test/capi2.test cb478885b8b1a6a9f703a9da1c8d7d101c0970d6
 F test/capi3.test 5f54824e8356ad25ee40a101b36452e74d68a945
 F test/capi3b.test 5f0bc94b104e11086b1103b20277e1910f59c7f4
+F test/capi3c.test d45ecc2e06879967a6a0786dff1caa2195f08ac1
 F test/cast.test f88e7b6946e9a467cf4bb142d92bb65a83747fc2
 F test/check.test e5ea0c1a06c10e81e3434ca029e2c4a562f2b673
 F test/collate1.test add9454cef160677bb8b34148b8f277ce7f9f1c4
@@ -268,13 +269,14 @@ F test/pagesize.test 05c74ea49f790734ec1e9ab765d9bf1cce79b8f2
 F test/pragma.test b81f483cac7b8fe519988a102303799cc72d25a2
 F test/printf.test cdd8e20dd901382a385afcbaa777b9377815c2ad
 F test/progress.test 8b22b4974b0a95272566385f8cb8c341c7130df8 x
-F test/quick.test 4eabf0fd67dc6e116cbebe3f8d899608239eae50
+F test/quick.test 71ed89a1a516fe1594b178e0001342eafcd2f32a
 F test/quote.test 5891f2338980916cf7415484b4ce785294044adb
 F test/reindex.test 38b138abe36bf9a08c791ed44d9f76cd6b97b78b
 F test/rollback.test 673cd8c44c685ad54987fe7f0eeba84efa09685d
 F test/rowid.test 040a3bef06f970c45f5fcd14b2355f7f4d62f0cf
 F test/safety.test 4a06934e45d03b8b50ebcd8d174eb0367d2fd851
 F test/schema.test 8a2ae440fb15f5798a68059e8746402f3137be46
+F test/schema2.test d815923e57e90b8c60ddf5e0d8fd65075e94f57f
 F test/select1.test fa4e941da0a38961635d903530bd292dc149a8e8
 F test/select2.test f3c2678c3a9f3cf08ec4988a3845bda64be6d9e3
 F test/select3.test 33c78663e6b1b41220dcec4eb6affb1a05001ffe
@@ -419,7 +421,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9
 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
 F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513
-P d309680ec7a806d181b601e0105aebf1e33bfb81
-R e590c00bb49173f4e5a5df7e97598d24
+P f39978ef13e986a16ee322ee84ab9bd38ffc5a8b
+R dc22d9f5d0aa421e42db62c318429739
 U drh
-Z 928b8f12ac488a19e769eda3ed1d6f5a
+Z d20637c076532a0c72ad06c1b6f375c3
index 93dcfe127973247c576d53cc77da5f15fd3327d1..c0741feff17e7c6a16fc01fd6cdadf92a1693368 100644 (file)
@@ -1 +1 @@
-f39978ef13e986a16ee322ee84ab9bd38ffc5a8b
\ No newline at end of file
+f1efae9224170c9155afcf17ab3ee769a557b874
\ No newline at end of file
index 0dc34d78328836b1972559ef465ce3cde3b49483..636f8cdc39aa288919a0bc97627b305fd911d735 100644 (file)
@@ -13,7 +13,7 @@
 ** interface, and routines that contribute to loading the database schema
 ** from disk.
 **
-** $Id: prepare.c,v 1.40 2006/09/23 20:36:02 drh Exp $
+** $Id: prepare.c,v 1.41 2006/11/09 00:24:54 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -445,12 +445,13 @@ int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){
 /*
 ** Compile the UTF-8 encoded SQL statement zSql into a statement handle.
 */
-int sqlite3_prepare(
+int sqlite3Prepare(
   sqlite3 *db,              /* Database handle. */
   const char *zSql,         /* UTF-8 encoded SQL statement. */
   int nBytes,               /* Length of zSql in bytes. */
+  int saveSqlFlag,          /* True to copy SQL text into the sqlite3_stmt */
   sqlite3_stmt **ppStmt,    /* OUT: A pointer to the prepared statement */
-  const char** pzTail       /* OUT: End of parsed string */
+  const char **pzTail       /* OUT: End of parsed string */
 ){
   Parse sParse;
   char *zErrMsg = 0;
@@ -503,7 +504,9 @@ int sqlite3_prepare(
   if( sqlite3MallocFailed() ){
     sParse.rc = SQLITE_NOMEM;
   }
-  if( pzTail ) *pzTail = sParse.zTail;
+  if( pzTail ){
+    *pzTail = sParse.zTail;
+  }
   rc = sParse.rc;
 
 #ifndef SQLITE_OMIT_EXPLAIN
@@ -521,13 +524,16 @@ int sqlite3_prepare(
       sqlite3VdbeSetColName(sParse.pVdbe, 3, COLNAME_NAME, "p2", P3_STATIC);
       sqlite3VdbeSetColName(sParse.pVdbe, 4, COLNAME_NAME, "p3", P3_STATIC);
     }
-  } 
+  }
 #endif
 
   if( sqlite3SafetyOff(db) ){
     rc = SQLITE_MISUSE;
   }
   if( rc==SQLITE_OK ){
+    if( saveSqlFlag ){
+      sqlite3VdbeSetSql(sParse.pVdbe, zSql, sParse.zTail - zSql);
+    }
     *ppStmt = (sqlite3_stmt*)sParse.pVdbe;
   }else if( sParse.pVdbe ){
     sqlite3_finalize((sqlite3_stmt*)sParse.pVdbe);
@@ -546,14 +552,72 @@ int sqlite3_prepare(
   return rc;
 }
 
+/*
+** Rerun the compilation of a statement after a schema change.
+** Return true if the statement was recompiled successfully.
+** Return false if there is an error of some kind.
+*/
+int sqlite3Reprepare(Vdbe *p){
+  int rc;
+  Vdbe *pNew;
+  const char *zSql;
+  sqlite3 *db;
+  
+  zSql = sqlite3VdbeGetSql(p);
+  if( zSql==0 ){
+    return 0;
+  }
+  db = sqlite3VdbeDb(p);
+  rc = sqlite3Prepare(db, zSql, -1, 0, (sqlite3_stmt**)&pNew, 0);
+  if( rc ){
+    assert( pNew==0 );
+    return 0;
+  }else{
+    assert( pNew!=0 );
+  }
+  sqlite3VdbeSwapOps(pNew, p);
+  sqlite3_finalize((sqlite3_stmt*)pNew);
+  return 1;
+}
+
+
+/*
+** Two versions of the official API.  Legacy and new use.  In the legacy
+** version, the original SQL text is not saved in the prepared statement
+** and so if a schema change occurs, SQLITE_SCHEMA is returned by
+** sqlite3_step().  In the new version, the original SQL text is retained
+** and the statement is automatically recompiled if an schema change
+** occurs.
+*/
+int sqlite3_prepare(
+  sqlite3 *db,              /* Database handle. */
+  const char *zSql,         /* UTF-8 encoded SQL statement. */
+  int nBytes,               /* Length of zSql in bytes. */
+  sqlite3_stmt **ppStmt,    /* OUT: A pointer to the prepared statement */
+  const char **pzTail       /* OUT: End of parsed string */
+){
+  return sqlite3Prepare(db,zSql,nBytes,0,ppStmt,pzTail);
+}
+int sqlite3_prepare_v2(
+  sqlite3 *db,              /* Database handle. */
+  const char *zSql,         /* UTF-8 encoded SQL statement. */
+  int nBytes,               /* Length of zSql in bytes. */
+  sqlite3_stmt **ppStmt,    /* OUT: A pointer to the prepared statement */
+  const char **pzTail       /* OUT: End of parsed string */
+){
+  return sqlite3Prepare(db,zSql,nBytes,1,ppStmt,pzTail);
+}
+
+
 #ifndef SQLITE_OMIT_UTF16
 /*
 ** Compile the UTF-16 encoded SQL statement zSql into a statement handle.
 */
-int sqlite3_prepare16(
+static int sqlite3Prepare16(
   sqlite3 *db,              /* Database handle. */ 
   const void *zSql,         /* UTF-8 encoded SQL statement. */
   int nBytes,               /* Length of zSql in bytes. */
+  int saveSqlFlag,          /* True to save SQL text into the sqlite3_stmt */
   sqlite3_stmt **ppStmt,    /* OUT: A pointer to the prepared statement */
   const void **pzTail       /* OUT: End of parsed string */
 ){
@@ -570,7 +634,7 @@ int sqlite3_prepare16(
   }
   zSql8 = sqlite3utf16to8(zSql, nBytes);
   if( zSql8 ){
-    rc = sqlite3_prepare(db, zSql8, -1, ppStmt, &zTail8);
+    rc = sqlite3Prepare(db, zSql8, -1, saveSqlFlag, ppStmt, &zTail8);
   }
 
   if( zTail8 && pzTail ){
@@ -585,4 +649,32 @@ int sqlite3_prepare16(
   sqliteFree(zSql8); 
   return sqlite3ApiExit(db, rc);
 }
+
+/*
+** Two versions of the official API.  Legacy and new use.  In the legacy
+** version, the original SQL text is not saved in the prepared statement
+** and so if a schema change occurs, SQLITE_SCHEMA is returned by
+** sqlite3_step().  In the new version, the original SQL text is retained
+** and the statement is automatically recompiled if an schema change
+** occurs.
+*/
+int sqlite3_prepare16(
+  sqlite3 *db,              /* Database handle. */ 
+  const void *zSql,         /* UTF-8 encoded SQL statement. */
+  int nBytes,               /* Length of zSql in bytes. */
+  sqlite3_stmt **ppStmt,    /* OUT: A pointer to the prepared statement */
+  const void **pzTail       /* OUT: End of parsed string */
+){
+  return sqlite3Prepare16(db,zSql,nBytes,0,ppStmt,pzTail);
+}
+int sqlite3_prepare16_v2(
+  sqlite3 *db,              /* Database handle. */ 
+  const void *zSql,         /* UTF-8 encoded SQL statement. */
+  int nBytes,               /* Length of zSql in bytes. */
+  sqlite3_stmt **ppStmt,    /* OUT: A pointer to the prepared statement */
+  const void **pzTail       /* OUT: End of parsed string */
+){
+  return sqlite3Prepare16(db,zSql,nBytes,1,ppStmt,pzTail);
+}
+
 #endif /* SQLITE_OMIT_UTF16 */
index f54bf816b7d71afbf57b084d8e81e9ca8b16f052..80450e88cb56462153ed99f68072d055f71b6dbc 100644 (file)
@@ -12,7 +12,7 @@
 ** This header file defines the interface that the SQLite library
 ** presents to client programs.
 **
-** @(#) $Id: sqlite.h.in,v 1.194 2006/09/16 21:45:14 drh Exp $
+** @(#) $Id: sqlite.h.in,v 1.195 2006/11/09 00:24:54 drh Exp $
 */
 #ifndef _SQLITE3_H_
 #define _SQLITE3_H_
@@ -692,6 +692,31 @@ int sqlite3_prepare16(
   const void **pzTail     /* OUT: Pointer to unused portion of zSql */
 );
 
+/*
+** Newer versions of the prepare API work just like the legacy versions
+** but with one exception:  The a copy of the SQL text is saved in the
+** sqlite3_stmt structure that is returned.  If this copy exists, it
+** modifieds the behavior of sqlite3_step() slightly.  First, sqlite3_step()
+** will no longer return an SQLITE_SCHEMA error but will instead automatically
+** rerun the compiler to rebuild the prepared statement.  Secondly, 
+** sqlite3_step() now turns a full result code - the result code that
+** use used to have to call sqlite3_reset() to get.
+*/
+int sqlite3_prepare_v2(
+  sqlite3 *db,            /* Database handle */
+  const char *zSql,       /* SQL statement, UTF-8 encoded */
+  int nBytes,             /* Length of zSql in bytes. */
+  sqlite3_stmt **ppStmt,  /* OUT: Statement handle */
+  const char **pzTail     /* OUT: Pointer to unused portion of zSql */
+);
+int sqlite3_prepare16_v2(
+  sqlite3 *db,            /* Database handle */
+  const void *zSql,       /* SQL statement, UTF-16 encoded */
+  int nBytes,             /* Length of zSql in bytes. */
+  sqlite3_stmt **ppStmt,  /* OUT: Statement handle */
+  const void **pzTail     /* OUT: Pointer to unused portion of zSql */
+);
+
 /*
 ** Pointers to the following two opaque structures are used to communicate
 ** with the implementations of user-defined functions.
index eb86caf4966d87b409d584612aff06347eee19d7..a3d4317d2e9868944d311b48219ca78bece6cff8 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.529 2006/09/23 20:36:02 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.530 2006/11/09 00:24:54 drh Exp $
 */
 #ifndef _SQLITEINT_H_
 #define _SQLITEINT_H_
@@ -1876,6 +1876,7 @@ int sqlite3VtabCallDestroy(sqlite3*, int, const char *);
 int sqlite3VtabBegin(sqlite3 *, sqlite3_vtab *);
 FuncDef *sqlite3VtabOverloadFunction(FuncDef*, int nArg, Expr*);
 void sqlite3InvalidFunction(sqlite3_context*,int,sqlite3_value**);
+int sqlite3Reprepare(Vdbe*);
 
 #ifdef SQLITE_SSE
 #include "sseInt.h"
index 92ecab83fc502f1275da0035ce0ac0c6449f4aca..cc4f4f1d781970a8f74ade76dd300fbb7a86ef05 100644 (file)
@@ -13,7 +13,7 @@
 ** is not included in the SQLite library.  It is used for automated
 ** testing of the SQLite library.
 **
-** $Id: test1.c,v 1.223 2006/10/19 20:27:59 shess Exp $
+** $Id: test1.c,v 1.224 2006/11/09 00:24:54 drh Exp $
 */
 #include "sqliteInt.h"
 #include "tcl.h"
@@ -2489,7 +2489,60 @@ static int test_prepare(
 }
 
 /*
-** Usage: sqlite3_prepare DB sql bytes tailvar
+** Usage: sqlite3_prepare_v2 DB sql bytes tailvar
+**
+** Compile up to <bytes> bytes of the supplied SQL string <sql> using
+** database handle <DB>. The parameter <tailval> is the name of a global
+** variable that is set to the unused portion of <sql> (if any). A
+** STMT handle is returned.
+*/
+static int test_prepare_v2(
+  void * clientData,
+  Tcl_Interp *interp,
+  int objc,
+  Tcl_Obj *CONST objv[]
+){
+  sqlite3 *db;
+  const char *zSql;
+  int bytes;
+  const char *zTail = 0;
+  sqlite3_stmt *pStmt = 0;
+  char zBuf[50];
+  int rc;
+
+  if( objc!=5 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", 
+       Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0);
+    return TCL_ERROR;
+  }
+  if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+  zSql = Tcl_GetString(objv[2]);
+  if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR;
+
+  rc = sqlite3_prepare_v2(db, zSql, bytes, &pStmt, &zTail);
+  if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
+  if( zTail ){
+    if( bytes>=0 ){
+      bytes = bytes - (zTail-zSql);
+    }
+    Tcl_ObjSetVar2(interp, objv[4], 0, Tcl_NewStringObj(zTail, bytes), 0);
+  }
+  if( rc!=SQLITE_OK ){
+    assert( pStmt==0 );
+    sprintf(zBuf, "(%d) ", rc);
+    Tcl_AppendResult(interp, zBuf, sqlite3_errmsg(db), 0);
+    return TCL_ERROR;
+  }
+
+  if( pStmt ){
+    if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR;
+    Tcl_AppendResult(interp, zBuf, 0);
+  }
+  return TCL_OK;
+}
+
+/*
+** Usage: sqlite3_prepare16 DB sql bytes tailvar
 **
 ** Compile up to <bytes> bytes of the supplied SQL string <sql> using
 ** database handle <DB>. The parameter <tailval> is the name of a global
@@ -2546,6 +2599,64 @@ static int test_prepare16(
   return TCL_OK;
 }
 
+/*
+** Usage: sqlite3_prepare16_v2 DB sql bytes tailvar
+**
+** Compile up to <bytes> bytes of the supplied SQL string <sql> using
+** database handle <DB>. The parameter <tailval> is the name of a global
+** variable that is set to the unused portion of <sql> (if any). A
+** STMT handle is returned.
+*/
+static int test_prepare16_v2(
+  void * clientData,
+  Tcl_Interp *interp,
+  int objc,
+  Tcl_Obj *CONST objv[]
+){
+#ifndef SQLITE_OMIT_UTF16
+  sqlite3 *db;
+  const void *zSql;
+  const void *zTail = 0;
+  Tcl_Obj *pTail = 0;
+  sqlite3_stmt *pStmt = 0;
+  char zBuf[50]; 
+  int rc;
+  int bytes;                /* The integer specified as arg 3 */
+  int objlen;               /* The byte-array length of arg 2 */
+
+  if( objc!=5 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", 
+       Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0);
+    return TCL_ERROR;
+  }
+  if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+  zSql = Tcl_GetByteArrayFromObj(objv[2], &objlen);
+  if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR;
+
+  rc = sqlite3_prepare16_v2(db, zSql, bytes, &pStmt, &zTail);
+  if( sqlite3TestErrCode(interp, db, rc) ) return TCL_ERROR;
+  if( rc ){
+    return TCL_ERROR;
+  }
+
+  if( zTail ){
+    objlen = objlen - ((u8 *)zTail-(u8 *)zSql);
+  }else{
+    objlen = 0;
+  }
+  pTail = Tcl_NewByteArrayObj((u8 *)zTail, objlen);
+  Tcl_IncrRefCount(pTail);
+  Tcl_ObjSetVar2(interp, objv[4], 0, pTail, 0);
+  Tcl_DecrRefCount(pTail);
+
+  if( pStmt ){
+    if( sqlite3TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR;
+  }
+  Tcl_AppendResult(interp, zBuf, 0);
+#endif /* SQLITE_OMIT_UTF16 */
+  return TCL_OK;
+}
+
 /*
 ** Usage: sqlite3_open filename ?options-list?
 */
@@ -3883,6 +3994,8 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
 
      { "sqlite3_prepare",               test_prepare       ,0 },
      { "sqlite3_prepare16",             test_prepare16     ,0 },
+     { "sqlite3_prepare_v2",            test_prepare_v2    ,0 },
+     { "sqlite3_prepare16_v2",          test_prepare16_v2  ,0 },
      { "sqlite3_finalize",              test_finalize      ,0 },
      { "sqlite3_reset",                 test_reset         ,0 },
      { "sqlite3_expired",               test_expired       ,0 },
index 46f6fe045d033ee22445cb72061adac53487b410..f499c18ad6e0adae75dc1f11ff899230814279fd 100644 (file)
@@ -15,7 +15,7 @@
 ** or VDBE.  The VDBE implements an abstract machine that runs a
 ** simple program to access and modify the underlying database.
 **
-** $Id: vdbe.h,v 1.105 2006/06/13 23:51:35 drh Exp $
+** $Id: vdbe.h,v 1.106 2006/11/09 00:24:54 drh Exp $
 */
 #ifndef _SQLITE_VDBE_H_
 #define _SQLITE_VDBE_H_
@@ -135,6 +135,9 @@ void sqlite3VdbeSetNumCols(Vdbe*,int);
 int sqlite3VdbeSetColName(Vdbe*, int, int, const char *, int);
 void sqlite3VdbeCountChanges(Vdbe*);
 sqlite3 *sqlite3VdbeDb(Vdbe*);
+void sqlite3VdbeSetSql(Vdbe*, const char *z, int n);
+const char *sqlite3VdbeGetSql(Vdbe*);
+void sqlite3VdbeSwapOps(Vdbe*,Vdbe*);
 
 #ifndef NDEBUG
   void sqlite3VdbeComment(Vdbe*, const char*, ...);
index db8034061b58dbd408a2df5370978c45f027e583..71d162d724a0dd91bece7761f935039cfffdcfaf 100644 (file)
@@ -328,6 +328,8 @@ struct Vdbe {
   u8 inVtabMethod;        /* See comments above */
   int nChange;            /* Number of db changes made since last reset */
   i64 startTime;          /* Time when query started - used for profiling */
+  int nSql;             /* Number of bytes in zSql */
+  char *zSql;           /* Text of the SQL statement that generated this */
 #ifdef SQLITE_SSE
   int fetchId;          /* Statement number used by sqlite3_fetch_statement */
   int lru;              /* Counter used for LRU cache replacement */
index 4758e967305b43b6cc3e81649aed324bf037becb..9440bbd10539a1167a0c27fd3357ab2adbb7ef95 100644 (file)
@@ -153,9 +153,13 @@ void sqlite3_result_value(sqlite3_context *pCtx, sqlite3_value *pValue){
 /*
 ** Execute the statement pStmt, either until a row of data is ready, the
 ** statement is completely executed or an error occurs.
+**
+** This routine implements the bulk of the logic behind the sqlite_step()
+** API.  The only thing omitted is the automatic recompile if a 
+** schema change has occurred.  That detail is handled by the
+** outer sqlite3_step() wrapper procedure.
 */
-int sqlite3_step(sqlite3_stmt *pStmt){
-  Vdbe *p = (Vdbe*)pStmt;
+static int sqlite3Step(Vdbe *p){
   sqlite3 *db;
   int rc;
 
@@ -172,7 +176,8 @@ int sqlite3_step(sqlite3_stmt *pStmt){
     if( p->rc==SQLITE_OK ){
       p->rc = SQLITE_SCHEMA;
     }
-    return SQLITE_ERROR;
+    rc = SQLITE_ERROR;
+    goto end_of_step;
   }
   db = p->db;
   if( sqlite3SafetyOn(db) ){
@@ -254,9 +259,42 @@ int sqlite3_step(sqlite3_stmt *pStmt){
 
   sqlite3Error(p->db, rc, 0);
   p->rc = sqlite3ApiExit(p->db, p->rc);
+end_of_step:
   assert( (rc&0xff)==rc );
+  if( p->zSql && (rc&0xff)<SQLITE_ROW ){
+    /* This behavior occurs if sqlite3_prepare_v2() was used to build
+    ** the prepared statement.  Return error codes directly */
+    return p->rc;
+  }else{
+    /* This is for legacy sqlite3_prepare() builds and when the code
+    ** is SQLITE_ROW or SQLITE_DONE */
+    return rc;
+  }
+}
+
+/*
+** This is the top-level implementation of sqlite3_step().  Call
+** sqlite3Step() to do most of the work.  If a schema error occurs,
+** call sqlite3Reprepare() and try again.
+*/
+#ifdef SQLITE_OMIT_PARSER
+int sqlite3_step(sqlite3_stmt *pStmt){
+  return sqlite3Step((Vdbe*)pStmt);
+}
+#else
+int sqlite3_step(sqlite3_stmt *pStmt){
+  int cnt = 0;
+  int rc;
+  Vdbe *v = (Vdbe*)pStmt;
+  while( (rc = sqlite3Step(v))==SQLITE_SCHEMA
+         && cnt++ < 5
+         && sqlite3Reprepare(v) ){
+    sqlite3_reset(pStmt);
+    v->expired = 0;
+  }
   return rc;
 }
+#endif
 
 /*
 ** Extract the user data from a sqlite3_context structure and return a
index f0f3b7097d169d378f29244cdba26d95c876ce74..5064cf4e0469691a590b5b042a24877b973e9080 100644 (file)
@@ -48,6 +48,38 @@ Vdbe *sqlite3VdbeCreate(sqlite3 *db){
   return p;
 }
 
+/*
+** Remember the SQL string for a prepared statement.
+*/
+void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n){
+  if( p==0 ) return;
+  assert( p->zSql==0 );
+  p->zSql = sqlite3StrNDup(z, n);
+}
+
+/*
+** Return the SQL associated with a prepared statement
+*/
+const char *sqlite3VdbeGetSql(Vdbe *p){
+  return p->zSql;
+}
+
+/*
+** Swap the set of Opcodes between to Vdbe structures.  No
+** other parts of either Vdbe structure are changed.
+*/
+void sqlite3VdbeSwapOps(Vdbe *pA, Vdbe *pB){
+  Op *aOp;
+  int nOp;
+  
+  aOp = pA->aOp;
+  nOp = pA->nOp;
+  pA->aOp = pB->aOp;
+  pA->nOp = pB->nOp;
+  pB->aOp = aOp;
+  pB->nOp = nOp;
+}
+
 /*
 ** Turn tracing on or off
 */
@@ -1574,6 +1606,7 @@ void sqlite3VdbeDelete(Vdbe *p){
   sqliteFree(p->aStack);
   releaseMemArray(p->aColName, p->nResColumn*COLNAME_N);
   sqliteFree(p->aColName);
+  sqliteFree(p->zSql);
   p->magic = VDBE_MAGIC_DEAD;
   sqliteFree(p);
 }
diff --git a/test/capi3c.test b/test/capi3c.test
new file mode 100644 (file)
index 0000000..74c11d8
--- /dev/null
@@ -0,0 +1,1052 @@
+# 2006 November 08
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library.  
+#
+# This is a copy of the capi3.test file that has been adapted to
+# test the new sqlite3_prepare_v2 interface.
+#
+# $Id: capi3c.test,v 1.1 2006/11/09 00:24:55 drh Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+# Return the UTF-16 representation of the supplied UTF-8 string $str.
+# If $nt is true, append two 0x00 bytes as a nul terminator.
+proc utf16 {str {nt 1}} {
+  set r [encoding convertto unicode $str]
+  if {$nt} {
+    append r "\x00\x00"
+  }
+  return $r
+}
+
+# Return the UTF-8 representation of the supplied UTF-16 string $str. 
+proc utf8 {str} {
+  # If $str ends in two 0x00 0x00 bytes, knock these off before
+  # converting to UTF-8 using TCL.
+  binary scan $str \c* vals
+  if {[lindex $vals end]==0 && [lindex $vals end-1]==0} {
+    set str [binary format \c* [lrange $vals 0 end-2]]
+  }
+
+  set r [encoding convertfrom unicode $str]
+  return $r
+}
+
+# These tests complement those in capi2.test. They are organized
+# as follows:
+#
+# capi3-1.*: Test sqlite3_prepare_v2 
+# capi3-2.*: Test sqlite3_prepare16_v2 
+# capi3-3.*: Test sqlite3_open
+# capi3-4.*: Test sqlite3_open16
+# capi3-5.*: Test the various sqlite3_result_* APIs
+# capi3-6.*: Test that sqlite3_close fails if there are outstanding VMs.
+#
+
+set DB [sqlite3_connection_pointer db]
+
+do_test capi3-1.0 {
+  sqlite3_get_autocommit $DB
+} 1
+do_test capi3-1.1 {
+  set STMT [sqlite3_prepare_v2 $DB {SELECT name FROM sqlite_master} -1 TAIL]
+  sqlite3_finalize $STMT
+  set TAIL
+} {}
+do_test capi3-1.2 {
+  sqlite3_errcode $DB
+} {SQLITE_OK}
+do_test capi3-1.3 {
+  sqlite3_errmsg $DB
+} {not an error}
+do_test capi3-1.4 {
+  set sql {SELECT name FROM sqlite_master;SELECT 10}
+  set STMT [sqlite3_prepare_v2 $DB $sql -1 TAIL]
+  sqlite3_finalize $STMT
+  set TAIL
+} {SELECT 10}
+do_test capi3-1.5 {
+  set sql {SELECT namex FROM sqlite_master}
+  catch {
+    set STMT [sqlite3_prepare_v2 $DB $sql -1 TAIL]
+  }
+} {1}
+do_test capi3-1.6 {
+  sqlite3_errcode $DB
+} {SQLITE_ERROR}
+do_test capi3-1.7 {
+  sqlite3_errmsg $DB
+} {no such column: namex}
+
+ifcapable {utf16} {
+  do_test capi3-2.1 {
+    set sql16 [utf16 {SELECT name FROM sqlite_master}]
+    set STMT [sqlite3_prepare16_v2  $DB $sql16 -1 ::TAIL]
+    sqlite3_finalize $STMT
+    utf8 $::TAIL
+  } {}
+  do_test capi3-2.2 {
+    set sql [utf16 {SELECT name FROM sqlite_master;SELECT 10}]
+    set STMT [sqlite3_prepare16_v2  $DB $sql -1 TAIL]
+    sqlite3_finalize $STMT
+    utf8 $TAIL
+  } {SELECT 10}
+  do_test capi3-2.3 {
+    set sql [utf16 {SELECT namex FROM sqlite_master}]
+    catch {
+      set STMT [sqlite3_prepare16_v2  $DB $sql -1 TAIL]
+    }
+  } {1}
+  do_test capi3-2.4 {
+    sqlite3_errcode $DB
+  } {SQLITE_ERROR}
+  do_test capi3-2.5 {
+    sqlite3_errmsg $DB
+  } {no such column: namex}
+
+  ifcapable schema_pragmas {
+    do_test capi3-2.6 {
+      execsql {CREATE TABLE tablename(x)}
+      set sql16 [utf16 {PRAGMA table_info("TableName")}]
+      set STMT [sqlite3_prepare16_v2  $DB $sql16 -1 TAIL]
+      sqlite3_step $STMT
+    } SQLITE_ROW
+    do_test capi3-2.7 {
+      sqlite3_step $STMT
+    } SQLITE_DONE
+    do_test capi3-2.8 {
+      sqlite3_finalize $STMT
+    } SQLITE_OK
+  }
+
+} ;# endif utf16
+
+# rename sqlite3_open sqlite3_open_old
+# proc sqlite3_open {fname options} {sqlite3_open_new $fname $options}
+
+do_test capi3-3.1 {
+  set db2 [sqlite3_open test.db {}]
+  sqlite3_errcode $db2
+} {SQLITE_OK}
+# FIX ME: Should test the db handle works.
+do_test capi3-3.2 {
+  sqlite3_close $db2
+} {SQLITE_OK}
+do_test capi3-3.3 {
+  catch {
+    set db2 [sqlite3_open /bogus/path/test.db {}]
+  }
+  sqlite3_errcode $db2
+} {SQLITE_CANTOPEN}
+do_test capi3-3.4 {
+  sqlite3_errmsg $db2
+} {unable to open database file}
+do_test capi3-3.5 {
+  sqlite3_close $db2
+} {SQLITE_OK}
+do_test capi3-3.6.1 {
+  sqlite3_close $db2
+} {SQLITE_MISUSE}
+do_test capi3-3.6.2 {
+  sqlite3_errmsg $db2
+} {library routine called out of sequence}
+ifcapable {utf16} {
+  do_test capi3-3.6.3 {
+    utf8 [sqlite3_errmsg16 $db2]
+  } {library routine called out of sequence}
+}
+
+# rename sqlite3_open ""
+# rename sqlite3_open_old sqlite3_open
+
+ifcapable {utf16} {
+do_test capi3-4.1 {
+  set db2 [sqlite3_open16 [utf16 test.db] {}]
+  sqlite3_errcode $db2
+} {SQLITE_OK}
+# FIX ME: Should test the db handle works.
+do_test capi3-4.2 {
+  sqlite3_close $db2
+} {SQLITE_OK}
+do_test capi3-4.3 {
+  catch {
+    set db2 [sqlite3_open16 [utf16 /bogus/path/test.db] {}]
+  }
+  sqlite3_errcode $db2
+} {SQLITE_CANTOPEN}
+do_test capi3-4.4 {
+  utf8 [sqlite3_errmsg16 $db2]
+} {unable to open database file}
+do_test capi3-4.5 {
+  sqlite3_close $db2
+} {SQLITE_OK}
+} ;# utf16
+
+# This proc is used to test the following API calls:
+#
+# sqlite3_column_count
+# sqlite3_column_name
+# sqlite3_column_name16
+# sqlite3_column_decltype
+# sqlite3_column_decltype16
+#
+# $STMT is a compiled SQL statement. $test is a prefix
+# to use for test names within this proc. $names is a list
+# of the column names that should be returned by $STMT.
+# $decltypes is a list of column declaration types for $STMT.
+#
+# Example:
+#
+# set STMT [sqlite3_prepare_v2 "SELECT 1, 2, 2;" -1 DUMMY]
+# check_header test1.1 {1 2 3} {"" "" ""}
+#
+proc check_header {STMT test names decltypes} {
+
+  # Use the return value of sqlite3_column_count() to build
+  # a list of column indexes. i.e. If sqlite3_column_count
+  # is 3, build the list {0 1 2}.
+  set ::idxlist [list]
+  set ::numcols [sqlite3_column_count $STMT]
+  for {set i 0} {$i < $::numcols} {incr i} {lappend ::idxlist $i}
+
+  # Column names in UTF-8
+  do_test $test.1 {
+    set cnamelist [list]
+    foreach i $idxlist {lappend cnamelist [sqlite3_column_name $STMT $i]} 
+    set cnamelist
+  } $names
+
+  # Column names in UTF-16
+  ifcapable {utf16} {
+    do_test $test.2 {
+      set cnamelist [list]
+      foreach i $idxlist {
+        lappend cnamelist [utf8 [sqlite3_column_name16 $STMT $i]]
+      }
+      set cnamelist
+    } $names
+  }
+
+  # Column names in UTF-8
+  do_test $test.3 {
+    set cnamelist [list]
+    foreach i $idxlist {lappend cnamelist [sqlite3_column_name $STMT $i]} 
+    set cnamelist
+  } $names
+
+  # Column names in UTF-16
+  ifcapable {utf16} {
+    do_test $test.4 {
+      set cnamelist [list]
+      foreach i $idxlist {
+        lappend cnamelist [utf8 [sqlite3_column_name16 $STMT $i]]
+      }
+      set cnamelist
+    } $names
+  }
+
+  # Column names in UTF-8
+  do_test $test.5 {
+    set cnamelist [list]
+    foreach i $idxlist {lappend cnamelist [sqlite3_column_decltype $STMT $i]} 
+    set cnamelist
+  } $decltypes
+
+  # Column declaration types in UTF-16
+  ifcapable {utf16} {
+    do_test $test.6 {
+      set cnamelist [list]
+      foreach i $idxlist {
+        lappend cnamelist [utf8 [sqlite3_column_decltype16 $STMT $i]]
+      }
+      set cnamelist
+    } $decltypes
+  }
+
+
+  # Test some out of range conditions:
+  ifcapable {utf16} {
+    do_test $test.7 {
+      list \
+        [sqlite3_column_name $STMT -1] \
+        [sqlite3_column_name16 $STMT -1] \
+        [sqlite3_column_decltype $STMT -1] \
+        [sqlite3_column_decltype16 $STMT -1] \
+        [sqlite3_column_name $STMT $numcols] \
+        [sqlite3_column_name16 $STMT $numcols] \
+        [sqlite3_column_decltype $STMT $numcols] \
+        [sqlite3_column_decltype16 $STMT $numcols]
+    } {{} {} {} {} {} {} {} {}}
+  }
+} 
+
+# This proc is used to test the following API calls:
+#
+# sqlite3_column_origin_name
+# sqlite3_column_origin_name16
+# sqlite3_column_table_name
+# sqlite3_column_table_name16
+# sqlite3_column_database_name
+# sqlite3_column_database_name16
+#
+# $STMT is a compiled SQL statement. $test is a prefix
+# to use for test names within this proc. $names is a list
+# of the column names that should be returned by $STMT.
+# $decltypes is a list of column declaration types for $STMT.
+#
+# Example:
+#
+# set STMT [sqlite3_prepare_v2 "SELECT 1, 2, 2;" -1 DUMMY]
+# check_header test1.1 {1 2 3} {"" "" ""}
+#
+proc check_origin_header {STMT test dbs tables cols} {
+  # If sqlite3_column_origin_name() and friends are not compiled into
+  # this build, this proc is a no-op.
+ifcapable columnmetadata {
+
+    # Use the return value of sqlite3_column_count() to build
+    # a list of column indexes. i.e. If sqlite3_column_count
+    # is 3, build the list {0 1 2}.
+    set ::idxlist [list]
+    set ::numcols [sqlite3_column_count $STMT]
+    for {set i 0} {$i < $::numcols} {incr i} {lappend ::idxlist $i}
+  
+    # Database names in UTF-8
+    do_test $test.8 {
+      set cnamelist [list]
+      foreach i $idxlist {
+        lappend cnamelist [sqlite3_column_database_name $STMT $i]
+      } 
+      set cnamelist
+    } $dbs
+  
+    # Database names in UTF-16
+    ifcapable {utf16} {
+      do_test $test.9 {
+        set cnamelist [list]
+        foreach i $idxlist {
+          lappend cnamelist [utf8 [sqlite3_column_database_name16 $STMT $i]]
+        }
+        set cnamelist
+      } $dbs
+    }
+  
+    # Table names in UTF-8
+    do_test $test.10 {
+      set cnamelist [list]
+      foreach i $idxlist {
+        lappend cnamelist [sqlite3_column_table_name $STMT $i]
+      } 
+      set cnamelist
+    } $tables
+  
+    # Table names in UTF-16
+    ifcapable {utf16} {
+      do_test $test.11 {
+        set cnamelist [list]
+        foreach i $idxlist {
+          lappend cnamelist [utf8 [sqlite3_column_table_name16 $STMT $i]]
+        }
+        set cnamelist
+      } $tables
+    }
+  
+    # Origin names in UTF-8
+    do_test $test.12 {
+      set cnamelist [list]
+      foreach i $idxlist {
+        lappend cnamelist [sqlite3_column_origin_name $STMT $i]
+      } 
+      set cnamelist
+    } $cols
+  
+    # Origin declaration types in UTF-16
+    ifcapable {utf16} {
+      do_test $test.13 {
+        set cnamelist [list]
+        foreach i $idxlist {
+          lappend cnamelist [utf8 [sqlite3_column_origin_name16 $STMT $i]]
+        }
+        set cnamelist
+      } $cols
+    }
+  }
+}
+
+# This proc is used to test the following APIs:
+#
+# sqlite3_data_count
+# sqlite3_column_type
+# sqlite3_column_int
+# sqlite3_column_text
+# sqlite3_column_text16
+# sqlite3_column_double
+#
+# $STMT is a compiled SQL statement for which the previous call 
+# to sqlite3_step returned SQLITE_ROW. $test is a prefix to use 
+# for test names within this proc. $types is a list of the 
+# manifest types for the current row. $ints, $doubles and $strings
+# are lists of the integer, real and string representations of
+# the values in the current row.
+#
+# Example:
+#
+# set STMT [sqlite3_prepare_v2 "SELECT 'hello', 1.1, NULL" -1 DUMMY]
+# sqlite3_step $STMT
+# check_data test1.2 {TEXT REAL NULL} {0 1 0} {0 1.1 0} {hello 1.1 {}}
+#
+proc check_data {STMT test types ints doubles strings} {
+
+  # Use the return value of sqlite3_column_count() to build
+  # a list of column indexes. i.e. If sqlite3_column_count
+  # is 3, build the list {0 1 2}.
+  set ::idxlist [list]
+  set numcols [sqlite3_data_count $STMT]
+  for {set i 0} {$i < $numcols} {incr i} {lappend ::idxlist $i}
+
+# types
+do_test $test.1 {
+  set types [list]
+  foreach i $idxlist {lappend types [sqlite3_column_type $STMT $i]}
+  set types
+} $types
+
+# Integers
+do_test $test.2 {
+  set ints [list]
+  foreach i $idxlist {lappend ints [sqlite3_column_int64 $STMT $i]}
+  set ints
+} $ints
+
+# bytes
+set lens [list]
+foreach i $::idxlist {
+  lappend lens [string length [lindex $strings $i]]
+}
+do_test $test.3 {
+  set bytes [list]
+  set lens [list]
+  foreach i $idxlist {
+    lappend bytes [sqlite3_column_bytes $STMT $i]
+  }
+  set bytes
+} $lens
+
+# bytes16
+ifcapable {utf16} {
+  set lens [list]
+  foreach i $::idxlist {
+    lappend lens [expr 2 * [string length [lindex $strings $i]]]
+  }
+  do_test $test.4 {
+    set bytes [list]
+    set lens [list]
+    foreach i $idxlist {
+      lappend bytes [sqlite3_column_bytes16 $STMT $i]
+    }
+    set bytes
+  } $lens
+}
+
+# Blob
+do_test $test.5 {
+  set utf8 [list]
+  foreach i $idxlist {lappend utf8 [sqlite3_column_blob $STMT $i]}
+  set utf8
+} $strings
+
+# UTF-8
+do_test $test.6 {
+  set utf8 [list]
+  foreach i $idxlist {lappend utf8 [sqlite3_column_text $STMT $i]}
+  set utf8
+} $strings
+
+# Floats
+do_test $test.7 {
+  set utf8 [list]
+  foreach i $idxlist {lappend utf8 [sqlite3_column_double $STMT $i]}
+  set utf8
+} $doubles
+
+# UTF-16
+ifcapable {utf16} {
+  do_test $test.8 {
+    set utf8 [list]
+    foreach i $idxlist {lappend utf8 [utf8 [sqlite3_column_text16 $STMT $i]]}
+    set utf8
+  } $strings
+}
+
+# Integers
+do_test $test.9 {
+  set ints [list]
+  foreach i $idxlist {lappend ints [sqlite3_column_int $STMT $i]}
+  set ints
+} $ints
+
+# Floats
+do_test $test.10 {
+  set utf8 [list]
+  foreach i $idxlist {lappend utf8 [sqlite3_column_double $STMT $i]}
+  set utf8
+} $doubles
+
+# UTF-8
+do_test $test.11 {
+  set utf8 [list]
+  foreach i $idxlist {lappend utf8 [sqlite3_column_text $STMT $i]}
+  set utf8
+} $strings
+
+# Types
+do_test $test.12 {
+  set types [list]
+  foreach i $idxlist {lappend types [sqlite3_column_type $STMT $i]}
+  set types
+} $types
+
+# Test that an out of range request returns the equivalent of NULL
+do_test $test.13 {
+  sqlite3_column_int $STMT -1
+} {0}
+do_test $test.13 {
+  sqlite3_column_text $STMT -1
+} {}
+
+}
+
+ifcapable !floatingpoint {
+  finish_test
+  return
+}
+
+do_test capi3-5.0 {
+  execsql {
+    CREATE TABLE t1(a VARINT, b BLOB, c VARCHAR(16));
+    INSERT INTO t1 VALUES(1, 2, 3);
+    INSERT INTO t1 VALUES('one', 'two', NULL);
+    INSERT INTO t1 VALUES(1.2, 1.3, 1.4);
+  }
+  set sql "SELECT * FROM t1"
+  set STMT [sqlite3_prepare_v2 $DB $sql -1 TAIL]
+  sqlite3_column_count $STMT
+} 3
+
+check_header $STMT capi3-5.1 {a b c} {VARINT BLOB VARCHAR(16)}
+check_origin_header $STMT capi3-5.1 {main main main} {t1 t1 t1} {a b c}
+do_test capi3-5.2 {
+  sqlite3_step $STMT
+} SQLITE_ROW
+
+check_header $STMT capi3-5.3 {a b c} {VARINT BLOB VARCHAR(16)}
+check_origin_header $STMT capi3-5.3 {main main main} {t1 t1 t1} {a b c}
+check_data $STMT capi3-5.4 {INTEGER INTEGER TEXT} {1 2 3} {1.0 2.0 3.0} {1 2 3}
+
+do_test capi3-5.5 {
+  sqlite3_step $STMT
+} SQLITE_ROW
+
+check_header $STMT capi3-5.6 {a b c} {VARINT BLOB VARCHAR(16)}
+check_origin_header $STMT capi3-5.6 {main main main} {t1 t1 t1} {a b c}
+check_data $STMT capi3-5.7 {TEXT TEXT NULL} {0 0 0} {0.0 0.0 0.0} {one two {}}
+
+do_test capi3-5.8 {
+  sqlite3_step $STMT
+} SQLITE_ROW
+
+check_header $STMT capi3-5.9 {a b c} {VARINT BLOB VARCHAR(16)}
+check_origin_header $STMT capi3-5.9 {main main main} {t1 t1 t1} {a b c}
+check_data $STMT capi3-5.10 {FLOAT FLOAT TEXT} {1 1 1} {1.2 1.3 1.4} {1.2 1.3 1.4}
+
+do_test capi3-5.11 {
+  sqlite3_step $STMT
+} SQLITE_DONE
+
+do_test capi3-5.12 {
+  sqlite3_finalize $STMT
+} SQLITE_OK
+
+do_test capi3-5.20 {
+  set sql "SELECT a, sum(b), max(c) FROM t1 GROUP BY a"
+  set STMT [sqlite3_prepare_v2 $DB $sql -1 TAIL]
+  sqlite3_column_count $STMT
+} 3
+
+check_header $STMT capi3-5.21 {a sum(b) max(c)} {VARINT {} {}}
+check_origin_header $STMT capi3-5.22 {main {} {}} {t1 {} {}} {a {} {}}
+do_test capi3-5.23 {
+  sqlite3_finalize $STMT
+} SQLITE_OK
+
+
+set ::ENC [execsql {pragma encoding}]
+db close
+
+do_test capi3-6.0 {
+btree_breakpoint
+  sqlite3 db test.db
+  set DB [sqlite3_connection_pointer db]
+btree_breakpoint
+  sqlite3_key $DB xyzzy
+  set sql {SELECT a FROM t1 order by rowid}
+  set STMT [sqlite3_prepare_v2 $DB $sql -1 TAIL]
+  expr 0
+} {0}
+do_test capi3-6.1 {
+  db cache flush
+  sqlite3_close $DB
+} {SQLITE_BUSY}
+do_test capi3-6.2 {
+  sqlite3_step $STMT
+} {SQLITE_ROW}
+check_data $STMT capi3-6.3 {INTEGER} {1} {1.0} {1}
+do_test capi3-6.3 {
+  sqlite3_finalize $STMT
+} {SQLITE_OK}
+do_test capi3-6.4 {
+  db cache flush
+  sqlite3_close $DB
+} {SQLITE_OK}
+db close
+
+if {![sqlite3 -has-codec]} {
+  # Test what happens when the library encounters a newer file format.
+  # Do this by updating the file format via the btree layer.
+  do_test capi3-7.1 {
+    set ::bt [btree_open test.db 10 0]
+    btree_begin_transaction $::bt
+    set meta [btree_get_meta $::bt]
+    lset meta 2 5
+    eval [concat btree_update_meta $::bt [lrange $meta 0 end]]
+    btree_commit $::bt
+    btree_close $::bt
+  } {}
+  do_test capi3-7.2 {
+    sqlite3 db test.db
+    catchsql {
+      SELECT * FROM sqlite_master;
+    }
+  } {1 {unsupported file format}}
+  db close
+}
+
+if {![sqlite3 -has-codec]} {
+  # Now test that the library correctly handles bogus entries in the
+  # sqlite_master table (schema corruption).
+  do_test capi3-8.1 {
+    file delete -force test.db
+    file delete -force test.db-journal
+    sqlite3 db test.db
+    execsql {
+      CREATE TABLE t1(a);
+    }
+    db close
+  } {}
+  do_test capi3-8.2 {
+    set ::bt [btree_open test.db 10 0]
+    btree_begin_transaction $::bt
+    set ::bc [btree_cursor $::bt 1 1]
+
+    # Build a 5-field row record consisting of 5 null records. This is
+    # officially black magic.
+    catch {unset data}
+    set data [binary format c6 {6 0 0 0 0 0}]
+    btree_insert $::bc 5 $data
+
+    btree_close_cursor $::bc
+    btree_commit $::bt
+    btree_close $::bt
+  } {}
+  do_test capi3-8.3 {
+    sqlite3 db test.db
+    catchsql {
+      SELECT * FROM sqlite_master;
+    }
+  } {1 {malformed database schema}}
+  do_test capi3-8.4 {
+    set ::bt [btree_open test.db 10 0]
+    btree_begin_transaction $::bt
+    set ::bc [btree_cursor $::bt 1 1]
+  
+    # Build a 5-field row record. The first field is a string 'table', and
+    # subsequent fields are all NULL. Replace the other broken record with
+    # this one and try to read the schema again. The broken record uses
+    # either UTF-8 or native UTF-16 (if this file is being run by
+    # utf16.test).
+    if { [string match UTF-16* $::ENC] } {
+      set data [binary format c6a10 {6 33 0 0 0 0} [utf16 table]]
+    } else {
+      set data [binary format c6a5 {6 23 0 0 0 0} table]
+    }
+    btree_insert $::bc 5 $data
+  
+    btree_close_cursor $::bc
+    btree_commit $::bt
+    btree_close $::bt
+  } {};
+  do_test capi3-8.5 {
+    db close 
+    sqlite3 db test.db
+    catchsql {
+      SELECT * FROM sqlite_master;
+    }
+  } {1 {malformed database schema}}
+  db close
+}
+file delete -force test.db
+file delete -force test.db-journal
+
+
+# Test the english language string equivalents for sqlite error codes
+set code2english [list \
+SQLITE_OK         {not an error} \
+SQLITE_ERROR      {SQL logic error or missing database} \
+SQLITE_PERM       {access permission denied} \
+SQLITE_ABORT      {callback requested query abort} \
+SQLITE_BUSY       {database is locked} \
+SQLITE_LOCKED     {database table is locked} \
+SQLITE_NOMEM      {out of memory} \
+SQLITE_READONLY   {attempt to write a readonly database} \
+SQLITE_INTERRUPT  {interrupted} \
+SQLITE_IOERR      {disk I/O error} \
+SQLITE_CORRUPT    {database disk image is malformed} \
+SQLITE_FULL       {database or disk is full} \
+SQLITE_CANTOPEN   {unable to open database file} \
+SQLITE_PROTOCOL   {database locking protocol failure} \
+SQLITE_EMPTY      {table contains no data} \
+SQLITE_SCHEMA     {database schema has changed} \
+SQLITE_CONSTRAINT {constraint failed} \
+SQLITE_MISMATCH   {datatype mismatch} \
+SQLITE_MISUSE     {library routine called out of sequence} \
+SQLITE_NOLFS      {kernel lacks large file support} \
+SQLITE_AUTH       {authorization denied} \
+SQLITE_FORMAT     {auxiliary database format error} \
+SQLITE_RANGE      {bind or column index out of range} \
+SQLITE_NOTADB     {file is encrypted or is not a database} \
+unknownerror      {unknown error} \
+]
+
+set test_number 1
+foreach {code english} $code2english {
+  do_test capi3-9.$test_number "sqlite3_test_errstr $code" $english
+  incr test_number
+}
+
+# Test the error message when a "real" out of memory occurs.
+if {[info command sqlite_malloc_stat]!=""} {
+set sqlite_malloc_fail 1
+do_test capi3-10-1 {
+  sqlite3 db test.db
+  set DB [sqlite3_connection_pointer db]
+  sqlite_malloc_fail 1
+  catchsql {
+    select * from sqlite_master;
+  }
+} {1 {out of memory}}
+do_test capi3-10-2 {
+  sqlite3_errmsg $::DB
+} {out of memory}
+ifcapable {utf16} {
+  do_test capi3-10-3 {
+    utf8 [sqlite3_errmsg16 $::DB]
+  } {out of memory}
+}
+db close
+sqlite_malloc_fail 0
+}
+
+# The following tests - capi3-11.* - test that a COMMIT or ROLLBACK
+# statement issued while there are still outstanding VMs that are part of
+# the transaction fails.
+sqlite3 db test.db
+set DB [sqlite3_connection_pointer db]
+sqlite_register_test_function $DB func
+do_test capi3-11.1 {
+  execsql {
+    BEGIN;
+    CREATE TABLE t1(a, b);
+    INSERT INTO t1 VALUES(1, 'int');
+    INSERT INTO t1 VALUES(2, 'notatype');
+  }
+} {}
+do_test capi3-11.1.1 {
+  sqlite3_get_autocommit $DB
+} 0
+do_test capi3-11.2 {
+  set STMT [sqlite3_prepare_v2 $DB "SELECT func(b, a) FROM t1" -1 TAIL]
+  sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test capi3-11.3 {
+  catchsql {
+    COMMIT;
+  }
+} {1 {cannot commit transaction - SQL statements in progress}}
+do_test capi3-11.3.1 {
+  sqlite3_get_autocommit $DB
+} 0
+do_test capi3-11.4 {
+  sqlite3_step $STMT
+} {SQLITE_ERROR}
+do_test capi3-11.5 {
+  sqlite3_finalize $STMT
+} {SQLITE_ERROR}
+do_test capi3-11.6 {
+  catchsql {
+    SELECT * FROM t1;
+  }
+} {0 {1 int 2 notatype}}
+do_test capi3-11.6.1 {
+  sqlite3_get_autocommit $DB
+} 0
+do_test capi3-11.7 {
+  catchsql {
+    COMMIT;
+  }
+} {0 {}}
+do_test capi3-11.7.1 {
+  sqlite3_get_autocommit $DB
+} 1
+do_test capi3-11.8 {
+  execsql {
+    CREATE TABLE t2(a);
+    INSERT INTO t2 VALUES(1);
+    INSERT INTO t2 VALUES(2);
+    BEGIN;
+    INSERT INTO t2 VALUES(3);
+  }
+} {}
+do_test capi3-11.8.1 {
+  sqlite3_get_autocommit $DB
+} 0
+do_test capi3-11.9 {
+  set STMT [sqlite3_prepare_v2 $DB "SELECT a FROM t2" -1 TAIL]
+  sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test capi3-11.9.1 {
+  sqlite3_get_autocommit $DB
+} 0
+do_test capi3-11.9.2 {
+  catchsql {
+    ROLLBACK;
+  }
+} {1 {cannot rollback transaction - SQL statements in progress}}
+do_test capi3-11.9.3 {
+  sqlite3_get_autocommit $DB
+} 0
+do_test capi3-11.10 {
+  sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test capi3-11.11 {
+  sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test capi3-11.12 {
+  sqlite3_step $STMT
+} {SQLITE_DONE}
+do_test capi3-11.13 {
+  sqlite3_finalize $STMT
+} {SQLITE_OK}
+do_test capi3-11.14 {
+  execsql {
+    SELECT a FROM t2;
+  }
+} {1 2 3}
+do_test capi3-11.14.1 {
+  sqlite3_get_autocommit $DB
+} 0
+do_test capi3-11.15 {
+  catchsql {
+    ROLLBACK;
+  }
+} {0 {}}
+do_test capi3-11.15.1 {
+  sqlite3_get_autocommit $DB
+} 1
+do_test capi3-11.16 {
+  execsql {
+    SELECT a FROM t2;
+  }
+} {1 2}
+
+# Sanity check on the definition of 'outstanding VM'. This means any VM
+# that has had sqlite3_step() called more recently than sqlite3_finalize() or
+# sqlite3_reset(). So a VM that has just been prepared or reset does not
+# count as an active VM.
+do_test capi3-11.17 {
+  execsql {
+    BEGIN;
+  }
+} {}
+do_test capi3-11.18 {
+  set STMT [sqlite3_prepare_v2 $DB "SELECT a FROM t1" -1 TAIL]
+  catchsql {
+    COMMIT;
+  }
+} {0 {}}
+do_test capi3-11.19 {
+  sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test capi3-11.20 {
+  catchsql {
+    BEGIN;
+    COMMIT;
+  }
+} {1 {cannot commit transaction - SQL statements in progress}}
+do_test capi3-11.20 {
+  sqlite3_reset $STMT
+  catchsql {
+    COMMIT;
+  }
+} {0 {}}
+do_test capi3-11.21 {
+  sqlite3_finalize $STMT
+} {SQLITE_OK}
+
+# The following tests - capi3-12.* - check that it's Ok to start a
+# transaction while other VMs are active, and that it's Ok to execute
+# atomic updates in the same situation 
+#
+do_test capi3-12.1 {
+  set STMT [sqlite3_prepare_v2 $DB "SELECT a FROM t2" -1 TAIL]
+  sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test capi3-12.2 {
+  catchsql {
+    INSERT INTO t1 VALUES(3, NULL);
+  }
+} {0 {}}
+do_test capi3-12.3 {
+  catchsql {
+    INSERT INTO t2 VALUES(4);
+  }
+} {0 {}}
+do_test capi3-12.4 {
+  catchsql {
+    BEGIN;
+    INSERT INTO t1 VALUES(4, NULL);
+  }
+} {0 {}}
+do_test capi3-12.5 {
+  sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test capi3-12.5.1 {
+  sqlite3_step $STMT
+} {SQLITE_ROW}
+do_test capi3-12.6 {
+  sqlite3_step $STMT
+} {SQLITE_DONE}
+do_test capi3-12.7 {
+  sqlite3_finalize $STMT
+} {SQLITE_OK}
+do_test capi3-12.8 {
+  execsql {
+    COMMIT;
+    SELECT a FROM t1;
+  }
+} {1 2 3 4}
+
+# Test cases capi3-13.* test the sqlite3_clear_bindings() and 
+# sqlite3_sleep APIs.
+#
+if {[llength [info commands sqlite3_clear_bindings]]>0} {
+  do_test capi3-13.1 {
+    execsql {
+      DELETE FROM t1;
+    }
+    set STMT [sqlite3_prepare_v2 $DB "INSERT INTO t1 VALUES(?, ?)" -1 TAIL]
+    sqlite3_step $STMT
+  } {SQLITE_DONE}
+  do_test capi3-13.2 {
+    sqlite3_reset $STMT
+    sqlite3_bind_text $STMT 1 hello 5
+    sqlite3_bind_text $STMT 2 world 5
+    sqlite3_step $STMT
+  } {SQLITE_DONE}
+  do_test capi3-13.3 {
+    sqlite3_reset $STMT
+    sqlite3_clear_bindings $STMT
+    sqlite3_step $STMT
+  } {SQLITE_DONE}
+  do_test capi3-13-4 {
+    sqlite3_finalize $STMT
+    execsql {
+      SELECT * FROM t1;
+    }
+  } {{} {} hello world {} {}}
+}
+if {[llength [info commands sqlite3_sleep]]>0} {
+  do_test capi3-13-5 {
+    set ms [sqlite3_sleep 80]
+    expr {$ms==80 || $ms==1000}
+  } {1}
+}
+
+# Ticket #1219:  Make sure binding APIs can handle a NULL pointer.
+#
+do_test capi3-14.1 {
+  set rc [catch {sqlite3_bind_text 0 1 hello 5} msg]
+  lappend rc $msg
+} {1 SQLITE_MISUSE}
+
+# Ticket #1650:  Honor the nBytes parameter to sqlite3_prepare.
+#
+do_test capi3-15.1 {
+  set sql {SELECT * FROM t2}
+  set nbytes [string length $sql]
+  append sql { WHERE a==1}
+  set STMT [sqlite3_prepare_v2 $DB $sql $nbytes TAIL]
+  sqlite3_step $STMT
+  sqlite3_column_int $STMT 0
+} {1}
+do_test capi3-15.2 {
+  sqlite3_step $STMT
+  sqlite3_column_int $STMT 0
+} {2}
+do_test capi3-15.3 {
+  sqlite3_finalize $STMT
+} {SQLITE_OK}
+
+# Make sure code is always generated even if an IF EXISTS or 
+# IF NOT EXISTS clause is present that the table does not or
+# does exists.  That way we will always have a prepared statement
+# to expire when the schema changes.
+#
+do_test capi3-16.1 {
+  set sql {DROP TABLE IF EXISTS t3}
+  set STMT [sqlite3_prepare_v2 $DB $sql -1 TAIL]
+  sqlite3_finalize $STMT
+  expr {$STMT!=""}
+} {1}
+do_test capi3-16.2 {
+  set sql {CREATE TABLE IF NOT EXISTS t1(x,y)}
+  set STMT [sqlite3_prepare_v2 $DB $sql -1 TAIL]
+  sqlite3_finalize $STMT
+  expr {$STMT!=""}
+} {1}
+
+# But still we do not generate code if there is no SQL
+#
+do_test capi3-16.3 {
+  set STMT [sqlite3_prepare_v2 $DB {} -1 TAIL]
+  sqlite3_finalize $STMT
+  expr {$STMT==""}
+} {1}
+do_test capi3-16.4 {
+  set STMT [sqlite3_prepare_v2 $DB {;} -1 TAIL]
+  sqlite3_finalize $STMT
+  expr {$STMT==""}
+} {1}
+
+
+
+finish_test
index 3f58e0a955d10c9351143a8a9b534fdd16218357..d592fb9c0c5ba6fe73c11e12e74fded50b74e85e 100644 (file)
@@ -6,7 +6,7 @@
 #***********************************************************************
 # This file runs all tests.
 #
-# $Id: quick.test,v 1.45 2006/06/23 08:05:38 danielk1977 Exp $
+# $Id: quick.test,v 1.46 2006/11/09 00:24:55 drh Exp $
 
 proc lshift {lvar} {
   upvar $lvar l
@@ -63,9 +63,17 @@ if {[sqlite3 -has-codec]} {
   #  conflict.test
 }
 
+
+# Files to include in the test.  If this list is empty then everything
+# that is not in the EXCLUDE list is run.
+#
+set INCLUDE {
+}
+
 foreach testfile [lsort -dictionary [glob $testdir/*.test]] {
   set tail [file tail $testfile]
   if {[lsearch -exact $EXCLUDE $tail]>=0} continue
+  if {[llength $INCLUDE]>0 && [lsearch -exact $INCLUDE $tail]<0} continue
   source $testfile
   catch {db close}
   if {$sqlite_open_file_count>0} {
diff --git a/test/schema2.test b/test/schema2.test
new file mode 100644 (file)
index 0000000..1bce2cd
--- /dev/null
@@ -0,0 +1,334 @@
+# 2006 November 08
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library.
+#
+# This file tests the various conditions under which an SQLITE_SCHEMA
+# error should be returned.  This is a copy of schema.test that
+# has been altered to use sqlite3_prepare_v2 instead of sqlite3_prepare
+#
+# $Id: schema2.test,v 1.1 2006/11/09 00:24:55 drh Exp $
+
+#---------------------------------------------------------------------
+# When any of the following types of SQL statements or actions are 
+# executed, all pre-compiled statements are invalidated. An attempt
+# to execute an invalidated statement always returns SQLITE_SCHEMA.
+#
+# CREATE/DROP TABLE...................................schema2-1.*
+# CREATE/DROP VIEW....................................schema2-2.*
+# CREATE/DROP TRIGGER.................................schema2-3.*
+# CREATE/DROP INDEX...................................schema2-4.*
+# DETACH..............................................schema2-5.*
+# Deleting a user-function............................schema2-6.*
+# Deleting a collation sequence.......................schema2-7.*
+# Setting or changing the authorization function......schema2-8.*
+#
+# Test cases schema2-9.* and schema2-10.* test some specific bugs
+# that came up during development.
+#
+# Test cases schema2-11.* test that it is impossible to delete or
+# change a collation sequence or user-function while SQL statements
+# are executing. Adding new collations or functions is allowed.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+do_test schema2-1.1 {
+  set ::STMT [sqlite3_prepare_v2 $::DB {SELECT * FROM sqlite_master} -1 TAIL]
+  execsql {
+    CREATE TABLE abc(a, b, c);
+  }
+  sqlite3_step $::STMT
+} {SQLITE_ROW}
+do_test schema2-1.2 {
+  sqlite3_finalize $::STMT
+} {SQLITE_OK}
+do_test schema2-1.3 {
+  set ::STMT [sqlite3_prepare_v2 $::DB {SELECT * FROM sqlite_master} -1 TAIL]
+  execsql {
+    DROP TABLE abc;
+  }
+  sqlite3_step $::STMT
+} {SQLITE_DONE}
+do_test schema2-1.4 {
+  sqlite3_finalize $::STMT
+} {SQLITE_OK}
+
+
+ifcapable view {
+  do_test schema2-2.1 {
+    set ::STMT [sqlite3_prepare_v2 $::DB {SELECT * FROM sqlite_master} -1 TAIL]
+    execsql {
+      CREATE VIEW v1 AS SELECT * FROM sqlite_master;
+    }
+    sqlite3_step $::STMT
+  } {SQLITE_ROW}
+  do_test schema2-2.2 {
+    sqlite3_finalize $::STMT
+  } {SQLITE_OK}
+  do_test schema2-2.3 {
+    set ::STMT [sqlite3_prepare_v2 $::DB {SELECT * FROM sqlite_master} -1 TAIL]
+    execsql {
+      DROP VIEW v1;
+    }
+    sqlite3_step $::STMT
+  } {SQLITE_DONE}
+  do_test schema2-2.4 {
+    sqlite3_finalize $::STMT
+  } {SQLITE_OK}
+}
+
+ifcapable trigger {
+  do_test schema2-3.1 {
+    execsql {
+      CREATE TABLE abc(a, b, c);
+    }
+    set ::STMT [sqlite3_prepare_v2 $::DB {SELECT * FROM sqlite_master} -1 TAIL]
+    execsql {
+      CREATE TRIGGER abc_trig AFTER INSERT ON abc BEGIN
+        SELECT 1, 2, 3;
+      END;
+    }
+    sqlite3_step $::STMT
+  } {SQLITE_ROW}
+  do_test schema2-3.2 {
+    sqlite3_finalize $::STMT
+  } {SQLITE_OK}
+  do_test schema2-3.3 {
+    set ::STMT [sqlite3_prepare_v2 $::DB {SELECT * FROM sqlite_master} -1 TAIL]
+    execsql {
+      DROP TRIGGER abc_trig;
+    }
+    sqlite3_step $::STMT
+  } {SQLITE_ROW}
+  do_test schema2-3.4 {
+    sqlite3_finalize $::STMT
+  } {SQLITE_OK}
+}
+
+do_test schema2-4.1 {
+  catchsql {
+    CREATE TABLE abc(a, b, c);
+  }
+  set ::STMT [sqlite3_prepare_v2 $::DB {SELECT * FROM sqlite_master} -1 TAIL]
+  execsql {
+    CREATE INDEX abc_index ON abc(a);
+  }
+  sqlite3_step $::STMT
+} {SQLITE_ROW}
+do_test schema2-4.2 {
+  sqlite3_finalize $::STMT
+} {SQLITE_OK}
+do_test schema2-4.3 {
+  set ::STMT [sqlite3_prepare_v2 $::DB {SELECT * FROM sqlite_master} -1 TAIL]
+  execsql {
+    DROP INDEX abc_index;
+  }
+  sqlite3_step $::STMT
+} {SQLITE_ROW}
+do_test schema2-4.4 {
+  sqlite3_finalize $::STMT
+} {SQLITE_OK}
+
+#---------------------------------------------------------------------
+# Tests 5.1 to 5.4 check that prepared statements are invalidated when
+# a database is DETACHed (but not when one is ATTACHed).
+#
+do_test schema2-5.1 {
+  set sql {SELECT * FROM abc;}
+  set ::STMT [sqlite3_prepare_v2 $::DB $sql -1 TAIL]
+  execsql {
+    ATTACH 'test2.db' AS aux;
+  }
+  sqlite3_step $::STMT
+} {SQLITE_DONE}
+do_test schema2-5.2 {
+  sqlite3_reset $::STMT
+} {SQLITE_OK}
+do_test schema2-5.3 {
+  execsql {
+    DETACH aux;
+  }
+  sqlite3_step $::STMT
+} {SQLITE_DONE}
+do_test schema2-5.4 {
+  sqlite3_finalize $::STMT
+} {SQLITE_OK}
+
+#---------------------------------------------------------------------
+# Tests 6.* check that prepared statements are invalidated when
+# a user-function is deleted (but not when one is added).
+do_test schema2-6.1 {
+  set sql {SELECT * FROM abc;}
+  set ::STMT [sqlite3_prepare_v2 $::DB $sql -1 TAIL]
+  db function hello_function {}
+  sqlite3_step $::STMT
+} {SQLITE_DONE}
+do_test schema2-6.2 {
+  sqlite3_reset $::STMT
+} {SQLITE_OK}
+do_test schema2-6.3 {
+  sqlite_delete_function $::DB hello_function
+  sqlite3_step $::STMT
+} {SQLITE_DONE}
+do_test schema2-6.4 {
+  sqlite3_finalize $::STMT
+} {SQLITE_OK}
+
+#---------------------------------------------------------------------
+# Tests 7.* check that prepared statements are invalidated when
+# a collation sequence is deleted (but not when one is added).
+#
+ifcapable utf16 {
+  do_test schema2-7.1 {
+    set sql {SELECT * FROM abc;}
+    set ::STMT [sqlite3_prepare_v2 $::DB $sql -1 TAIL]
+    add_test_collate $::DB 1 1 1
+    sqlite3_step $::STMT
+  } {SQLITE_DONE}
+  do_test schema2-7.2 {
+    sqlite3_reset $::STMT
+  } {SQLITE_OK}
+  do_test schema2-7.3 {
+    add_test_collate $::DB 0 0 0 
+    sqlite3_step $::STMT
+  } {SQLITE_DONE}
+  do_test schema2-7.4 {
+    sqlite3_finalize $::STMT
+  } {SQLITE_OK}
+}
+
+#---------------------------------------------------------------------
+# Tests 8.1 and 8.2 check that prepared statements are invalidated when
+# the authorization function is set.
+#
+ifcapable auth {
+  do_test schema2-8.1 {
+    set ::STMT [sqlite3_prepare_v2 $::DB {SELECT * FROM sqlite_master} -1 TAIL]
+    db auth {}
+    sqlite3_step $::STMT
+  } {SQLITE_ROW}
+  do_test schema2-8.3 {
+    sqlite3_finalize $::STMT
+  } {SQLITE_OK}
+}
+
+#---------------------------------------------------------------------
+# schema2-9.1: Test that if a table is dropped by one database connection, 
+#             other database connections are aware of the schema change.
+# schema2-9.2: Test that if a view is dropped by one database connection,
+#             other database connections are aware of the schema change.
+#
+do_test schema2-9.1 {
+  sqlite3 db2 test.db
+  execsql {
+    DROP TABLE abc;
+  } db2
+  db2 close
+  catchsql {
+    SELECT * FROM abc;
+  }
+} {1 {no such table: abc}}
+execsql {
+  CREATE TABLE abc(a, b, c);
+}
+ifcapable view {
+  do_test schema2-9.2 {
+    execsql {
+      CREATE VIEW abcview AS SELECT * FROM abc;
+    }
+    sqlite3 db2 test.db
+    execsql {
+      DROP VIEW abcview;
+    } db2
+    db2 close
+    catchsql {
+      SELECT * FROM abcview;
+    }
+  } {1 {no such table: abcview}}
+}
+
+#---------------------------------------------------------------------
+# Test that if a CREATE TABLE statement fails because there are other
+# btree cursors open on the same database file it does not corrupt
+# the sqlite_master table.
+#
+do_test schema2-10.1 {
+  execsql {
+    INSERT INTO abc VALUES(1, 2, 3);
+  }
+  set sql {SELECT * FROM abc}
+  set ::STMT [sqlite3_prepare_v2 $::DB $sql -1 TAIL]
+  sqlite3_step $::STMT
+} {SQLITE_ROW}
+do_test schema2-10.2 {
+  catchsql {
+    CREATE TABLE t2(a, b, c);
+  }
+} {1 {database table is locked}}
+do_test schema2-10.3 {
+  sqlite3_finalize $::STMT
+} {SQLITE_OK}
+do_test schema2-10.4 {
+  sqlite3 db2 test.db
+  execsql {
+    SELECT * FROM abc
+  } db2
+} {1 2 3}
+do_test schema2-10.5 {
+  db2 close
+} {}
+
+#---------------------------------------------------------------------
+# Attempting to delete or replace a user-function or collation sequence 
+# while there are active statements returns an SQLITE_BUSY error.
+#
+# schema2-11.1 - 11.4: User function.
+# schema2-11.5 - 11.8: Collation sequence.
+#
+do_test schema2-11.1 {
+  db function tstfunc {}
+  set sql {SELECT * FROM abc}
+  set ::STMT [sqlite3_prepare_v2 $::DB $sql -1 TAIL]
+  sqlite3_step $::STMT
+} {SQLITE_ROW}
+do_test schema2-11.2 {
+  sqlite_delete_function $::DB tstfunc
+} {SQLITE_BUSY}
+do_test schema2-11.3 {
+  set rc [catch {
+    db function tstfunc {}
+  } msg]
+  list $rc $msg
+} {1 {Unable to delete/modify user-function due to active statements}}
+do_test schema2-11.4 {
+  sqlite3_finalize $::STMT
+} {SQLITE_OK}
+do_test schema2-11.5 {
+  db collate tstcollate {}
+  set sql {SELECT * FROM abc}
+  set ::STMT [sqlite3_prepare_v2 $::DB $sql -1 TAIL]
+  sqlite3_step $::STMT
+} {SQLITE_ROW}
+do_test schema2-11.6 {
+  sqlite_delete_collation $::DB tstcollate
+} {SQLITE_BUSY}
+do_test schema2-11.7 {
+  set rc [catch {
+    db collate tstcollate {}
+  } msg]
+  list $rc $msg
+} {1 {Unable to delete/modify collation sequence due to active statements}}
+do_test schema2-11.8 {
+  sqlite3_finalize $::STMT
+} {SQLITE_OK}
+
+finish_test