Index: VERSION ================================================================== --- VERSION +++ VERSION @@ -1,1 +1,1 @@ -1.0.10 +1.0.11 Index: src/build.c ================================================================== --- src/build.c +++ src/build.c @@ -31,11 +31,11 @@ ** DROP INDEX ** creating expressions and ID lists ** COPY ** VACUUM ** -** $Id: build.c,v 1.23 2000/08/03 15:09:20 drh Exp $ +** $Id: build.c,v 1.24 2000/10/16 22:06:42 drh Exp $ */ #include "sqliteInt.h" /* ** This routine is called after a single SQL statement has been @@ -46,24 +46,26 @@ ** ** Note that if an error occurred, it might be the case that ** no VDBE code was generated. */ void sqliteExec(Parse *pParse){ + int rc = SQLITE_OK; if( pParse->pVdbe ){ if( pParse->explain ){ - sqliteVdbeList(pParse->pVdbe, pParse->xCallback, pParse->pArg, - &pParse->zErrMsg); + rc = sqliteVdbeList(pParse->pVdbe, pParse->xCallback, pParse->pArg, + &pParse->zErrMsg); }else{ FILE *trace = (pParse->db->flags & SQLITE_VdbeTrace)!=0 ? stderr : 0; sqliteVdbeTrace(pParse->pVdbe, trace); - sqliteVdbeExec(pParse->pVdbe, pParse->xCallback, pParse->pArg, - &pParse->zErrMsg, pParse->db->pBusyArg, - pParse->db->xBusyCallback); + rc = sqliteVdbeExec(pParse->pVdbe, pParse->xCallback, pParse->pArg, + &pParse->zErrMsg, pParse->db->pBusyArg, + pParse->db->xBusyCallback); } sqliteVdbeDelete(pParse->pVdbe); pParse->pVdbe = 0; pParse->colNamesSet = 0; + pParse->rc = rc; } } /* ** Construct a new expression node and return a pointer to it. Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -24,11 +24,11 @@ ** Main file for the SQLite library. The routines in this file ** implement the programmer interface to the library. Routines in ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** -** $Id: main.c,v 1.19 2000/10/11 19:28:52 drh Exp $ +** $Id: main.c,v 1.20 2000/10/16 22:06:42 drh Exp $ */ #include "sqliteInt.h" /* ** This is the callback routine for the code that initializes the @@ -152,11 +152,11 @@ }; /* Create a virtual machine to run the initialization program. Run ** the program. The delete the virtual machine. */ - vdbe = sqliteVdbeCreate(db->pBe); + vdbe = sqliteVdbeCreate(db); if( vdbe==0 ){ sqliteSetString(pzErrMsg, "out of memory",0); return 1; } sqliteVdbeAddOpList(vdbe, sizeof(initProg)/sizeof(initProg[0]), initProg); @@ -225,11 +225,11 @@ /* Attempt to read the schema */ rc = sqliteInit(db, pzErrMsg); if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){ sqlite_close(db); return 0; - }else{ + }else /* if( pzErrMsg ) */{ free(*pzErrMsg); *pzErrMsg = 0; } return db; } @@ -309,13 +309,12 @@ } memset(&sParse, 0, sizeof(sParse)); sParse.db = db; sParse.xCallback = xCallback; sParse.pArg = pArg; - rc = sqliteRunParser(&sParse, zSql, pzErrMsg); - sqliteStrRealloc(pzErrMsg); - return rc; + sqliteRunParser(&sParse, zSql, pzErrMsg); + return sParse.rc; } /* ** This routine implements a busy callback that sleeps and tries ** again until a timeout value is reached. The timeout value is @@ -380,5 +379,12 @@ sqlite_busy_handler(db, sqlite_default_busy_callback, (void*)ms); }else{ sqlite_busy_handler(db, 0, 0); } } + +/* +** Cause any pending operation to stop at its earliest opportunity. +*/ +void sqlite_interrupt(sqlite *db){ + db->flags |= SQLITE_Interrupt; +} Index: src/select.c ================================================================== --- src/select.c +++ src/select.c @@ -22,11 +22,11 @@ ** ************************************************************************* ** This file contains C code routines that are called by the parser ** to handle SELECT statements. ** -** $Id: select.c,v 1.26 2000/07/29 13:06:59 drh Exp $ +** $Id: select.c,v 1.27 2000/10/16 22:06:42 drh Exp $ */ #include "sqliteInt.h" /* ** Allocate a new Select structure and return a pointer to that @@ -422,11 +422,11 @@ ** If an error occurs, return NULL and leave a message in pParse. */ Vdbe *sqliteGetVdbe(Parse *pParse){ Vdbe *v = pParse->pVdbe; if( v==0 ){ - v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); + v = pParse->pVdbe = sqliteVdbeCreate(pParse->db); } if( v==0 ){ sqliteSetString(&pParse->zErrMsg, "out of memory", 0); pParse->nErr++; } @@ -816,11 +816,11 @@ /* Begin generating code. */ v = pParse->pVdbe; if( v==0 ){ - v = pParse->pVdbe = sqliteVdbeCreate(pParse->db->pBe); + v = pParse->pVdbe = sqliteVdbeCreate(pParse->db); } if( v==0 ){ sqliteSetString(&pParse->zErrMsg, "out of memory", 0); pParse->nErr++; return 1; Index: src/shell.c ================================================================== --- src/shell.c +++ src/shell.c @@ -22,27 +22,37 @@ ** ************************************************************************* ** This file contains code to implement the "sqlite" command line ** utility for accessing SQLite databases. ** -** $Id: shell.c,v 1.26 2000/10/08 22:20:58 drh Exp $ +** $Id: shell.c,v 1.27 2000/10/16 22:06:42 drh Exp $ */ #include #include #include #include "sqlite.h" #include #include +#ifdef OS_UNIX +# include +#endif #if defined(HAVE_READLINE) && HAVE_READLINE==1 # include # include #else # define readline getline # define add_history(X) #endif +/* +** The following is the open SQLite database. We make a pointer +** to this database a static variable so that it can be accessed +** by the SIGINT handler to interrupt database processing. +*/ +static sqlite *db = 0; + /* ** This routine reads a line of text from standard input, stores ** the text in memory obtained from malloc() and returns a pointer ** to the text. NULL is returned at end of file, or if malloc() ** fails. @@ -226,10 +236,17 @@ break; } z += i + 1; } } + +/* +** This routine runs when the user presses Ctrl-C +*/ +static void interrupt_handler(int NotUsed){ + if( db ) sqlite_interrupt(db); +} /* ** This is the callback routine that the SQLite library ** invokes for each row of a query result. */ @@ -445,11 +462,10 @@ /* Process the input line. */ if( nArg==0 ) return; n = strlen(azArg[0]); c = azArg[0][0]; - if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){ char *zErrMsg = 0; if( nArg==1 ){ sqlite_exec(db, "SELECT name, type, sql FROM sqlite_master " @@ -667,19 +683,21 @@ azArg[0]); } } int main(int argc, char **argv){ - sqlite *db; char *zErrMsg = 0; char *argv0 = argv[0]; struct callback_data data; memset(&data, 0, sizeof(data)); data.mode = MODE_List; strcpy(data.separator,"|"); data.showHeader = 0; +#ifdef SIGINT + signal(SIGINT, interrupt_handler); +#endif while( argc>=2 && argv[1][0]=='-' ){ if( strcmp(argv[1],"-html")==0 ){ data.mode = MODE_Html; argc--; argv++; Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -22,11 +22,11 @@ ** ************************************************************************* ** This header file defines the interface that the sqlite library ** presents to client programs. ** -** @(#) $Id: sqlite.h.in,v 1.5 2000/10/09 12:57:01 drh Exp $ +** @(#) $Id: sqlite.h.in,v 1.6 2000/10/16 22:06:42 drh Exp $ */ #ifndef _SQLITE_H_ #define _SQLITE_H_ #include /* Needed for the definition of va_list */ @@ -128,17 +128,27 @@ /* ** Return values for sqlite_exec() */ #define SQLITE_OK 0 /* Successful result */ -#define SQLITE_INTERNAL 1 /* An internal logic error in SQLite */ -#define SQLITE_ERROR 2 /* SQL error or missing database */ +#define SQLITE_ERROR 1 /* SQL error or missing database */ +#define SQLITE_INTERNAL 2 /* An internal logic error in SQLite */ #define SQLITE_PERM 3 /* Access permission denied */ #define SQLITE_ABORT 4 /* Callback routine requested an abort */ #define SQLITE_BUSY 5 /* One or more database files are locked */ #define SQLITE_NOMEM 6 /* A malloc() failed */ #define SQLITE_READONLY 7 /* Attempt to write a readonly database */ +#define SQLITE_INTERRUPT 8 /* Operation terminated by sqlite_interrupt() */ + +/* This function causes any pending database operation to abort and +** return at its earliest opportunity. This routine is typically +** called in response to a user include such as pressing "Cancel" +** or Ctrl-C where the user wants a long query operation to halt +** immediately. +*/ +void sqlite_interrupt(sqlite*); + /* This function returns true if the given input string comprises ** one or more complete SQL statements. ** ** The algorithm is simple. If the last token other than spaces Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -21,11 +21,11 @@ ** http://www.hwaci.com/drh/ ** ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.30 2000/08/28 15:51:44 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.31 2000/10/16 22:06:42 drh Exp $ */ #include "sqlite.h" #include "dbbe.h" #include "vdbe.h" #include "parse.h" @@ -133,12 +133,13 @@ }; /* ** Possible values for the sqlite.flags. */ -#define SQLITE_VdbeTrace 0x00000001 -#define SQLITE_Initialized 0x00000002 +#define SQLITE_VdbeTrace 0x00000001 /* True to trace VDBE execution */ +#define SQLITE_Initialized 0x00000002 /* True after initialization */ +#define SQLITE_Interrupt 0x00000004 /* Cancel current operation */ /* ** Current file format version */ #define SQLITE_FileFormat 2 @@ -323,10 +324,11 @@ /* ** An SQL parser context */ struct Parse { sqlite *db; /* The main database structure */ + int rc; /* Return code from execution */ sqlite_callback xCallback; /* The callback function */ void *pArg; /* First argument to the callback function */ char *zErrMsg; /* An error message */ Token sErrToken; /* The token at which the error occurred */ Token sFirstToken; /* The first token parsed */ Index: src/tokenize.c ================================================================== --- src/tokenize.c +++ src/tokenize.c @@ -25,11 +25,11 @@ ** ** This file contains C code that splits an SQL input string up into ** individual tokens and sends those tokens one-by-one over to the ** parser for analysis. ** -** $Id: tokenize.c,v 1.13 2000/08/09 17:17:25 drh Exp $ +** $Id: tokenize.c,v 1.14 2000/10/16 22:06:42 drh Exp $ */ #include "sqliteInt.h" #include #include @@ -296,11 +296,11 @@ return 1; } /* ** Run the parser on the given SQL string. The parser structure is -** passed in. Return the number of errors. +** passed in. An SQLITE_ status code. */ int sqliteRunParser(Parse *pParse, char *zSql, char **pzErrMsg){ int nErr = 0; int i; void *pEngine; @@ -309,10 +309,12 @@ extern void *sqliteParserAlloc(void*(*)(int)); extern void sqliteParserFree(void*, void(*)(void*)); extern int sqliteParser(void*, int, ...); extern void sqliteParserTrace(FILE*, char *); + pParse->db->flags &= ~SQLITE_Interrupt; + pParse->rc = SQLITE_OK; i = 0; sqliteParseInfoReset(pParse); pEngine = sqliteParserAlloc((void*(*)(int))malloc); if( pEngine==0 ){ sqliteSetString(pzErrMsg, "out of memory", 0); @@ -320,10 +322,15 @@ } sqliteParserTrace(trace, "parser: "); while( nErr==0 && i>=0 && zSql[i]!=0 ){ int tokenType; + if( (pParse->db->flags & SQLITE_Interrupt)!=0 ){ + pParse->rc = SQLITE_INTERRUPT; + sqliteSetString(pzErrMsg, "interrupt", 0); + break; + } pParse->sLastToken.z = &zSql[i]; pParse->sLastToken.n = sqliteGetToken(&zSql[i], &tokenType); i += pParse->sLastToken.n; if( once ){ pParse->sFirstToken = pParse->sLastToken; @@ -389,11 +396,11 @@ pParse->zErrMsg = 0; } break; } } - if( nErr==0 ){ + if( nErr==0 && (pParse->db->flags & SQLITE_Interrupt)==0 ){ sqliteParser(pEngine, 0, pParse->sLastToken, pParse); if( pParse->zErrMsg && pParse->sErrToken.z ){ sqliteSetNString(pzErrMsg, "near \"", -1, pParse->sErrToken.z, pParse->sErrToken.n, "\": ", -1, @@ -421,7 +428,11 @@ if( pParse->pNewTable ){ sqliteDeleteTable(pParse->db, pParse->pNewTable); pParse->pNewTable = 0; } sqliteParseInfoReset(pParse); + sqliteStrRealloc(pzErrMsg); + if( nErr>0 && pParse->rc==SQLITE_OK ){ + pParse->rc = SQLITE_ERROR; + } return nErr; } Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -39,11 +39,11 @@ ** Most of the code in this file is taken up by the sqliteVdbeExec() ** function which does the work of interpreting a VDBE program. ** But other routines are also provided to help in building up ** a program instruction by instruction. ** -** $Id: vdbe.c,v 1.42 2000/10/11 19:28:53 drh Exp $ +** $Id: vdbe.c,v 1.43 2000/10/16 22:06:43 drh Exp $ */ #include "sqliteInt.h" #include #include @@ -167,10 +167,11 @@ /* ** An instance of the virtual machine */ struct Vdbe { + sqlite *db; /* The whole database */ Dbbe *pBe; /* Opaque context structure used by DB backend */ FILE *trace; /* Write an execution trace here, if not NULL */ int nOp; /* Number of instructions in the program */ int nOpAlloc; /* Number of slots allocated for aOp[] */ Op *aOp; /* Space to hold the virtual machine's program */ @@ -202,15 +203,16 @@ }; /* ** Create a new virtual database engine. */ -Vdbe *sqliteVdbeCreate(Dbbe *pBe){ +Vdbe *sqliteVdbeCreate(sqlite *db){ Vdbe *p; p = sqliteMalloc( sizeof(Vdbe) ); - p->pBe = pBe; + p->pBe = db->pBe; + p->db = db; return p; } /* ** Turn tracing on or off @@ -834,10 +836,16 @@ azValue[3] = zP2; azValue[5] = 0; rc = SQLITE_OK; /* if( pzErrMsg ){ *pzErrMsg = 0; } */ for(i=0; rc==SQLITE_OK && inOp; i++){ + if( p->db->flags & SQLITE_Interrupt ){ + p->db->flags &= ~SQLITE_Interrupt; + sqliteSetString(pzErrMsg, "interrupted", 0); + rc = SQLITE_INTERRUPT; + break; + } sprintf(zAddr,"%d",i); sprintf(zP1,"%d", p->aOp[i].p1); sprintf(zP2,"%d", p->aOp[i].p2); azValue[4] = p->aOp[i].p3; azValue[1] = zOpName[p->aOp[i].opcode]; @@ -926,10 +934,19 @@ } #endif /* if( pzErrMsg ){ *pzErrMsg = 0; } */ for(pc=0; rc==SQLITE_OK && pcnOp && pc>=0; pc++){ pOp = &p->aOp[pc]; + + /* Interrupt processing if requested. + */ + if( p->db->flags & SQLITE_Interrupt ){ + p->db->flags &= ~SQLITE_Interrupt; + rc = SQLITE_INTERRUPT; + sqliteSetString(pzErrMsg, "interrupted", 0); + break; + } /* Only allow tracing if NDEBUG is not defined. */ #ifndef NDEBUG if( p->trace ){ Index: src/vdbe.h ================================================================== --- src/vdbe.h +++ src/vdbe.h @@ -25,11 +25,11 @@ ** ** This header defines the interface to the virtual database engine ** 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.13 2000/10/11 19:28:53 drh Exp $ +** $Id: vdbe.h,v 1.14 2000/10/16 22:06:43 drh Exp $ */ #ifndef _SQLITE_VDBE_H_ #define _SQLITE_VDBE_H_ #include @@ -180,11 +180,11 @@ /* ** Prototypes for the VDBE interface. See comments on the implementation ** for a description of what each of these routines does. */ -Vdbe *sqliteVdbeCreate(Dbbe*); +Vdbe *sqliteVdbeCreate(sqlite*); int sqliteVdbeAddOp(Vdbe*,int,int,int,const char*,int); int sqliteVdbeAddOpList(Vdbe*, int nOp, VdbeOp const *aOp); void sqliteVdbeChangeP3(Vdbe*, int addr, const char *zP1, int N); void sqliteVdbeDequoteP3(Vdbe*, int addr); int sqliteVdbeMakeLabel(Vdbe*); Index: test/dbbe.test ================================================================== --- test/dbbe.test +++ test/dbbe.test @@ -21,11 +21,11 @@ # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is exercising the code in dbbe.c. # -# $Id: dbbe.test,v 1.3 2000/08/17 09:50:00 drh Exp $ +# $Id: dbbe.test,v 1.4 2000/10/16 22:06:43 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Try to open a database that does not exist. @@ -128,9 +128,9 @@ execsql {CREATE TABLE t1(x int)} db close sqlite db testdb 0444 set v [catch {execsql {INSERT INTO t1 VALUES(1)}} msg] lappend v $msg -} {1 {table t1 is readonly}} +} {7 {table t1 is readonly}} finish_test Index: test/lock.test ================================================================== --- test/lock.test +++ test/lock.test @@ -21,11 +21,11 @@ # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this script is database locks. # -# $Id: lock.test,v 1.2 2000/08/04 13:51:11 drh Exp $ +# $Id: lock.test,v 1.3 2000/10/16 22:06:43 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -57,11 +57,11 @@ do_test lock-1.2 { # Now try to update the database # set v [catch {execsql {UPDATE big SET f2='xyz' WHERE f1=11}} msg] lappend v $msg -} {1 {table big is locked}} +} {5 {table big is locked}} do_test lock-1.3 { # Try to update the database in a separate process # set f [open update.sql w] Index: www/c_interface.tcl ================================================================== --- www/c_interface.tcl +++ www/c_interface.tcl @@ -1,9 +1,9 @@ # # Run this Tcl script to generate the sqlite.html file. # -set rcsid {$Id: c_interface.tcl,v 1.10 2000/10/09 12:57:01 drh Exp $} +set rcsid {$Id: c_interface.tcl,v 1.11 2000/10/16 22:06:43 drh Exp $} puts { The C language interface to the SQLite library @@ -65,10 +65,12 @@ int *ncolumn, char **errmsg ); void sqlite_free_table(char**); + +void sqlite_interrupt(sqlite*); int sqlite_complete(const char *sql); void sqlite_busy_handler(sqlite*, int (*)(void*,const char*,int), void*); @@ -261,10 +263,14 @@

This return code indicates that an attempt was made to write to a database file that was originally opened for reading only. This can happen if the callback from a query attempts to update the table being queried.

+
SQLITE_INTERRUPT
+

This value is returned if a call to sqlite_interrupt() +interrupts a database operation in progress. +

Querying without using a callback function

@@ -315,10 +321,18 @@ to sqlite_free_table() when the table is no longer needed.

The sqlite_get_table() routine returns the same integer result code as sqlite_exec().

+

Interrupting an SQLite operation

+ +

The sqlite_interrupt() function can be called from a +different thread or from a signal handler to the current database +operation to exit at its first opportunity. When this happens, +the sqlite_exec() routine (or the equivalent) that started +the database operation will return SQLITE_INTERRUPT.

+

Testing for a complete SQL statement

The next interface routine to SQLite is a convenience function used to test whether or not a string forms a complete SQL statement. If the sqlite_complete() function returns true when its input Index: www/changes.tcl ================================================================== --- www/changes.tcl +++ www/changes.tcl @@ -15,11 +15,18 @@ proc chng {date desc} { puts "

$date
" puts "

    $desc

" } -chng {2000 Oct 11 (Not Released)} { +chng {2000 Oct 16 (1.0.11) +
  • Added the sqlite_interrupt() interface.
  • +
  • In the shell, sqlite_interrupt() is invoked when the + user presses Control-C
  • +
  • Fixed bugs in the return value of sqlite_exec().
  • +} + +chng {2000 Oct 11 (1.0.10)
  • Added notes on how to compile for Windows95/98.
  • Add Doug Lea's memory allocator to the distribution, for completeness.
  • Removed a few variables that were not being used. Etc.
  • }