]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
First code for the new callback-free API. All regression tests pass but the
authordrh <drh@noemail.net>
Tue, 28 Jan 2003 23:13:10 +0000 (23:13 +0000)
committerdrh <drh@noemail.net>
Tue, 28 Jan 2003 23:13:10 +0000 (23:13 +0000)
new API is mostly untested and is unlikely to work. (CVS 852)

FossilOrigin-Name: 065fa818ffc8d7562889172acea16e4e44e773ef

12 files changed:
manifest
manifest.uuid
src/build.c
src/main.c
src/parse.y
src/sqlite.h.in
src/sqliteInt.h
src/test1.c
src/tokenize.c
src/vdbe.c
src/vdbe.h
test/trigger3.test

index 4b56588b0a3931414f082afecb7f13959bf5cd47..0f5340f67114b8af9c14960393e379c230cce0c3 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Added\ssection\son\scomments.\nCorrected\sbroken\s</p>\send\stags.\s(CVS\s851)
-D 2003-01-26T15:28:18
+C First\scode\sfor\sthe\snew\scallback-free\sAPI.\s\sAll\sregression\stests\spass\sbut\sthe\nnew\sAPI\sis\smostly\suntested\sand\sis\sunlikely\sto\swork.\s(CVS\s852)
+D 2003-01-28T23:13:11
 F Makefile.in 6606854b1512f185b8e8c779b8d7fc2750463d64
 F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
@@ -21,7 +21,7 @@ F sqlite.1 83f4a9d37bdf2b7ef079a82d54eaf2e3509ee6ea
 F src/auth.c 9c2db0bc7707f2d2e227f47e3d557b41d44ade75
 F src/btree.c eb4f430b062500d7533c031097d3ff8824eca3ba
 F src/btree.h 17710339f7a8f46e3c7d6d0d4648ef19c584ffda
-F src/build.c d6716dae74cccdcb9db6d05f65f7994762aa6d9f
+F src/build.c 1a4c0d71863f0aa0be7e5a2148b103c3e761c771
 F src/delete.c cbd499f3f9297504c42e328af89bef1a2113d04c
 F src/encode.c faf03741efe921755ec371cf4a6984536de00042
 F src/expr.c 382839b92cb66a34cfa71cf1d2bc8fb818226c90
@@ -29,32 +29,32 @@ F src/func.c 90c583f0b91220f7cd411a2407deaf9327245d63
 F src/hash.c 4fc39feb7b7711f6495ee9f2159559bedb043e1f
 F src/hash.h cd0433998bc1a3759d244e1637fe5a3c13b53bf8
 F src/insert.c db954e955970795819145a3649fd2ad116a58890
-F src/main.c ad3193c56da5acd31bc6cd48aa50dae1962d7c78
+F src/main.c c58cdfb5f0c1938e78d47584e88799b77091700c
 F src/md5.c fe4f9c9c6f71dfc26af8da63e4d04489b1430565
 F src/os.c ed27e178e0c4b71f2807da81b8851f0fadc50778
 F src/os.h afa3e096213bad86845f8bdca81a9e917505e401
 F src/pager.c 95f5c5c775ed47e837ce02b407d80527d93e6c43
 F src/pager.h 540833e8cb826b80ce2e39aa917deee5e12db626
-F src/parse.y a4fbfbe3c4254c96dae8c33264fb54af755a3770
+F src/parse.y aea0819c07ec9c81b810039df9be9d5705b1e497
 F src/printf.c e8e9a0605602cb1a3a2dc754e0978fa9064ecee7
 F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
 F src/select.c c3c0b8263587d290592dca8b4371b5c1162ca684
 F src/shell.c cbb29252f0bd7b144d1e3126e64e17e5a314f2fd
 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
-F src/sqlite.h.in 90657185cff387069d17c5b876a87a6a7a3b6f10
-F src/sqliteInt.h 1d614e04f3c439d7bb60a65f821f8ec53ef6a7e8
+F src/sqlite.h.in ace5c971df379f07ade9ae4b2066cc18ee8b6cfa
+F src/sqliteInt.h 576855338db3e3673605bd08b32a5a8cb3f57cf8
 F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63
 F src/tclsqlite.c 9f2c00a92338c51171ded8943bd42d77f7e69e64
-F src/test1.c 921e5dda494f836d2ebea703bd5b239a7cc963d0
+F src/test1.c c00a4e561287da1976149fe156748253bd8926e7
 F src/test2.c 03f05e984c8e2f2badc44644d42baf72b249096b
 F src/test3.c c12ea7f1c3fbbd58904e81e6cb10ad424e6fc728
 F src/threadtest.c d641a5219e718e18a1a80a50eb9bb549f451f42e
-F src/tokenize.c 7ac1c33e0149647c9eb5959c48992df6906d4809
+F src/tokenize.c 0ba74091f432b79687fc7fbecc9e9087bfb40a56
 F src/trigger.c da142decd2808bc39e801f3bb1f161dbc2bd4005
 F src/update.c f06afa9bf1f777d17702e0f6e33cf44c44bc4f75
 F src/util.c e23f8ffc654923e18f8db2d8e0de97c166fca20f
-F src/vdbe.c 7796485aa510c89aa378a11d3fbeb6fcf3893433
-F src/vdbe.h 754eba497cfe0c3e352b9c101ab2f811f10d0a55
+F src/vdbe.c 899b59df8a6d91020c3d5c54a817fb669f98176b
+F src/vdbe.h 30b808a32f8d66e9948d0a9931f1e1037d2ec2ad
 F src/where.c 5bf7f1e1d756ab3d25a18b24bb42106cb8e14d18
 F test/all.test 873d30e25a41b3aa48fec5633a7ec1816e107029
 F test/auth.test 95aeda24f76b6fd028bdb3d6ae1e30b153d942fe
@@ -110,7 +110,7 @@ F test/tester.tcl 6f603d90881bd835ea27c568a7fecaa57dce91cc
 F test/trans.test 10b53c77e2cc4ad9529c15fdcb390b8d5722ea65
 F test/trigger1.test ec1da76e1a9f618deb96e505f459dcf8a23f2247
 F test/trigger2.test ee346d8c612e7f847c9543058f1b89d094d27ffb
-F test/trigger3.test 5958cdb44e95842298436cb61d5de5251ec2d28e
+F test/trigger3.test 870afef7997a5b86bf3ea893ce0c2e85d6356c72
 F test/trigger4.test 9a5c1406344d743020c2753ae8d6dfe6eb75f818
 F test/unique.test 572aa791327c1e8d797932263e9d67f176cfdb44
 F test/update.test 7ffb062d580a972e7870d0f51d5af3ab9bfeae08
@@ -154,7 +154,7 @@ F www/speed.tcl 4d463e2aea41f688ed320a937f93ff885be918c3
 F www/sqlite.tcl ae3dcfb077e53833b59d4fcc94d8a12c50a44098
 F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
-P bdba796f3b89690ab5d53a9e16924383ef72657c
-R 2fbda7b77729464b96834537506192f2
-U jplyon
-Z 22282820ee286f511aad4850aeea1aad
+P c957f4f0c6b486f25bc567dafeed186f91c8c315
+R fba6d74be1d355dfd1b4bac9d5e01203
+U drh
+Z 6e26856c68db7a18e1f9ce32bbc7b32d
index db4fc6da89c0b237b7fef1e9c1194d43255db582..85ce1efb5d7abb1c887682ebb1a06196c1dbc6a7 100644 (file)
@@ -1 +1 @@
-c957f4f0c6b486f25bc567dafeed186f91c8c315
\ No newline at end of file
+065fa818ffc8d7562889172acea16e4e44e773ef
\ No newline at end of file
index 94045463ba2c4de1650b6fa06d0e2090fd3218b1..9afb8a14a95e1c9f4dbf8adfd0b61039c020e6ad 100644 (file)
@@ -25,7 +25,7 @@
 **     ROLLBACK
 **     PRAGMA
 **
-** $Id: build.c,v 1.125 2003/01/25 14:34:23 drh Exp $
+** $Id: build.c,v 1.126 2003/01/28 23:13:11 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -48,6 +48,17 @@ void sqliteBeginParse(Parse *pParse, int explainFlag){
   }
 }
 
+/*
+** This is a fake callback procedure used when sqlite_exec() is
+** invoked with a NULL callback pointer.  If we pass a NULL callback
+** pointer into sqliteVdbeExec() it will return at every OP_Callback,
+** which we do not want it to do.  So we substitute a pointer to this
+** procedure in place of the NULL.
+*/
+static int fakeCallback(void *NotUsed, int n, char **az1, char **az2){
+  return 0;
+}
+
 /*
 ** This routine is called after a single SQL statement has been
 ** parsed and we want to execute the VDBE code to implement 
@@ -61,24 +72,33 @@ void sqliteBeginParse(Parse *pParse, int explainFlag){
 void sqliteExec(Parse *pParse){
   int rc = SQLITE_OK;
   sqlite *db = pParse->db;
+  Vdbe *v = pParse->pVdbe;
+  int (*xCallback)(void*,int,char**,char**);
+
   if( sqlite_malloc_failed ) return;
-  if( pParse->pVdbe && pParse->nErr==0 ){
-    if( pParse->explain ){
-      rc = sqliteVdbeList(pParse->pVdbe, pParse->xCallback, pParse->pArg, 
-                          &pParse->zErrMsg);
-      db->next_cookie = db->schema_cookie;
-    }else{
-      FILE *trace = (db->flags & SQLITE_VdbeTrace)!=0 ? stdout : 0;
-      sqliteVdbeTrace(pParse->pVdbe, trace);
-      rc = sqliteVdbeExec(pParse->pVdbe, pParse->xCallback, pParse->pArg, 
-                          &pParse->zErrMsg, db->pBusyArg,
-                          db->xBusyCallback);
+  xCallback = pParse->xCallback;
+  if( xCallback==0 && pParse->useCallback ) xCallback = fakeCallback;
+  if( v && pParse->nErr==0 ){
+    FILE *trace = (db->flags & SQLITE_VdbeTrace)!=0 ? stdout : 0;
+    sqliteVdbeTrace(v, trace);
+    sqliteVdbeMakeReady(v, xCallback, pParse->pArg, pParse->explain);
+    if( pParse->useCallback ){
+      if( pParse->explain ){
+        rc = sqliteVdbeList(v);
+        db->next_cookie = db->schema_cookie;
+      }else{
+        sqliteVdbeExec(v);
+      }
+      rc = sqliteVdbeFinalize(v, &pParse->zErrMsg);
       if( rc ) pParse->nErr++;
+      sqliteVdbeDelete(v);
+      pParse->pVdbe = 0;
+      pParse->rc = rc;
+      if( rc ) pParse->nErr++;
+    }else{
+      pParse->rc = pParse->nErr ? SQLITE_ERROR : SQLITE_DONE;
     }
-    sqliteVdbeDelete(pParse->pVdbe);
-    pParse->pVdbe = 0;
     pParse->colNamesSet = 0;
-    pParse->rc = rc;
     pParse->schemaVerified = 0;
   }
   pParse->nTab = 0;
index 23efd5546622eb6a934ba0b4ec9488fa5dcd6693..2be750bec819360dd337fb409c1aa6fee96f2889 100644 (file)
@@ -14,7 +14,7 @@
 ** other files are for internal use by SQLite and should not be
 ** accessed by users of the library.
 **
-** $Id: main.c,v 1.109 2003/01/19 03:59:47 drh Exp $
+** $Id: main.c,v 1.110 2003/01/28 23:13:12 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -69,6 +69,7 @@ int sqliteInitCallback(void *pInit, int argc, char **argv, char **azColName){
         sParse.initFlag = 1;
         sParse.isTemp = argv[4][0] - '0';
         sParse.newTnum = atoi(argv[2]);
+        sParse.useCallback = 1;
         sqliteRunParser(&sParse, argv[3], pData->pzErrMsg);
       }else{
         /* If the SQL column is blank it means this is an index that
@@ -297,6 +298,7 @@ int sqliteInit(sqlite *db, char **pzErrMsg){
   sParse.xCallback = sqliteInitCallback;
   sParse.pArg = (void*)&initData;
   sParse.initFlag = 1;
+  sParse.useCallback = 1;
   sqliteRunParser(&sParse,
       db->file_format>=2 ? init_script : older_init_script,
       pzErrMsg);
@@ -584,29 +586,23 @@ int sqlite_complete(const char *zSql){
 }
 
 /*
-** Execute SQL code.  Return one of the SQLITE_ success/failure
-** codes.  Also write an error message into memory obtained from
-** malloc() and make *pzErrMsg point to that message.
-**
-** If the SQL is a query, then for each row in the query result
-** the xCallback() function is called.  pArg becomes the first
-** argument to xCallback().  If xCallback=NULL then no callback
-** is invoked, even for queries.
+** This routine does the work of either sqlite_exec() or sqlite_compile().
+** It works like sqlite_exec() if pVm==NULL and it works like sqlite_compile()
+** otherwise.
 */
-int sqlite_exec(
+static int sqliteMain(
   sqlite *db,                 /* The database on which the SQL executes */
   const char *zSql,           /* The SQL to be executed */
   sqlite_callback xCallback,  /* Invoke this callback routine */
   void *pArg,                 /* First argument to xCallback() */
-  char **pzErrMsg             /* Write error messages here */
+  const char **pzTail,        /* OUT: Next statement after the first */
+  sqlite_vm **ppVm,           /* OUT: The virtual machine */
+  char **pzErrMsg             /* OUT: Write error messages here */
 ){
   Parse sParse;
 
   if( pzErrMsg ) *pzErrMsg = 0;
   if( sqliteSafetyOn(db) ) goto exec_misuse;
-#ifndef SQLITE_OMIT_TRACE
-  if( db->xTrace ) db->xTrace(db->pTraceArg, zSql);
-#endif
   if( (db->flags & SQLITE_Initialized)==0 ){
     int rc, cnt = 1;
     while( (rc = sqliteInit(db, pzErrMsg))==SQLITE_BUSY
@@ -633,6 +629,10 @@ int sqlite_exec(
   sParse.pBe = db->pBe;
   sParse.xCallback = xCallback;
   sParse.pArg = pArg;
+  sParse.useCallback = ppVm==0;
+#ifndef SQLITE_OMIT_TRACE
+  if( db->xTrace ) db->xTrace(db->pTraceArg, zSql);
+#endif
   sqliteRunParser(&sParse, zSql, pzErrMsg);
   if( sqlite_malloc_failed ){
     sqliteSetString(pzErrMsg, "out of memory", 0);
@@ -650,6 +650,11 @@ int sqlite_exec(
     sqliteResetInternalSchema(db);
   }
   db->recursionDepth--;
+  if( sParse.useCallback==0 ){
+    assert( ppVm );
+    *ppVm = sParse.pVdbe;
+    *pzTail = &sParse.sLastToken.z[sParse.sLastToken.n];
+  }
   if( sqliteSafetyOff(db) ) goto exec_misuse;
   return sParse.rc;
 
@@ -662,6 +667,59 @@ exec_misuse:
   return SQLITE_MISUSE;
 }
 
+/*
+** Execute SQL code.  Return one of the SQLITE_ success/failure
+** codes.  Also write an error message into memory obtained from
+** malloc() and make *pzErrMsg point to that message.
+**
+** If the SQL is a query, then for each row in the query result
+** the xCallback() function is called.  pArg becomes the first
+** argument to xCallback().  If xCallback=NULL then no callback
+** is invoked, even for queries.
+*/
+int sqlite_exec(
+  sqlite *db,                 /* The database on which the SQL executes */
+  const char *zSql,           /* The SQL to be executed */
+  sqlite_callback xCallback,  /* Invoke this callback routine */
+  void *pArg,                 /* First argument to xCallback() */
+  char **pzErrMsg             /* Write error messages here */
+){
+  return sqliteMain(db, zSql, xCallback, pArg, 0, 0, pzErrMsg);
+}
+
+/*
+** Compile a single statement of SQL into a virtual machine.  Return one
+** of the SQLITE_ success/failure codes.  Also write an error message into
+** memory obtained from malloc() and make *pzErrMsg point to that message.
+*/
+int sqlite_compile(
+  sqlite *db,                 /* The database on which the SQL executes */
+  const char *zSql,           /* The SQL to be executed */
+  const char **pzTail,        /* OUT: Next statement after the first */
+  sqlite_vm **ppVm,           /* OUT: The virtual machine */
+  char **pzErrMsg             /* OUT: Write error messages here */
+){
+  return sqliteMain(db, zSql, 0, 0, pzTail, ppVm, pzErrMsg);
+}
+
+/*
+** The following routine destroys a virtual machine that is created by
+** the sqlite_compile() routine.
+**
+** The integer returned is an SQLITE_ success/failure code that describes
+** the result of executing the virtual machine.  An error message is
+** written into memory obtained from malloc and *pzErrMsg is made to
+** point to that error if pzErrMsg is not NULL.  The calling routine
+** should use sqlite_freemem() to delete the message when it has finished
+** with it.
+*/
+int sqlite_finalize(
+  sqlite_vm *pVm,            /* The virtual machine to be destroyed */
+  char **pzErrMsg            /* OUT: Write error messages here */
+){
+  return sqliteVdbeFinalize((Vdbe*)pVm, pzErrMsg);
+}
+
 /*
 ** Return a static string that describes the kind of error specified in the
 ** argument.
index 0b26348eddb63638c1efe129511f02566ee5d2f4..665b46e65d22366f6c7d974ee4326cf81f219f51 100644 (file)
 ** the parser.  Lemon will also generate a header file containing
 ** numeric codes for all of the tokens.
 **
-** @(#) $Id: parse.y,v 1.88 2003/01/18 20:11:07 drh Exp $
+** @(#) $Id: parse.y,v 1.89 2003/01/28 23:13:12 drh Exp $
 */
 %token_prefix TK_
 %token_type {Token}
 %default_type {Token}
 %extra_argument {Parse *pParse}
 %syntax_error {
-  sqliteSetString(&pParse->zErrMsg,"syntax error",0);
-  pParse->sErrToken = TOKEN;
+  if( pParse->zErrMsg==0 ){
+    if( TOKEN.z[0] ){
+      sqliteSetNString(&pParse->zErrMsg, 
+          "near \"", -1, TOKEN.z, TOKEN.n, "\": syntax error", -1, 0);
+    }else{
+      sqliteSetString(&pParse->zErrMsg, "incomplete SQL statement", 0);
+    }
+  }
+  pParse->nErr++;
 }
 %name sqliteParser
 %include {
index 0f5dde875fb01dd5d70e0e6d0a65036abd09c37c..1b1c5289255f5025871a770f2fb97a274961a45b 100644 (file)
 ** This header file defines the interface that the SQLite library
 ** presents to client programs.
 **
-** @(#) $Id: sqlite.h.in,v 1.39 2003/01/16 16:28:54 drh Exp $
+** @(#) $Id: sqlite.h.in,v 1.40 2003/01/28 23:13:12 drh Exp $
 */
 #ifndef _SQLITE_H_
 #define _SQLITE_H_
 #include <stdarg.h>     /* Needed for the definition of va_list */
 
-/*
-** The version of the SQLite library.
-*/
-#define SQLITE_VERSION         "--VERS--"
-
 /*
 ** Make sure we can call this stuff from C++.
 */
 extern "C" {
 #endif
 
+/*
+** The version of the SQLite library.
+*/
+#define SQLITE_VERSION         "--VERS--"
+
 /*
 ** The version string is also compiled into the library so that a program
 ** can check to make sure that the lib*.a file and the *.h file are from
@@ -73,7 +73,7 @@ typedef struct sqlite sqlite;
 ** The Truth:  As currently implemented, all databases are opened
 ** for writing all the time.  Maybe someday we will provide the
 ** ability to open a database readonly.  The mode parameters is
-** provide in anticipation of that enhancement.
+** provided in anticipation of that enhancement.
 */
 sqlite *sqlite_open(const char *filename, int mode, char **errmsg);
 
@@ -118,7 +118,8 @@ typedef int (*sqlite_callback)(void*,int,char**, char**);
 ** message is written into memory obtained from malloc() and
 ** *errmsg is made to point to that message.  The calling function
 ** is responsible for freeing the memory that holds the error
-** message.  If errmsg==NULL, then no error message is ever written.
+** message.   Use sqlite_freemem() for this.  If errmsg==NULL,
+** then no error message is ever written.
 **
 ** The return value is is SQLITE_OK if there are no errors and
 ** some other return code if there is an error.  The particular
@@ -138,7 +139,7 @@ int sqlite_exec(
 );
 
 /*
-** Return values for sqlite_exec()
+** Return values for sqlite_exec() and sqlite_step()
 */
 #define SQLITE_OK           0   /* Successful result */
 #define SQLITE_ERROR        1   /* SQL error or missing database */
@@ -164,6 +165,8 @@ int sqlite_exec(
 #define SQLITE_MISUSE      21   /* Library used incorrectly */
 #define SQLITE_NOLFS       22   /* Uses OS features not supported on host */
 #define SQLITE_AUTH        23   /* Authorization denied */
+#define SQLITE_ROW         100  /* sqlite_step() has another row ready */
+#define SQLITE_DONE        101  /* sqlite_step() has finished executing */
 
 /*
 ** Each entry in an SQLite table has a unique integer key.  (The key is
@@ -501,10 +504,11 @@ int sqlite_aggregate_count(sqlite_func*);
 
 /*
 ** This routine registers a callback with the SQLite library.  The
-** callback is invoked for every attempt to access a column of a table
-** in the database.  The callback returns SQLITE_OK if access is allowed,
-** SQLITE_DENY if the entire SQL statement should be aborted with an error
-** and SQLITE_IGNORE if the column should be treated as a NULL value.
+** callback is invoked (at compile-time, not at run-time) for each
+** attempt to access a column of a table in the database.  The callback
+** returns SQLITE_OK if access is allowed, SQLITE_DENY if the entire
+** SQL statement should be aborted with an error and SQLITE_IGNORE
+** if the column should be treated as a NULL value.
 */
 int sqlite_set_authorizer(
   sqlite*,
@@ -555,17 +559,127 @@ int sqlite_set_authorizer(
 #define SQLITE_IGNORE 2   /* Don't allow access, but don't generate an error */
 
 /*
-** Register a function that is called at every invocation of sqlite_exec().
-** This function can be used (for example) to generate a log file of all
-** SQL executed against a database.
+** Register a function that is called at every invocation of sqlite_exec()
+** or sqlite_compile().  This function can be used (for example) to generate
+** a log file of all SQL executed against a database.
 */
 void *sqlite_trace(sqlite*, void(*xTrace)(void*,const char*), void*);
 
+/*** The Callback-Free API
+** 
+** The following routines implement a new way to access SQLite that does not
+** involve the use of callbacks.
+**
+** An sqlite_vm is an opaque object that represents a single SQL statement
+** that is ready to be executed.
+*/
+typedef struct sqlite_vm sqlite_vm;
+
+/*
+** To execute an SQLite query without the use of callbacks, you first have
+** to compile the SQL using this routine.  The 1st parameter "db" is a pointer
+** to an sqlite object obtained from sqlite_open().  The 2nd parameter
+** "zSql" is the text of the SQL to be compiled.   The remaining parameters
+** are all outputs.
+**
+** *pzTail is made to point to the first character past the end of the first
+** SQL statement in zSql.  This routine only compiles the first statement
+** in zSql, so *pzTail is left pointing to what remains uncompiled.
+**
+** *ppVm is left pointing to a "virtual machine" that can be used to execute
+** the compiled statement.  Or if there is an error, *ppVm may be set to NULL.
+**
+** If any errors are detected during compilation, an error message is written
+** into space obtained from malloc() and *pzErrMsg is made to point to that
+** error message.  The calling routine is responsible for freeing the text
+** of this message when it has finished with it.  Use sqlite_freemem() to
+** free the message.  pzErrMsg may be NULL in which case no error message
+** will be generated.
+**
+** On success, SQLITE_OK is returned.  Otherwise and error code is returned.
+*/
+int sqlite_compile(
+  sqlite *db,                   /* The open database */
+  const char *zSql,             /* SQL statement to be compiled */
+  const char **pzTail,          /* OUT: uncompiled tail of zSql */
+  sqlite_vm **ppVm,             /* OUT: the virtual machine to execute zSql */
+  char **pzErrmsg               /* OUT: Error message. */
+);
+
+/*
+** After an SQL statement has been compiled, it is handed to this routine
+** to be executed.  This routine executes the statement as far as it can
+** go then returns.  The return value will be one of SQLITE_DONE,
+** SQLITE_ERROR, SQLITE_BUSY, SQLITE_ROW, or SQLITE_MISUSE.
+**
+** SQLITE_DONE means that the execute of the SQL statement is complete
+** an no errors have occurred.  sqlite_step() should not be called again
+** for the same virtual machine.  *pN is set to the number of columns in
+** the result set and *pazColName is set to an array of strings that
+** describe the column names and datatypes.  The name of the i-th column
+** is (*pazColName)[i] and the datatype of the i-th column is
+** (*pazColName)[i+*pN].  *pazValue is set to NULL.
+**
+** SQLITE_ERROR means that the virtual machine encountered a run-time
+** error.  sqlite_step() should not be called again for the same
+** virtual machine.  *pN is set to 0 and *pazColName and *pazValue are set
+** to NULL.  Use sqlite_finalize() to obtain the specific error code
+** and the error message text for the error.
+**
+** SQLITE_BUSY means that an attempt to open the database failed because
+** another thread or process is holding a lock.  The calling routine
+** can try again to open the database by calling sqlite_step() again.
+** The return code will only be SQLITE_BUSY if no busy handler is registered
+** using the sqlite_busy_handler() or sqlite_busy_timeout() routines.  If
+** a busy handler callback has been registered but returns 0, then this
+** routine will return SQLITE_ERROR and sqltie_finalize() will return
+** SQLITE_BUSY when it is called.
+**
+** SQLITE_ROW means that a single row of the result is now available.
+** The data is contained in *pazValue.  The value of the i-th column is
+** (*azValue)[i].  *pN and *pazColName are set as described in SQLITE_DONE.
+** Invoke sqlite_step() again to advance to the next row.
+**
+** SQLITE_MISUSE is returned if sqlite_step() is called incorrectly.
+** For example, if you call sqlite_step() after the virtual machine
+** has halted (after a prior call to sqlite_step() has returned SQLITE_DONE)
+** or if you call sqlite_step() with an incorrectly initialized virtual
+** machine or a virtual machine that has been deleted or that is associated
+** with an sqlite structure that has been closed.
+*/
+int sqlite_step(
+  sqlite_vm *pVm,              /* The virtual machine to execute */
+  int *pN,                     /* OUT: Number of columns in result */
+  const char ***pazValue,      /* OUT: Column data */
+  const char ***pazColName     /* OUT: Column names and datatypes */
+);
+
+/*
+** This routine is called to delete a virtual machine after it has finished
+** executing.  The return value is the result code.  SQLITE_OK is returned
+** if the statement executed successfully and some other value is returned if
+** there was any kind of error.  If an error occurred and pzErrMsg is not
+** NULL, then an error message is written into memory obtained from malloc()
+** and *pzErrMsg is made to point to that error message.  The calling routine
+** should use sqlite_freemem() to delete this message when it has finished
+** with it.
+**
+** This routine can be called at any point during the execution of the
+** virtual machine.  If the virtual machine has not completed execution
+** when this routine is called, that is like encountering an error or
+** an interrupt.  (See sqlite_interrupt().)  Incomplete updates may be
+** rolled back and transactions cancelled,  depending on the circumstances,
+** and the result code returned will be SQLITE_ABORT.
+*/
+int sqlite_finalize(sqlite_vm*, char **pzErrMsg);
+
 /*
 ** Attempt to open the file named in the argument as the auxiliary database
 ** file.  The auxiliary database file is used to store TEMP tables.  But
 ** by using this API, it is possible to trick SQLite into opening two
 ** separate databases and acting on them as if they were one.
+**
+****** THIS IS AN EXPERIMENTAL API AND IS SUBJECT TO CHANGE. ******
 */
 int sqlite_open_aux_file(sqlite *db, const char *zName, char **pzErrMsg);
 
index 51d7a55ac435c4ca314ec052ae394ed2c5c04a3b..734eadd1864623987ed89bbb950a0e3635dcd607 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.156 2003/01/18 20:11:07 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.157 2003/01/28 23:13:12 drh Exp $
 */
 #include "config.h"
 #include "sqlite.h"
@@ -748,6 +748,7 @@ struct Parse {
   u8 schemaVerified;   /* True if an OP_VerifySchema has been coded someplace
                        ** other than after an OP_Transaction */
   u8 isTemp;           /* True if parsing temporary tables */
+  u8 useCallback;      /* True if callbacks should be used to report results */
   int newTnum;         /* Table number to use when reparsing CREATE TABLEs */
   int nErr;            /* Number of errors seen */
   int nTab;            /* Number of previously allocated VDBE cursors */
index a518e5bddc710e85cad63fcd1cb803632d88ca67..434662a2138e1c550880d5160afab46bfa0e2a1e 100644 (file)
 ** is not included in the SQLite library.  It is used for automated
 ** testing of the SQLite library.
 **
-** $Id: test1.c,v 1.16 2003/01/13 23:27:33 drh Exp $
+** $Id: test1.c,v 1.17 2003/01/28 23:13:12 drh Exp $
 */
 #include "sqliteInt.h"
 #include "tcl.h"
 #include <stdlib.h>
 #include <string.h>
 
+/*
+** Decode a pointer to an sqlite object.
+*/
+static int getDbPointer(Tcl_Interp *interp, const char *zArg, sqlite **ppDb){
+  if( sscanf(zArg, "%p", (void**)ppDb)!=1 ){
+    Tcl_AppendResult(interp, "\"", zArg, "\" is not a valid pointer value", 0);
+    return TCL_ERROR;
+  }
+  return TCL_OK;
+}
+
+/*
+** Decode a pointer to an sqlite_vm object.
+*/
+static int getVmPointer(Tcl_Interp *interp, const char *zArg, sqlite_vm **ppVm){
+  if( sscanf(zArg, "%p", (void**)ppVm)!=1 ){
+    Tcl_AppendResult(interp, "\"", zArg, "\" is not a valid pointer value", 0);
+    return TCL_ERROR;
+  }
+  return TCL_OK;
+}
+
 /*
 ** Usage:   sqlite_open filename
 **
@@ -45,7 +67,7 @@ static int sqlite_test_open(
     free(zErr);
     return TCL_ERROR;
   }
-  sprintf(zBuf,"%d",(int)db);
+  sprintf(zBuf,"%p", db);
   Tcl_AppendResult(interp, zBuf, 0);
   return TCL_OK;
 }
@@ -91,7 +113,7 @@ static int test_exec_printf(
        " DB FORMAT STRING", 0);
     return TCL_ERROR;
   }
-  db = (sqlite*)strtol(argv[1], 0, 0);
+  if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
   Tcl_DStringInit(&str);
   rc = sqlite_exec_printf(db, argv[2], exec_printf_cb, &str, &zErr, argv[3]);
   sprintf(zBuf, "%d", rc);
@@ -128,7 +150,7 @@ static int test_get_table_printf(
        " DB FORMAT STRING", 0);
     return TCL_ERROR;
   }
-  db = (sqlite*)strtol(argv[1], 0, 0);
+  if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
   Tcl_DStringInit(&str);
   rc = sqlite_get_table_printf(db, argv[2], &aResult, &nRow, &nCol, 
                &zErr, argv[3]);
@@ -169,7 +191,7 @@ static int test_last_rowid(
     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " DB\"", 0);
     return TCL_ERROR;
   }
-  db = (sqlite*)strtol(argv[1], 0, 0);
+  if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
   sprintf(zBuf, "%d", sqlite_last_insert_rowid(db));
   Tcl_AppendResult(interp, zBuf, 0);
   return SQLITE_OK;
@@ -192,7 +214,7 @@ static int sqlite_test_close(
        " FILENAME\"", 0);
     return TCL_ERROR;
   }
-  db = (sqlite*)strtol(argv[1], 0, 0);
+  if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
   sqlite_close(db);
   return TCL_OK;
 }
@@ -251,7 +273,7 @@ static int test_create_function(
        " FILENAME\"", 0);
     return TCL_ERROR;
   }
-  db = (sqlite*)strtol(argv[1], 0, 0);
+  if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
   sqlite_create_function(db, "x_coalesce", -1, ifnullFunc, 0);
   sqlite_create_function(db, "x_sqlite_exec", 1, sqliteExecFunc, db);
   return TCL_OK;
@@ -300,7 +322,7 @@ static int test_create_aggregate(
        " FILENAME\"", 0);
     return TCL_ERROR;
   }
-  db = (sqlite*)strtol(argv[1], 0, 0);
+  if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
   sqlite_create_aggregate(db, "x_count", 0, countStep, countFinalize, 0);
   sqlite_create_aggregate(db, "x_count", 1, countStep, countFinalize, 0);
   return TCL_OK;
@@ -497,7 +519,7 @@ static int test_register_func(
        " DB FUNCTION-NAME", 0);
     return TCL_ERROR;
   }
-  db = (sqlite*)strtol(argv[1], 0, 0);
+  if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
   rc = sqlite_create_function(db, argv[2], -1, testFunc, 0);
   if( rc!=0 ){
     Tcl_AppendResult(interp, sqlite_error_string(rc), 0);
@@ -550,7 +572,7 @@ static int sqlite_datatypes(
        " DB SQL", 0);
     return TCL_ERROR;
   }
-  db = (sqlite*)strtol(argv[1], 0, 0);
+  if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
   rc = sqlite_exec(db, argv[2], rememberDataTypes, interp, 0);
   if( rc!=0 && rc!=SQLITE_ABORT ){
     Tcl_AppendResult(interp, sqlite_error_string(rc), 0);
@@ -659,7 +681,7 @@ static int test_set_authorizer(
          " DB CALLBACK\"", 0);
     return TCL_ERROR;
   }
-  db = (sqlite*)strtol(argv[1], 0, 0);
+  if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
   zCmd = argv[2];
   if( zCmd[0]==0 ){
     sqlite_set_authorizer(db, 0, 0);
@@ -677,6 +699,131 @@ static int test_set_authorizer(
 }
 #endif /* SQLITE_OMIT_AUTHORIZATION */
 
+
+/*
+** Usage:  sqlite_compile  DB  SQL  TAILVAR
+**
+** Attempt to compile an SQL statement.  Return a pointer to the virtual
+** machine used to execute that statement.  Unprocessed SQL is written
+** into TAILVAR.
+*/
+static int test_compile(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  char **argv            /* Text of each argument */
+){
+  sqlite *db;
+  sqlite_vm *vm;
+  int rc;
+  char *zErr = 0;
+  const char *zTail;
+  char zBuf[50];
+  if( argc!=4 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 
+       " DB SQL TAILVAR", 0);
+    return TCL_ERROR;
+  }
+  if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR;
+  rc = sqlite_compile(db, argv[2], &zTail, &vm, &zErr);
+  if( rc ){
+    assert( vm==0 );
+    sprintf(zBuf, "(%d) ", rc);
+    Tcl_AppendResult(interp, zBuf, zErr, 0);
+    sqlite_freemem(zErr);
+    return TCL_ERROR;
+  }
+  sprintf(zBuf, "%p", vm);
+  Tcl_AppendResult(interp, zBuf, 0);
+  Tcl_SetVar(interp, argv[3], zTail, 0);
+  return TCL_OK;
+}
+
+/*
+** Usage:  sqlite_step  VM  NVAR  VALUEVAR  COLNAMEVAR
+**
+** Step a virtual machine.  Return a the result code as a string.
+** Column results are written into three variables.
+*/
+static int test_step(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  char **argv            /* Text of each argument */
+){
+  sqlite_vm *vm;
+  int rc, i;
+  const char **azValue;
+  const char **azColName;
+  int N;
+  char *zRc;
+  char zBuf[50];
+  if( argc!=5 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 
+       " VM NVAR VALUEVAR COLNAMEVAR", 0);
+    return TCL_ERROR;
+  }
+  if( getVmPointer(interp, argv[1], &vm) ) return TCL_ERROR;
+  rc = sqlite_step(vm, &N, &azValue, &azColName);
+  if( rc==SQLITE_DONE || SQLITE_ROW ){
+    sprintf(zBuf, "%d", N);
+    Tcl_SetVar(interp, argv[2], zBuf, 0);
+    Tcl_SetVar(interp, argv[3], "", 0);
+    if( rc==SQLITE_ROW ){
+      for(i=0; i<N; i++){
+        Tcl_SetVar(interp, argv[3], azValue[i] ? azValue[i] : "",
+            TCL_APPEND_VALUE | TCL_LIST_ELEMENT);
+      }
+    }
+    Tcl_SetVar(interp, argv[4], "", 0);
+    for(i=0; i<N*2; i++){
+      Tcl_SetVar(interp, argv[4], azValue[i] ? azValue[i] : "",
+          TCL_APPEND_VALUE | TCL_LIST_ELEMENT);
+    }
+  }
+  switch( rc ){
+    case SQLITE_DONE:   zRc = "SQLITE_DONE";    break;
+    case SQLITE_BUSY:   zRc = "SQLITE_DONE";    break;
+    case SQLITE_ROW:    zRc = "SQLITE_DONE";    break;
+    case SQLITE_ERROR:  zRc = "SQLITE_DONE";    break;
+    case SQLITE_MISUSE: zRc = "SQLITE_MISUSE";  break;
+    default:            zRc = "unknown";        break;
+  }
+  Tcl_AppendResult(interp, zRc, 0);
+  return TCL_OK;
+}
+
+/*
+** Usage:  sqlite_finalize  VM 
+**
+** Shutdown a virtual machine.
+*/
+static int test_finalize(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  char **argv            /* Text of each argument */
+){
+  sqlite_vm *vm;
+  int rc;
+  char *zErrMsg = 0;
+  if( argc!=2 ){
+    Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], 
+       " VM NVAR VALUEVAR COLNAMEVAR", 0);
+    return TCL_ERROR;
+  }
+  if( getVmPointer(interp, argv[1], &vm) ) return TCL_ERROR;
+  rc = sqlite_finalize(vm, &zErrMsg);
+  if( rc ){
+    char zBuf[50];
+    sprintf(zBuf, "(%d) ", rc);
+    Tcl_AppendResult(interp, zBuf, zErrMsg, 0);
+    sqlite_freemem(zErrMsg);
+    return TCL_ERROR;
+  }
+  return TCL_OK;
+}
+
 /*
 ** Register commands with the TCL interpreter.
 */
@@ -706,6 +853,9 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
      { "sqlite_malloc_fail",             (Tcl_CmdProc*)sqlite_malloc_fail    },
      { "sqlite_malloc_stat",             (Tcl_CmdProc*)sqlite_malloc_stat    },
 #endif
+     { "sqlite_compile",                 (Tcl_CmdProc*)test_compile          },
+     { "sqlite_step",                    (Tcl_CmdProc*)test_step             },
+     { "sqlite_finalize",                (Tcl_CmdProc*)test_finalize         },
   };
   int i;
 
index 7e551f9bc5b4842eb69e2cb29d45ea77833dd0f1..df27fa7326faa4535130c10a234edf3100ac9ed6 100644 (file)
@@ -15,7 +15,7 @@
 ** individual tokens and sends those tokens one-by-one over to the
 ** parser for analysis.
 **
-** $Id: tokenize.c,v 1.53 2003/01/07 02:47:48 drh Exp $
+** $Id: tokenize.c,v 1.54 2003/01/28 23:13:12 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -406,6 +406,8 @@ int sqliteRunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
   int nErr = 0;
   int i;
   void *pEngine;
+  int tokenType;
+  int lastTokenParsed = -1;
   sqlite *db = pParse->db;
   extern void *sqliteParserAlloc(void*(*)(int));
   extern void sqliteParserFree(void*, void(*)(void*));
@@ -421,7 +423,6 @@ int sqliteRunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
   }
   pParse->sLastToken.dyn = 0;
   while( sqlite_malloc_failed==0 && zSql[i]!=0 ){
-    int tokenType;
     
     assert( i>=0 );
     pParse->sLastToken.z = &zSql[i];
@@ -446,19 +447,8 @@ int sqliteRunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
       }
       default: {
         sqliteParser(pEngine, tokenType, pParse->sLastToken, pParse);
-        if( pParse->zErrMsg && pParse->sErrToken.z ){
-          sqliteSetNString(pzErrMsg, "near \"", -1, 
-             pParse->sErrToken.z, pParse->sErrToken.n,
-             "\": ", -1,
-             pParse->zErrMsg, -1,
-             0);
-          nErr++;
-          sqliteFree(pParse->zErrMsg);
-          pParse->zErrMsg = 0;
-          goto abort_parse;
-        }else if( pParse->rc!=SQLITE_OK ){
-          sqliteSetString(pzErrMsg, sqlite_error_string(pParse->rc), 0);
-          nErr++;
+        lastTokenParsed = tokenType;
+        if( pParse->rc!=SQLITE_OK ){
           goto abort_parse;
         }
         break;
@@ -466,31 +456,26 @@ int sqliteRunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
     }
   }
 abort_parse:
-  if( zSql[i]==0 && nErr==0 ){
-    sqliteParser(pEngine, TK_SEMI, pParse->sLastToken, pParse);
-    sqliteParser(pEngine, 0, pParse->sLastToken, pParse);
-    if( pParse->zErrMsg && pParse->sErrToken.z ){
-       sqliteSetNString(pzErrMsg, "near \"", -1, 
-          pParse->sErrToken.z, pParse->sErrToken.n,
-          "\": ", -1,
-          pParse->zErrMsg, -1,
-          0);
-       nErr++;
-       sqliteFree(pParse->zErrMsg);
-       pParse->zErrMsg = 0;
+  if( zSql[i]==0 && nErr==0 && pParse->rc==SQLITE_OK ){
+    if( lastTokenParsed!=TK_SEMI ){
+      sqliteParser(pEngine, TK_SEMI, pParse->sLastToken, pParse);
     }
+    sqliteParser(pEngine, 0, pParse->sLastToken, pParse);
   }
   sqliteParserFree(pEngine, free);
+  if( pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE && pParse->zErrMsg==0 ){
+    sqliteSetString(&pParse->zErrMsg, sqlite_error_string(pParse->rc), 0);
+  }
   if( pParse->zErrMsg ){
-    if( pzErrMsg ){
-      sqliteFree(*pzErrMsg);
+    if( pzErrMsg && *pzErrMsg==0 ){
       *pzErrMsg = pParse->zErrMsg;
     }else{
       sqliteFree(pParse->zErrMsg);
     }
+    pParse->zErrMsg = 0;
     if( !nErr ) nErr++;
   }
-  if( pParse->pVdbe ){
+  if( pParse->pVdbe && (pParse->useCallback || pParse->nErr>0) ){
     sqliteVdbeDelete(pParse->pVdbe);
     pParse->pVdbe = 0;
   }
@@ -498,7 +483,7 @@ abort_parse:
     sqliteDeleteTable(pParse->db, pParse->pNewTable);
     pParse->pNewTable = 0;
   }
-  if( nErr>0 && pParse->rc==SQLITE_OK ){
+  if( nErr>0 && (pParse->rc==SQLITE_OK || pParse->rc==SQLITE_DONE) ){
     pParse->rc = SQLITE_ERROR;
   }
   return nErr;
index ac92fa0447b759193a4b29f9651423598e53881b..0839b303bcc8db942720bba4307fecad7dc678c6 100644 (file)
@@ -36,7 +36,7 @@
 ** in this file for details.  If in doubt, do not deviate from existing
 ** commenting and indentation practices when changing or adding code.
 **
-** $Id: vdbe.c,v 1.199 2003/01/19 03:59:47 drh Exp $
+** $Id: vdbe.c,v 1.200 2003/01/28 23:13:12 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -234,7 +234,11 @@ struct Keylist {
 };
 
 /*
-** An instance of the virtual machine
+** An instance of the virtual machine.  This structure contains the complete
+** state of the virtual machine.
+**
+** The "sqlite_vm" structure pointer that is returned by sqlite_compile()
+** is really a pointer to an instance of this structure.
 */
 struct Vdbe {
   sqlite *db;         /* The whole database */
@@ -252,23 +256,47 @@ struct Vdbe {
   char **azColName;   /* Becomes the 4th parameter to callbacks */
   int nCursor;        /* Number of slots in aCsr[] */
   Cursor *aCsr;       /* One element of this array for each open cursor */
-  Keylist *pList;     /* A list of ROWIDs */
   Sorter *pSort;      /* A linked list of objects to be sorted */
   FILE *pFile;        /* At most one open file handler */
   int nField;         /* Number of file fields */
   char **azField;     /* Data for each file field */
-  char *zLine;        /* A single line from the input file */
-  int nLineAlloc;     /* Number of spaces allocated for zLine */
-  int nMem;           /* Number of memory locations currently allocated */
-  Mem *aMem;          /* The memory locations */
-  Agg agg;            /* Aggregate information */
-  int nSet;           /* Number of sets allocated */
-  Set *aSet;          /* An array of sets */
-  int nCallback;      /* Number of callbacks invoked so far */
+  char *zLine;            /* A single line from the input file */
+  int magic;              /* Magic number for sanity checking */
+  int nLineAlloc;         /* Number of spaces allocated for zLine */
+  int nMem;               /* Number of memory locations currently allocated */
+  Mem *aMem;              /* The memory locations */
+  Agg agg;                /* Aggregate information */
+  int nSet;               /* Number of sets allocated */
+  Set *aSet;              /* An array of sets */
+  int nCallback;          /* Number of callbacks invoked so far */
+  Keylist *pList;         /* A list of ROWIDs */
   int keylistStackDepth;  /* The size of the "keylist" stack */
   Keylist **keylistStack; /* The stack used by opcodes ListPush & ListPop */
+  int pc;                 /* The program counter */
+  int rc;                 /* Value to return */
+  unsigned uniqueCnt;     /* Used by OP_MakeRecord when P2!=0 */
+  int errorAction;        /* Recovery action to do in case of an error */
+  int undoTransOnError;   /* If error, either ROLLBACK or COMMIT */
+  int inTempTrans;        /* True if temp database is transactioned */
+  int returnStack[100];   /* Return address stack for OP_Gosub & OP_Return */
+  int returnDepth;        /* Next unused element in returnStack[] */
+  int nResColumn;         /* Number of columns in one row of the result set */
+  char **azResColumn;                        /* Values for one row of result */ 
+  int (*xCallback)(void*,int,char**,char**); /* Callback for SELECT results */
+  void *pCbArg;                              /* First argument to xCallback() */
+  int popStack;           /* Pop the stack this much on entry to VdbeExec() */
+  char *zErrMsg;          /* Error message written here */
+  u8 explain;             /* True if EXPLAIN present on SQL command */
 };
 
+/*
+** The following are allowed values for Vdbe.magic
+*/
+#define VDBE_MAGIC_INIT     0x26bceaa5    /* Building a VDBE program */
+#define VDBE_MAGIC_RUN      0xbdf20da3    /* VDBE is ready to execute */
+#define VDBE_MAGIC_HALT     0x519c2973    /* VDBE has completed execution */
+#define VDBE_MAGIC_DEAD     0xb606c3c8    /* The VDBE has been deallocated */
+
 /*
 ** When debugging the code generator in a symbolic debugger, one can
 ** set the sqlite_vdbe_addop_trace to 1 and all opcodes will be printed
@@ -288,6 +316,7 @@ Vdbe *sqliteVdbeCreate(sqlite *db){
   if( p==0 ) return 0;
   p->pBt = db->pBe;
   p->db = db;
+  p->magic = VDBE_MAGIC_INIT;
   return p;
 }
 
@@ -319,6 +348,7 @@ int sqliteVdbeAddOp(Vdbe *p, int op, int p1, int p2){
 
   i = p->nOp;
   p->nOp++;
+  assert( p->magic==VDBE_MAGIC_INIT );
   if( i>=p->nOpAlloc ){
     int oldSize = p->nOpAlloc;
     Op *aNew;
@@ -360,6 +390,7 @@ int sqliteVdbeAddOp(Vdbe *p, int op, int p1, int p2){
 int sqliteVdbeMakeLabel(Vdbe *p){
   int i;
   i = p->nLabel++;
+  assert( p->magic==VDBE_MAGIC_INIT );
   if( i>=p->nLabelAlloc ){
     int *aNew;
     p->nLabelAlloc = p->nLabelAlloc*2 + 10;
@@ -385,6 +416,7 @@ int sqliteVdbeMakeLabel(Vdbe *p){
 */
 void sqliteVdbeResolveLabel(Vdbe *p, int x){
   int j;
+  assert( p->magic==VDBE_MAGIC_INIT );
   if( x<0 && (-x)<=p->nLabel && p->aOp ){
     if( p->aLabel[-1-x]==p->nOp ) return;
     assert( p->aLabel[-1-x]<0 );
@@ -399,6 +431,7 @@ void sqliteVdbeResolveLabel(Vdbe *p, int x){
 ** Return the address of the next instruction to be inserted.
 */
 int sqliteVdbeCurrentAddr(Vdbe *p){
+  assert( p->magic==VDBE_MAGIC_INIT );
   return p->nOp;
 }
 
@@ -408,6 +441,7 @@ int sqliteVdbeCurrentAddr(Vdbe *p){
 */
 int sqliteVdbeAddOpList(Vdbe *p, int nOp, VdbeOp const *aOp){
   int addr;
+  assert( p->magic==VDBE_MAGIC_INIT );
   if( p->nOp + nOp >= p->nOpAlloc ){
     int oldSize = p->nOpAlloc;
     Op *aNew;
@@ -444,6 +478,7 @@ int sqliteVdbeAddOpList(Vdbe *p, int nOp, VdbeOp const *aOp){
 ** few minor changes to the program.
 */
 void sqliteVdbeChangeP1(Vdbe *p, int addr, int val){
+  assert( p->magic==VDBE_MAGIC_INIT );
   if( p && addr>=0 && p->nOp>addr && p->aOp ){
     p->aOp[addr].p1 = val;
   }
@@ -455,6 +490,7 @@ void sqliteVdbeChangeP1(Vdbe *p, int addr, int val){
 */
 void sqliteVdbeChangeP2(Vdbe *p, int addr, int val){
   assert( val>=0 );
+  assert( p->magic==VDBE_MAGIC_INIT );
   if( p && addr>=0 && p->nOp>addr && p->aOp ){
     p->aOp[addr].p2 = val;
   }
@@ -479,6 +515,7 @@ void sqliteVdbeChangeP2(Vdbe *p, int addr, int val){
 */
 void sqliteVdbeChangeP3(Vdbe *p, int addr, const char *zP3, int n){
   Op *pOp;
+  assert( p->magic==VDBE_MAGIC_INIT );
   if( p==0 || p->aOp==0 ) return;
   if( addr<0 || addr>=p->nOp ){
     addr = p->nOp - 1;
@@ -512,6 +549,7 @@ void sqliteVdbeChangeP3(Vdbe *p, int addr, const char *zP3, int n){
 */
 void sqliteVdbeDequoteP3(Vdbe *p, int addr){
   Op *pOp;
+  assert( p->magic==VDBE_MAGIC_INIT );
   if( p->aOp==0 || addr<0 || addr>=p->nOp ) return;
   pOp = &p->aOp[addr];
   if( pOp->p3==0 || pOp->p3[0]==0 ) return;
@@ -532,6 +570,7 @@ void sqliteVdbeCompressSpace(Vdbe *p, int addr){
   char *z;
   int i, j;
   Op *pOp;
+  assert( p->magic==VDBE_MAGIC_INIT );
   if( p->aOp==0 || addr<0 || addr>=p->nOp ) return;
   pOp = &p->aOp[addr];
   if( pOp->p3type==P3_POINTER ){
@@ -563,6 +602,7 @@ void sqliteVdbeCompressSpace(Vdbe *p, int addr){
 */
 int sqliteVdbeFindOp(Vdbe *p, int op, int p2){
   int i;
+  assert( p->magic==VDBE_MAGIC_INIT );
   for(i=0; i<p->nOp; i++){
     if( p->aOp[i].opcode==op && p->aOp[i].p2==p2 ) return 1;
   }
@@ -688,6 +728,66 @@ int sqlite_aggregate_count(sqlite_func *p){
   return p->cnt;
 }
 
+/*
+** Advance the virtual machine to the next output row.
+**
+** The return vale will be either SQLITE_BUSY, SQLITE_DONE, or
+** SQLITE_ROW.
+**
+** SQLITE_BUSY means that the virtual machine attempted to open
+** a locked database and there is no busy callback registered.
+** Call sqlite_step() again to retry the open.  *pN is set to 0
+** and *pazColName and *pazValue are both set to NULL.
+**
+** SQLITE_DONE means that the virtual machine has finished
+** executing.  sqlite_step() should not be called again on this
+** virtual machine.  *pN and *pazColName are set appropriately
+** but *pazValue is set to NULL.
+**
+** SQLITE_ROW means that the virtual machine has generated another
+** row of the result set.  *pN is set to the number of columns in
+** the row.  *pazColName is set to the names of the columns followed
+** by the column datatypes.  *pazValue is set to the values of each
+** column in the row.  The value of the i-th column is (*pazValue)[i].
+** The name of the i-th column is (*pazColName)[i] and the datatype
+** of the i-th column is (*pazColName)[i+*pN].
+**
+** If a run-time error is encountered, SQLITE_DONE is returned.  You
+** can access the error code and error message using the sqlite_finalize()
+** routine. 
+*/
+int sqlite_step(
+  sqlite_vm *pVm,              /* The virtual machine to execute */
+  int *pN,                     /* OUT: Number of columns in result */
+  const char ***pazValue,      /* OUT: Column data */
+  const char ***pazColName     /* OUT: Column names and datatypes */
+){
+  Vdbe *p = (Vdbe*)pVm;
+  int rc;
+
+  if( p->magic!=VDBE_MAGIC_RUN ){
+    return SQLITE_MISUSE;
+  }
+  if( p->explain ){
+    rc = sqliteVdbeList(p);
+  }else{
+    rc = sqliteVdbeExec(p);
+  }
+  if( rc!=SQLITE_DONE ){
+    *pazColName = (const char**)p->azColName;
+    *pN = p->nResColumn;
+  }else{
+    *pN = 0;
+    *pazColName = 0;
+  }
+  if( rc==SQLITE_ROW ){
+    *pazValue = (const char**)p->azResColumn;
+  }else{
+    *pazValue = 0;
+  }
+  return rc;
+}
+
 /*
 ** Reset an Agg structure.  Delete all its contents. 
 **
@@ -1079,6 +1179,9 @@ static void Cleanup(Vdbe *p){
     p->keylistStackDepth = 0;
     p->keylistStack = 0;
   }
+  sqliteFree(p->zErrMsg);
+  p->zErrMsg = 0;
+  p->magic = VDBE_MAGIC_DEAD;
 }
 
 /*
@@ -1111,61 +1214,61 @@ void sqliteVdbeDelete(Vdbe *p){
 ** This feature is used to implement "EXPLAIN".
 */
 int sqliteVdbeList(
-  Vdbe *p,                   /* The VDBE */
-  sqlite_callback xCallback, /* The callback */
-  void *pArg,                /* 1st argument to callback */
-  char **pzErrMsg            /* Error msg written here */
+  Vdbe *p                   /* The VDBE */
 ){
   sqlite *db = p->db;
-  int i, rc;
-  char *azValue[6];
-  char zAddr[20];
-  char zP1[20];
-  char zP2[20];
-  char zP3[40];
+  int i;
   static char *azColumnNames[] = {
-     "addr", "opcode", "p1", "p2", "p3", 0
+     "addr", "opcode", "p1",  "p2",  "p3", 
+     "int",  "text",   "int", "int", "text",
+     0
   };
 
-  if( xCallback==0 ) return 0;
-  azValue[0] = zAddr;
-  azValue[2] = zP1;
-  azValue[3] = zP2;
-  azValue[5] = 0;
-  rc = SQLITE_OK;
-  for(i=0; rc==SQLITE_OK && i<p->nOp; i++){
+  assert( p->popStack==0 );
+  assert( p->explain );
+  p->azColName = azColumnNames;
+  p->azResColumn = p->zStack;
+  for(i=0; i<5; i++) p->zStack[i] = p->aStack[i].z;
+  p->rc = SQLITE_OK;
+  for(i=p->pc; p->rc==SQLITE_OK && i<p->nOp; i++){
     if( db->flags & SQLITE_Interrupt ){
       db->flags &= ~SQLITE_Interrupt;
       if( db->magic!=SQLITE_MAGIC_BUSY ){
-        rc = SQLITE_MISUSE;
+        p->rc = SQLITE_MISUSE;
       }else{
-        rc = SQLITE_INTERRUPT;
+        p->rc = SQLITE_INTERRUPT;
       }
-      sqliteSetString(pzErrMsg, sqlite_error_string(rc), 0);
+      sqliteSetString(&p->zErrMsg, sqlite_error_string(p->rc), 0);
       break;
     }
-    sprintf(zAddr,"%d",i);
-    sprintf(zP1,"%d", p->aOp[i].p1);
-    sprintf(zP2,"%d", p->aOp[i].p2);
+    sprintf(p->zStack[0],"%d",i);
+    sprintf(p->zStack[2],"%d", p->aOp[i].p1);
+    sprintf(p->zStack[3],"%d", p->aOp[i].p2);
     if( p->aOp[i].p3type==P3_POINTER ){
-      sprintf(zP3, "ptr(%#x)", (int)p->aOp[i].p3);
-      azValue[4] = zP3;
+      sprintf(p->aStack[4].z, "ptr(%#x)", (int)p->aOp[i].p3);
+      p->zStack[4] = p->aStack[4].z;
     }else{
-      azValue[4] = p->aOp[i].p3;
+      p->zStack[4] = p->aOp[i].p3;
+    }
+    p->zStack[1] = sqliteOpcodeNames[p->aOp[i].opcode];
+    if( p->xCallback==0 ){
+      p->pc = i+1;
+      p->azResColumn = p->zStack;
+      p->nResColumn = 5;
+      return SQLITE_CALLBACK;
     }
-    azValue[1] = sqliteOpcodeNames[p->aOp[i].opcode];
     if( sqliteSafetyOff(db) ){
-      rc = SQLITE_MISUSE;
+      p->rc = SQLITE_MISUSE;
       break;
     }
-    if( xCallback(pArg, 5, azValue, azColumnNames) ){
-      rc = SQLITE_ABORT;
+    if( p->xCallback(p->pCbArg, 5, p->zStack, p->azColName) ){
+      p->rc = SQLITE_ABORT;
     }
     if( sqliteSafetyOn(db) ){
-      rc = SQLITE_MISUSE;
+      p->rc = SQLITE_MISUSE;
     }
   }
-  return rc;
+  return p->rc==SQLITE_OK ? SQLITE_OK : SQLITE_ERROR;
 }
 
 /*
@@ -1343,94 +1446,131 @@ __inline__ unsigned long long int hwtime(void){
 
 
 /*
-** Execute the program in the VDBE.
+** Prepare a virtual machine for execution.  This involves things such
+** as allocating stack space and initializing the program counter.
+** After the VDBE has be prepped, it can be executed by one or more
+** calls to sqliteVdbeExec().  
+**
+** The behavior of sqliteVdbeExec() is influenced by the parameters to
+** this routine.  If xCallback is NULL, then sqliteVdbeExec() will return
+** with SQLITE_CALLBACK whenever there is a row of the result set ready
+** to be delivered.  p->azResColumn will point to the row and 
+** p->nResColumn gives the number of columns in the row.  If xCallback
+** is not NULL, then the xCallback() routine is invoked to process each
+** row in the result set.
+*/
+void sqliteVdbeMakeReady(
+  Vdbe *p,                       /* The VDBE */
+  sqlite_callback xCallback,     /* Result callback */
+  void *pCallbackArg,            /* 1st argument to xCallback() */
+  int isExplain                  /* True if the EXPLAIN keywords is present */
+){
+  int n;
+
+  assert( p!=0 );
+  assert( p->aStack==0 );
+  assert( p->magic==VDBE_MAGIC_INIT );
+
+  /* Add a HALT instruction to the very end of the program.
+  */
+  sqliteVdbeAddOp(p, OP_Halt, 0, 0);
+
+  /* No instruction ever pushes more than a single element onto the
+  ** stack.  And the stack never grows on successive executions of the
+  ** same loop.  So the total number of instructions is an upper bound
+  ** on the maximum stack depth required.
+  **
+  ** Allocation all the stack space we will ever need.
+  */
+  n = isExplain ? 10 : p->nOp;
+  p->aStack = sqliteMalloc( n*(sizeof(p->aStack[0]) + 2*sizeof(char*)) );
+  p->zStack = (char**)&p->aStack[n];
+  p->azColName = (char**)&p->zStack[n];
+
+  sqliteHashInit(&p->agg.hash, SQLITE_HASH_BINARY, 0);
+  p->agg.pSearch = 0;
+#ifdef MEMORY_DEBUG
+  if( access("vdbe_trace",0)==0 ){
+    p->trace = stdout;
+  }
+#endif
+  p->tos = -1;
+  p->pc = 0;
+  p->rc = SQLITE_OK;
+  p->uniqueCnt = 0;
+  p->returnDepth = 0;
+  p->errorAction = OE_Abort;
+  p->undoTransOnError = 0;
+  p->xCallback = xCallback;
+  p->pCbArg = pCallbackArg;
+  p->popStack =  0;
+  p->explain = isExplain;
+  p->magic = VDBE_MAGIC_RUN;
+#ifdef VDBE_PROFILE
+  for(i=0; i<p->nOp; i++){
+    p->aOp[i].cnt = 0;
+    p->aOp[i].cycles = 0;
+  }
+#endif
+}
+
+/*
+** Execute as much of a VDBE program as we can then return.
+**
+** sqliteVdbeMakeReady() must be called before this routine in order to
+** close the program with a final OP_Halt and to set up the callbacks
+** and the error message pointer.
+**
+** Whenever a row or result data is available, this routine will either
+** invoke the result callback (if there is one) or return with
+** SQLITE_CALLBACK.
+**
+** If an attempt is made to open a locked database, then this routine
+** will either invoke the busy callback (if there is one) or it will
+** return SQLITE_BUSY.
 **
 ** If an error occurs, an error message is written to memory obtained
-** from sqliteMalloc() and *pzErrMsg is made to point to that memory.
-** The return parameter is the number of errors.
+** from sqliteMalloc() and p->zErrMsg is made to point to that memory.
+** The error code is stored in p->rc and this routine returns SQLITE_ERROR.
 **
 ** If the callback ever returns non-zero, then the program exits
-** immediately.  There will be no error message but the function
-** does return SQLITE_ABORT.
+** immediately.  There will be no error message but the p->rc field is
+** set to SQLITE_ABORT and this routine will return SQLITE_ERROR.
 **
-** A memory allocation error causes this routine to return SQLITE_NOMEM
-** and abandon furture processing.
+** A memory allocation error causes p->rc to be set SQLITE_NOMEM and this
+** routien to return SQLITE_ERROR.
 **
 ** Other fatal errors return SQLITE_ERROR.
 **
-** If a database file could not be opened because it is locked by
-** another database instance, then the xBusy() callback is invoked
-** with pBusyArg as its first argument, the name of the table as the
-** second argument, and the number of times the open has been attempted
-** as the third argument.  The xBusy() callback will typically wait
-** for the database file to be openable, then return.  If xBusy()
-** returns non-zero, another attempt is made to open the file.  If
-** xBusy() returns zero, or if xBusy is NULL, then execution halts
-** and this routine returns SQLITE_BUSY.
+** After this routine has finished, sqliteVdbeFinalize() should be
+** used to clean up the mess that was left behind.
 */
 int sqliteVdbeExec(
-  Vdbe *p,                   /* The VDBE */
-  sqlite_callback xCallback, /* The callback */
-  void *pArg,                /* 1st argument to callback */
-  char **pzErrMsg,           /* Error msg written here */
-  void *pBusyArg,            /* 1st argument to the busy callback */
-  int (*xBusy)(void*,const char*,int)  /* Called when a file is busy */
+  Vdbe *p                    /* The VDBE */
 ){
   int pc;                    /* The program counter */
   Op *pOp;                   /* Current operation */
-  int rc;                    /* Value to return */
+  int rc = SQLITE_OK;        /* Value to return */
   Btree *pBt = p->pBt;       /* The backend driver */
   sqlite *db = p->db;        /* The database */
-  char **zStack;             /* Text stack */
-  Stack *aStack;             /* Additional stack information */
-  unsigned uniqueCnt = 0;     /* Used by OP_MakeRecord when P2!=0 */
-  int errorAction = OE_Abort; /* Recovery action to do in case of an error */
-  int undoTransOnError = 0;   /* If error, either ROLLBACK or COMMIT */
-  int inTempTrans = 0;        /* True if temp database is transactioned */
-  char zBuf[100];             /* Space to sprintf() an integer */
-  int returnStack[100];     /* Return address stack for OP_Gosub & OP_Return */
-  int returnDepth = 0;      /* Next unused element in returnStack[] */
+  char **zStack = p->zStack; /* Text stack */
+  Stack *aStack = p->aStack; /* Additional stack information */
+  char zBuf[100];            /* Space to sprintf() an integer */
 #ifdef VDBE_PROFILE
-  unsigned long long start;
-  int origPc;
+  unsigned long long start;  /* CPU clock count at start of opcode */
+  int origPc;                /* Program counter at start of opcode */
 #endif
 
-  /* No instruction ever pushes more than a single element onto the
-  ** stack.  And the stack never grows on successive executions of the
-  ** same loop.  So the total number of instructions is an upper bound
-  ** on the maximum stack depth required.
-  **
-  ** Allocation all the stack space we will ever need.
-  */
-  sqliteVdbeAddOp(p, OP_Halt, 0, 0);
-  aStack = sqliteMalloc( p->nOp*(sizeof(aStack[0]) + 2*sizeof(char*)) );
-  p->aStack = aStack;
-  zStack = p->zStack = (char**)&aStack[p->nOp];
-  p->azColName = (char**)&zStack[p->nOp];
-  p->tos = -1;
-#ifdef VDBE_PROFILE
-  {
-    int i;
-    for(i=0; i<p->nOp; i++){
-      p->aOp[i].cnt = 0;
-      p->aOp[i].cycles = 0;
-    }
-  }
-#endif
-
-  /* Initialize the aggregrate hash table.
-  */
-  sqliteHashInit(&p->agg.hash, SQLITE_HASH_BINARY, 0);
-  p->agg.pSearch = 0;
-
-  rc = SQLITE_OK;
-#ifdef MEMORY_DEBUG
-  if( access("vdbe_trace",0)==0 ){
-    p->trace = stdout;
-  }
-#endif
+  if( p->magic!=VDBE_MAGIC_RUN ) return SQLITE_MISUSE;
+  assert( db->magic==SQLITE_MAGIC_BUSY );
+  assert( p->rc==SQLITE_OK );
+  assert( p->explain==0 );
   if( sqlite_malloc_failed ) goto no_mem;
-  for(pc=0; rc==SQLITE_OK; pc++){
+  if( p->popStack ){
+    PopStack(p, p->popStack);
+    p->popStack = 0;
+  }
+  for(pc=p->pc; rc==SQLITE_OK; pc++){
     assert( pc>=0 && pc<p->nOp );
 #ifdef VDBE_PROFILE
     origPc = pc;
@@ -1500,12 +1640,12 @@ case OP_Goto: {
 ** with a fatal error.
 */
 case OP_Gosub: {
-  if( returnDepth>=sizeof(returnStack)/sizeof(returnStack[0]) ){
-    sqliteSetString(pzErrMsg, "return address stack overflow", 0);
-    rc = SQLITE_INTERNAL;
-    goto cleanup;
+  if( p->returnDepth>=sizeof(p->returnStack)/sizeof(p->returnStack[0]) ){
+    sqliteSetString(&p->zErrMsg, "return address stack overflow", 0);
+    p->rc = SQLITE_INTERNAL;
+    return SQLITE_ERROR;
   }
-  returnStack[returnDepth++] = pc+1;
+  p->returnStack[p->returnDepth++] = pc+1;
   pc = pOp->p2 - 1;
   break;
 }
@@ -1517,13 +1657,13 @@ case OP_Gosub: {
 ** processing aborts with a fatal error.
 */
 case OP_Return: {
-  if( returnDepth<=0 ){
-    sqliteSetString(pzErrMsg, "return address stack underflow", 0);
-    rc = SQLITE_INTERNAL;
-    goto cleanup;
+  if( p->returnDepth<=0 ){
+    sqliteSetString(&p->zErrMsg, "return address stack underflow", 0);
+    p->rc = SQLITE_INTERNAL;
+    return SQLITE_ERROR;
   }
-  returnDepth--;
-  pc = returnStack[returnDepth] - 1;
+  p->returnDepth--;
+  pc = p->returnStack[p->returnDepth] - 1;
   break;
 }
 
@@ -1546,16 +1686,16 @@ case OP_Return: {
 */
 case OP_Halt: {
   if( pOp->p1!=SQLITE_OK ){
-    rc = pOp->p1;
-    errorAction = pOp->p2;
+    p->rc = pOp->p1;
+    p->errorAction = pOp->p2;
     if( pOp->p3 ){
-       sqliteSetString(pzErrMsg, pOp->p3, 0);
-       goto cleanup;
+      sqliteSetString(&p->zErrMsg, pOp->p3, 0);
     }
-    goto abort_due_to_error;
   }else{
-    goto cleanup;
+    p->rc = SQLITE_OK;
   }
+  p->magic = VDBE_MAGIC_HALT;
+  return SQLITE_DONE;
 }
 
 /* Opcode: Integer P1 * P3
@@ -1742,14 +1882,19 @@ case OP_Callback: {
     }
   }
   zStack[p->tos+1] = 0;
-  if( xCallback!=0 ){
-    if( sqliteSafetyOff(db) ) goto abort_due_to_misuse; 
-    if( xCallback(pArg, pOp->p1, &zStack[i], p->azColName)!=0 ){
-      rc = SQLITE_ABORT;
-    }
-    if( sqliteSafetyOn(db) ) goto abort_due_to_misuse;
-    p->nCallback++;
-  }
+  if( p->xCallback==0 ){
+    p->azResColumn = &zStack[i];
+    p->nResColumn = pOp->p1;
+    p->popStack = pOp->p1;
+    p->pc = pc + 1;
+    return SQLITE_CALLBACK;
+  }
+  if( sqliteSafetyOff(db) ) goto abort_due_to_misuse; 
+  if( p->xCallback(p->pCbArg, pOp->p1, &zStack[i], p->azColName)!=0 ){
+    rc = SQLITE_ABORT;
+  }
+  if( sqliteSafetyOn(db) ) goto abort_due_to_misuse;
+  p->nCallback++;
   PopStack(p, pOp->p1);
   if( sqlite_malloc_failed ) goto no_mem;
   break;
@@ -1772,15 +1917,15 @@ case OP_Callback: {
 ** in cases where the result set is empty.
 */
 case OP_NullCallback: {
-  if( xCallback!=0 && p->nCallback==0 ){
+  if( p->nCallback==0 && p->xCallback!=0 ){
     if( sqliteSafetyOff(db) ) goto abort_due_to_misuse; 
-    if( xCallback(pArg, pOp->p1, 0, p->azColName)!=0 ){
+    if( p->xCallback(p->pCbArg, pOp->p1, 0, p->azColName)!=0 ){
       rc = SQLITE_ABORT;
     }
     if( sqliteSafetyOn(db) ) goto abort_due_to_misuse;
     p->nCallback++;
+    if( sqlite_malloc_failed ) goto no_mem;
   }
-  if( sqlite_malloc_failed ) goto no_mem;
   break;
 }
 
@@ -2005,7 +2150,7 @@ case OP_Function: {
     zStack[p->tos] = 0;
   }
   if( ctx.isError ){
-    sqliteSetString(pzErrMsg, 
+    sqliteSetString(&p->zErrMsg, 
        zStack[p->tos] ? zStack[p->tos] : "user function error", 0);
     rc = SQLITE_ERROR;
   }
@@ -2725,7 +2870,7 @@ case OP_MakeRecord: {
       nByte += aStack[i].n;
     }
   }
-  if( addUnique ) nByte += sizeof(uniqueCnt);
+  if( addUnique ) nByte += sizeof(p->uniqueCnt);
   if( nByte + nField + 1 < 256 ){
     idxWidth = 1;
   }else if( nByte + 2*nField + 2 < 65536 ){
@@ -2745,7 +2890,7 @@ case OP_MakeRecord: {
     if( zNewRecord==0 ) goto no_mem;
   }
   j = 0;
-  addr = idxWidth*(nField+1) + addUnique*sizeof(uniqueCnt);
+  addr = idxWidth*(nField+1) + addUnique*sizeof(p->uniqueCnt);
   for(i=p->tos-nField+1; i<=p->tos; i++){
     zNewRecord[j++] = addr & 0xff;
     if( idxWidth>1 ){
@@ -2766,9 +2911,9 @@ case OP_MakeRecord: {
     }
   }
   if( addUnique ){
-    memcpy(&zNewRecord[j], &uniqueCnt, sizeof(uniqueCnt));
-    uniqueCnt++;
-    j += sizeof(uniqueCnt);
+    memcpy(&zNewRecord[j], &p->uniqueCnt, sizeof(p->uniqueCnt));
+    p->uniqueCnt++;
+    j += sizeof(p->uniqueCnt);
   }
   for(i=p->tos-nField+1; i<=p->tos; i++){
     if( (aStack[i].flags & STK_Null)==0 ){
@@ -3006,20 +3151,25 @@ case OP_Checkpoint: {
 ** can be made to the database.
 */
 case OP_Transaction: {
-  int busy = 0;
-  if( db->pBeTemp && !inTempTrans ){
+  int busy = 1;
+  if( db->pBeTemp && !p->inTempTrans ){
     rc = sqliteBtreeBeginTrans(db->pBeTemp);
     if( rc!=SQLITE_OK ){
       goto abort_due_to_error;
     }
-    inTempTrans = 1;
+    p->inTempTrans = 1;
   }
-  if( pOp->p1==0 ) do{
+  while( pOp->p1==0 && busy ){
     rc = sqliteBtreeBeginTrans(pBt);
     switch( rc ){
       case SQLITE_BUSY: {
-        if( xBusy==0 || (*xBusy)(pBusyArg, "", ++busy)==0 ){
-          sqliteSetString(pzErrMsg, sqlite_error_string(rc), 0);
+        if( db->xBusyCallback==0 ){
+          p->pc = pc;
+          p->undoTransOnError = 1;
+          p->rc = SQLITE_BUSY;
+          return SQLITE_BUSY;
+        }else if( (*db->xBusyCallback)(db->pBusyArg, "", busy++)==0 ){
+          sqliteSetString(&p->zErrMsg, sqlite_error_string(rc), 0);
           busy = 0;
         }
         break;
@@ -3029,7 +3179,7 @@ case OP_Transaction: {
         /* Fall thru into the next case */
       }
       case SQLITE_OK: {
-        inTempTrans = 0;
+        p->inTempTrans = 0;
         busy = 0;
         break;
       }
@@ -3037,8 +3187,8 @@ case OP_Transaction: {
         goto abort_due_to_error;
       }
     }
-  }while( busy );
-  undoTransOnError = 1;
+  }
+  p->undoTransOnError = 1;
   break;
 }
 
@@ -3052,7 +3202,7 @@ case OP_Transaction: {
 */
 case OP_Commit: {
   if( db->pBeTemp==0 || (rc = sqliteBtreeCommit(db->pBeTemp))==SQLITE_OK ){
-    rc = inTempTrans ? SQLITE_OK : sqliteBtreeCommit(pBt);
+    rc = p->inTempTrans ? SQLITE_OK : sqliteBtreeCommit(pBt);
   }
   if( rc==SQLITE_OK ){
     sqliteCommitInternalChanges(db);
@@ -3061,7 +3211,7 @@ case OP_Commit: {
     sqliteBtreeRollback(pBt);
     sqliteRollbackInternalChanges(db);
   }
-  inTempTrans = 0;
+  p->inTempTrans = 0;
   break;
 }
 
@@ -3162,7 +3312,7 @@ case OP_VerifyCookie: {
   assert( pOp->p2<SQLITE_N_BTREE_META );
   rc = sqliteBtreeGetMeta(pBt, aMeta);
   if( rc==SQLITE_OK && aMeta[1+pOp->p2]!=pOp->p1 ){
-    sqliteSetString(pzErrMsg, "database schema has changed", 0);
+    sqliteSetString(&p->zErrMsg, "database schema has changed", 0);
     rc = SQLITE_SCHEMA;
   }
   break;
@@ -3241,9 +3391,9 @@ case OP_Open: {
     p2 = p->aStack[tos].i;
     POPSTACK;
     if( p2<2 ){
-      sqliteSetString(pzErrMsg, "root page number less than 2", 0);
+      sqliteSetString(&p->zErrMsg, "root page number less than 2", 0);
       rc = SQLITE_INTERNAL;
-      goto cleanup;
+      break;
     }
   }
   VERIFY( if( i<0 ) goto bad_instruction; )
@@ -3256,8 +3406,12 @@ case OP_Open: {
     rc = sqliteBtreeCursor(pX, p2, wrFlag, &p->aCsr[i].pCursor);
     switch( rc ){
       case SQLITE_BUSY: {
-        if( xBusy==0 || (*xBusy)(pBusyArg, pOp->p3, ++busy)==0 ){
-          sqliteSetString(pzErrMsg, sqlite_error_string(rc), 0);
+        if( db->xBusyCallback==0 ){
+          p->pc = pc;
+          p->rc = SQLITE_BUSY;
+          return SQLITE_BUSY;
+        }else if( (*db->xBusyCallback)(db->pBusyArg, pOp->p3, ++busy)==0 ){
+          sqliteSetString(&p->zErrMsg, sqlite_error_string(rc), 0);
           busy = 0;
         }
         break;
@@ -3271,6 +3425,9 @@ case OP_Open: {
       }
     }
   }while( busy );
+  if( p2<=0 ){
+    POPSTACK;
+  }
   break;
 }
 
@@ -4124,7 +4281,7 @@ case OP_IdxPut: {
         ){
           rc = SQLITE_CONSTRAINT;
           if( pOp->p3 && pOp->p3[0] ){
-            sqliteSetString(pzErrMsg, "duplicate index entry: ", pOp->p3,0);
+            sqliteSetString(&p->zErrMsg, "duplicate index entry: ", pOp->p3,0);
           }
           goto abort_due_to_error;
         }
@@ -4681,9 +4838,15 @@ case OP_SortNext: {
 case OP_SortCallback: {
   int i = p->tos;
   VERIFY( if( i<0 ) goto not_enough_stack; )
-  if( xCallback!=0 ){
+  if( p->xCallback==0 ){
+    p->pc = pc+1;
+    p->azResColumn = (char**)zStack[i];
+    p->nResColumn = pOp->p1;
+    p->popStack = 1;
+    return SQLITE_CALLBACK;
+  }else{
     if( sqliteSafetyOff(db) ) goto abort_due_to_misuse;
-    if( xCallback(pArg, pOp->p1, (char**)zStack[i], p->azColName)!=0 ){
+    if( p->xCallback(p->pCbArg, pOp->p1, (char**)zStack[i], p->azColName)!=0 ){
       rc = SQLITE_ABORT;
     }
     if( sqliteSafetyOn(db) ) goto abort_due_to_misuse;
@@ -4720,9 +4883,8 @@ case OP_FileOpen: {
     p->pFile = fopen(pOp->p3, "r");
   }
   if( p->pFile==0 ){
-    sqliteSetString(pzErrMsg,"unable to open file: ", pOp->p3, 0);
+    sqliteSetString(&p->zErrMsg,"unable to open file: ", pOp->p3, 0);
     rc = SQLITE_ERROR;
-    goto cleanup;
   }
   break;
 }
@@ -5309,7 +5471,7 @@ case OP_SetNext: {
 */
 default: {
   sprintf(zBuf,"%d",pOp->opcode);
-  sqliteSetString(pzErrMsg, "unknown opcode ", zBuf, 0);
+  sqliteSetString(&p->zErrMsg, "unknown opcode ", zBuf, 0);
   rc = SQLITE_INTERNAL;
   break;
 }
@@ -5341,7 +5503,7 @@ default: {
     */
 #ifndef NDEBUG
     if( pc<-1 || pc>=p->nOp ){
-      sqliteSetString(pzErrMsg, "jump destination out of range", 0);
+      sqliteSetString(&p->zErrMsg, "jump destination out of range", 0);
       rc = SQLITE_INTERNAL;
     }
     if( p->trace && p->tos>=0 ){
@@ -5394,14 +5556,101 @@ default: {
       fprintf(p->trace,"\n");
     }
 #endif
+  }  /* The end of the for(;;) loop the loops through opcodes */
+
+  /* If we reach this point, it means that execution is finished.
+  */
+vdbe_halt:
+  if( rc ){
+    p->rc = rc;
+    rc = SQLITE_ERROR;
+  }else{
+    rc = SQLITE_DONE;
   }
+  p->magic = VDBE_MAGIC_HALT;
+  return rc;
+
+  /* Jump to here if a malloc() fails.  It's hard to get a malloc()
+  ** to fail on a modern VM computer, so this code is untested.
+  */
+no_mem:
+  sqliteSetString(&p->zErrMsg, "out of memory", 0);
+  rc = SQLITE_NOMEM;
+  goto vdbe_halt;
+
+  /* Jump to here for an SQLITE_MISUSE error.
+  */
+abort_due_to_misuse:
+  rc = SQLITE_MISUSE;
+  /* Fall thru into abort_due_to_error */
+
+  /* Jump to here for any other kind of fatal error.  The "rc" variable
+  ** should hold the error number.
+  */
+abort_due_to_error:
+  sqliteSetString(&p->zErrMsg, sqlite_error_string(rc), 0);
+  goto vdbe_halt;
+
+  /* Jump to here if the sqlite_interrupt() API sets the interrupt
+  ** flag.
+  */
+abort_due_to_interrupt:
+  assert( db->flags & SQLITE_Interrupt );
+  db->flags &= ~SQLITE_Interrupt;
+  if( db->magic!=SQLITE_MAGIC_BUSY ){
+    rc = SQLITE_MISUSE;
+  }else{
+    rc = SQLITE_INTERRUPT;
+  }
+  sqliteSetString(&p->zErrMsg, sqlite_error_string(rc), 0);
+  goto vdbe_halt;
+
+  /* Jump to here if a operator is encountered that requires more stack
+  ** operands than are currently available on the stack.
+  */
+not_enough_stack:
+  sprintf(zBuf,"%d",pc);
+  sqliteSetString(&p->zErrMsg, "too few operands on stack at ", zBuf, 0);
+  rc = SQLITE_INTERNAL;
+  goto vdbe_halt;
+
+  /* Jump here if an illegal or illformed instruction is executed.
+  */
+VERIFY(
+bad_instruction:
+  sprintf(zBuf,"%d",pc);
+  sqliteSetString(&p->zErrMsg, "illegal operation at ", zBuf, 0);
+  rc = SQLITE_INTERNAL;
+  goto vdbe_halt;
+)
+}
+
+
+/*
+** Clean up the VDBE after execution.  Return an integer which is the
+** result code.
+*/
+int sqliteVdbeFinalize(Vdbe *p, char **pzErrMsg){
+  sqlite *db = p->db;
+  Btree *pBt = p->pBt;
 
-cleanup:
+  if( p->magic!=VDBE_MAGIC_RUN && p->magic!=VDBE_MAGIC_HALT ){
+    sqliteSetString(pzErrMsg, sqlite_error_string(SQLITE_MISUSE), 0);
+    return SQLITE_MISUSE;
+  }
+  if( p->zErrMsg ){
+    if( pzErrMsg && *pzErrMsg==0 ){
+      *pzErrMsg = p->zErrMsg;
+    }else{
+      sqliteFree(p->zErrMsg);
+    }
+    p->zErrMsg = 0;
+  }
   Cleanup(p);
-  if( rc!=SQLITE_OK ){
-    switch( errorAction ){
+  if( p->rc!=SQLITE_OK ){
+    switch( p->errorAction ){
       case OE_Abort: {
-        if( !undoTransOnError ){
+        if( !p->undoTransOnError ){
           sqliteBtreeRollbackCkpt(pBt);
           if( db->pBeTemp ) sqliteBtreeRollbackCkpt(db->pBeTemp);
           break;
@@ -5416,7 +5665,7 @@ cleanup:
         break;
       }
       default: {
-        if( undoTransOnError ){
+        if( p->undoTransOnError ){
           sqliteBtreeCommit(pBt);
           if( db->pBeTemp ) sqliteBtreeCommit(db->pBeTemp);
           db->flags &= ~SQLITE_InTrans;
@@ -5429,7 +5678,7 @@ cleanup:
   }
   sqliteBtreeCommitCkpt(pBt);
   if( db->pBeTemp ) sqliteBtreeCommitCkpt(db->pBeTemp);
-  assert( p->tos<pc || sqlite_malloc_failed==1 );
+  assert( p->tos<p->pc || sqlite_malloc_failed==1 );
 #ifdef VDBE_PROFILE
   {
     FILE *out = fopen("vdbe_profile.out", "a");
@@ -5452,58 +5701,5 @@ cleanup:
     }
   }
 #endif
-  return rc;
-
-  /* Jump to here if a malloc() fails.  It's hard to get a malloc()
-  ** to fail on a modern VM computer, so this code is untested.
-  */
-no_mem:
-  sqliteSetString(pzErrMsg, "out of memory", 0);
-  rc = SQLITE_NOMEM;
-  goto cleanup;
-
-  /* Jump to here for an SQLITE_MISUSE error.
-  */
-abort_due_to_misuse:
-  rc = SQLITE_MISUSE;
-  /* Fall thru into abort_due_to_error */
-
-  /* Jump to here for any other kind of fatal error.  The "rc" variable
-  ** should hold the error number.
-  */
-abort_due_to_error:
-  sqliteSetString(pzErrMsg, sqlite_error_string(rc), 0);
-  goto cleanup;
-
-  /* Jump to here if the sqlite_interrupt() API sets the interrupt
-  ** flag.
-  */
-abort_due_to_interrupt:
-  assert( db->flags & SQLITE_Interrupt );
-  db->flags &= ~SQLITE_Interrupt;
-  if( db->magic!=SQLITE_MAGIC_BUSY ){
-    rc = SQLITE_MISUSE;
-  }else{
-    rc = SQLITE_INTERRUPT;
-  }
-  sqliteSetString(pzErrMsg, sqlite_error_string(rc), 0);
-  goto cleanup;
-
-  /* Jump to here if a operator is encountered that requires more stack
-  ** operands than are currently available on the stack.
-  */
-not_enough_stack:
-  sprintf(zBuf,"%d",pc);
-  sqliteSetString(pzErrMsg, "too few operands on stack at ", zBuf, 0);
-  rc = SQLITE_INTERNAL;
-
-  /* Jump here if an illegal or illformed instruction is executed.
-  */
-VERIFY(
-bad_instruction:
-  sprintf(zBuf,"%d",pc);
-  sqliteSetString(pzErrMsg, "illegal operation at ", zBuf, 0);
-  rc = SQLITE_INTERNAL;
-  goto cleanup;
-)
+  return p->rc;
 }
index 6986463b74b87846127f4d5d9f1ac0d48c764171..535b1e7d60c408fce309fcdee0f29db981f10593 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.61 2003/01/01 23:06:21 drh Exp $
+** $Id: vdbe.h,v 1.62 2003/01/28 23:13:13 drh Exp $
 */
 #ifndef _SQLITE_VDBE_H_
 #define _SQLITE_VDBE_H_
@@ -62,6 +62,13 @@ typedef struct VdbeOp VdbeOp;
 */
 #define ADDR(X)  (-1-(X))
 
+/*
+** The sqliteVdbeExec() routine can return any of the normal SQLite return
+** codes defined in sqlite.h.  But it can also return the following
+** additional values:
+*/
+#define SQLITE_CALLBACK    100    /* sqliteVdbeExec() hit an OP_Callback */
+
 /*
 ** The makefile scans the vdbe.c source file and creates the "opcodes.h"
 ** header file that defines a number for each opcode used by the VDBE.
@@ -83,9 +90,10 @@ void sqliteVdbeDequoteP3(Vdbe*, int addr);
 int sqliteVdbeFindOp(Vdbe*, int, int);
 int sqliteVdbeMakeLabel(Vdbe*);
 void sqliteVdbeDelete(Vdbe*);
-int sqliteVdbeExec(Vdbe*,sqlite_callback,void*,char**,void*,
-                   int(*)(void*,const char*,int));
-int sqliteVdbeList(Vdbe*,sqlite_callback,void*,char**);
+void sqliteVdbeMakeReady(Vdbe*,sqlite_callback,void*,int);
+int sqliteVdbeExec(Vdbe*);
+int sqliteVdbeList(Vdbe*);
+int sqliteVdbeFinalize(Vdbe*,char**);
 void sqliteVdbeResolveLabel(Vdbe*, int);
 int sqliteVdbeCurrentAddr(Vdbe*);
 void sqliteVdbeTrace(Vdbe*,FILE*);
index af45c686431a971a8e00dc2b29fbfb03591c4c10..1da144e81780e6187041e4c723d7c5240844edea 100644 (file)
@@ -36,7 +36,6 @@ do_test trigger3-1.1 {
         INSERT INTO tbl VALUES (1, 5, 6);
     }
 } {1 {Trigger abort}}
-
 do_test trigger3-1.2 {
     execsql {
        SELECT * FROM tbl;