diff --git a/manifest b/manifest index 4b56588b0a..0f5340f671 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Added\ssection\son\scomments.\nCorrected\sbroken\s

\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 diff --git a/manifest.uuid b/manifest.uuid index db4fc6da89..85ce1efb5d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c957f4f0c6b486f25bc567dafeed186f91c8c315 \ No newline at end of file +065fa818ffc8d7562889172acea16e4e44e773ef \ No newline at end of file diff --git a/src/build.c b/src/build.c index 94045463ba..9afb8a14a9 100644 --- a/src/build.c +++ b/src/build.c @@ -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 @@ -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; diff --git a/src/main.c b/src/main.c index 23efd55466..2be750bec8 100644 --- a/src/main.c +++ b/src/main.c @@ -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. diff --git a/src/parse.y b/src/parse.y index 0b26348edd..665b46e65d 100644 --- a/src/parse.y +++ b/src/parse.y @@ -14,15 +14,22 @@ ** 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 { diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 0f5dde875f..1b1c528925 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -12,17 +12,12 @@ ** 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 /* 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++. */ @@ -30,6 +25,11 @@ 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); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 51d7a55ac4..734eadd186 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -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 */ diff --git a/src/test1.c b/src/test1.c index a518e5bddc..434662a213 100644 --- a/src/test1.c +++ b/src/test1.c @@ -13,13 +13,35 @@ ** 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 #include +/* +** 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; idb; 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; diff --git a/src/vdbe.c b/src/vdbe.c index ac92fa0447..0839b303bc 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -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 @@ -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; inOp; 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 && inOp; 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 && inOp; 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,57 +1446,34 @@ __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(). ** -** 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. -** -** 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. -** -** A memory allocation error causes this routine to return SQLITE_NOMEM -** and abandon furture processing. -** -** 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. +** 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. */ -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 */ +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 pc; /* The program counter */ - Op *pOp; /* Current operation */ - int rc; /* 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[] */ -#ifdef VDBE_PROFILE - unsigned long long start; - int origPc; -#endif + 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 @@ -1402,35 +1482,95 @@ int sqliteVdbeExec( ** ** 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; inOp; i++){ - p->aOp[i].cnt = 0; - p->aOp[i].cycles = 0; - } - } -#endif + 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]; - /* 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 + 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; inOp; 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 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 p->rc field is +** set to SQLITE_ABORT and this routine will return SQLITE_ERROR. +** +** 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. +** +** After this routine has finished, sqliteVdbeFinalize() should be +** used to clean up the mess that was left behind. +*/ +int sqliteVdbeExec( + Vdbe *p /* The VDBE */ +){ + int pc; /* The program counter */ + Op *pOp; /* Current operation */ + int rc = SQLITE_OK; /* Value to return */ + Btree *pBt = p->pBt; /* The backend driver */ + sqlite *db = p->db; /* The database */ + 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; /* CPU clock count at start of opcode */ + int origPc; /* Program counter at start of opcode */ +#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 && pcnOp ); #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->p2p2]!=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 */ -cleanup: + /* 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; + + 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->tostospc || 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; } diff --git a/src/vdbe.h b/src/vdbe.h index 6986463b74..535b1e7d60 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -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*); diff --git a/test/trigger3.test b/test/trigger3.test index af45c68643..1da144e817 100644 --- a/test/trigger3.test +++ b/test/trigger3.test @@ -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;