Index: src/attach.c ================================================================== --- src/attach.c +++ src/attach.c @@ -9,11 +9,11 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains code used to implement the ATTACH and DETACH commands. ** -** $Id: attach.c,v 1.20 2004/06/29 08:59:35 danielk1977 Exp $ +** $Id: attach.c,v 1.21 2004/06/30 09:49:23 danielk1977 Exp $ */ #include "sqliteInt.h" /* ** This routine is called by the parser to process an ATTACH statement: @@ -29,10 +29,11 @@ char *zFile, *zName; sqlite *db; Vdbe *v; v = sqlite3GetVdbe(pParse); + if( !v ) return; sqlite3VdbeAddOp(v, OP_Halt, 0, 0); if( pParse->explain ) return; db = pParse->db; if( db->nDb>=MAX_ATTACHED+2 ){ sqlite3ErrorMsg(pParse, "too many attached databases - max %d", @@ -138,10 +139,11 @@ sqlite *db; Vdbe *v; Db *pDb = 0; v = sqlite3GetVdbe(pParse); + if( !v ) return; sqlite3VdbeAddOp(v, OP_Halt, 0, 0); if( pParse->explain ) return; db = pParse->db; for(i=0; inDb; i++){ pDb = &db->aDb[i]; Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -12,11 +12,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.242 2004/06/29 13:18:24 danielk1977 Exp $ +** $Id: main.c,v 1.243 2004/06/30 09:49:24 danielk1977 Exp $ */ #include "sqliteInt.h" #include "os.h" #include @@ -960,10 +960,14 @@ const char** pzTail /* OUT: End of parsed string */ ){ Parse sParse; char *zErrMsg = 0; int rc = SQLITE_OK; + + if( sqlite3_malloc_failed ){ + return SQLITE_NOMEM; + } assert( ppStmt ); *ppStmt = 0; if( sqlite3SafetyOn(db) ){ return SQLITE_MISUSE; Index: src/pager.c ================================================================== --- src/pager.c +++ src/pager.c @@ -16,11 +16,11 @@ ** is separate from the database file. The pager also implements file ** locking to prevent two processes from writing the same database ** file simultaneously, or one process from reading the database while ** another is writing. ** -** @(#) $Id: pager.c,v 1.147 2004/06/28 04:52:30 danielk1977 Exp $ +** @(#) $Id: pager.c,v 1.148 2004/06/30 09:49:24 danielk1977 Exp $ */ #include "os.h" /* Must be first to enable large file support */ #include "sqliteInt.h" #include "pager.h" #include @@ -2315,11 +2315,11 @@ if( rc!=SQLITE_OK ){ sqliteFree(pPager->aInJournal); pPager->aInJournal = 0; sqlite3OsUnlock(&pPager->fd, SHARED_LOCK); pPager->state = PAGER_SHARED; - return SQLITE_CANTOPEN; + return rc; } sqlite3OsOpenDirectory(pPager->zDirectory, &pPager->jfd); pPager->journalOpen = 1; pPager->journalStarted = 0; pPager->needSync = 0; Index: src/parse.y ================================================================== --- src/parse.y +++ src/parse.y @@ -12,11 +12,11 @@ ** This file contains SQLite's grammar for SQL. Process this file ** using the lemon parser generator to generate C code that runs ** the parser. Lemon will also generate a header file containing ** numeric codes for all of the tokens. ** -** @(#) $Id: parse.y,v 1.128 2004/06/26 06:37:07 danielk1977 Exp $ +** @(#) $Id: parse.y,v 1.129 2004/06/30 09:49:24 danielk1977 Exp $ */ %token_prefix TK_ %token_type {Token} %default_type {Token} %extra_argument {Parse *pParse} @@ -440,11 +440,11 @@ A = sqlite3ExprListAppend(X,Y,C.n>0?&C:0); if( A ) A->a[A->nExpr-1].sortOrder = Z; } sortlist(A) ::= sortitem(Y) collate(C) sortorder(Z). { A = sqlite3ExprListAppend(0,Y,C.n>0?&C:0); - if( A ) A->a[0].sortOrder = Z; + if( A && A->a ) A->a[0].sortOrder = Z; } sortitem(A) ::= expr(X). {A = X;} %type sortorder {int} %type collate {Token} Index: src/pragma.c ================================================================== --- src/pragma.c +++ src/pragma.c @@ -9,11 +9,11 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains code used to implement the PRAGMA command. ** -** $Id: pragma.c,v 1.56 2004/06/29 08:59:35 danielk1977 Exp $ +** $Id: pragma.c,v 1.57 2004/06/30 09:49:24 danielk1977 Exp $ */ #include "sqliteInt.h" #include #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) @@ -146,10 +146,11 @@ ** index of the database this pragma is being applied to in db.aDb[]. */ iDb = sqlite3TwoPartName(pParse, pId1, pId2, &pId); if( iDb<0 ) return; zLeft = sqlite3NameFromToken(pId); + if( !zLeft ) return; if( minusFlag ){ zRight = 0; sqlite3SetNString(&zRight, "-", 1, pValue->z, pValue->n, 0); }else{ zRight = sqlite3NameFromToken(pValue); Index: src/vacuum.c ================================================================== --- src/vacuum.c +++ src/vacuum.c @@ -12,11 +12,11 @@ ** This file contains code used to implement the VACUUM command. ** ** Most of the code in this file may be omitted by defining the ** SQLITE_OMIT_VACUUM macro. ** -** $Id: vacuum.c,v 1.25 2004/06/30 06:30:26 danielk1977 Exp $ +** $Id: vacuum.c,v 1.26 2004/06/30 09:49:24 danielk1977 Exp $ */ #include "sqliteInt.h" #include "os.h" #if !defined(SQLITE_OMIT_VACUUM) || SQLITE_OMIT_VACUUM @@ -80,11 +80,13 @@ ** with 2.0.0, SQLite no longer uses GDBM so this command has ** become a no-op. */ void sqlite3Vacuum(Parse *pParse, Token *pTableName){ Vdbe *v = sqlite3GetVdbe(pParse); - sqlite3VdbeAddOp(v, OP_Vacuum, 0, 0); + if( v ){ + sqlite3VdbeAddOp(v, OP_Vacuum, 0, 0); + } return; } /* ** This routine implements the OP_Vacuum opcode of the VDBE. @@ -108,26 +110,30 @@ /* Get the full pathname of the database file and create a ** temporary filename in the same directory as the original file. */ zFilename = sqlite3BtreeGetFilename(db->aDb[0].pBt); - if( zFilename==0 ){ - /* The in-memory database. Do nothing. */ - goto end_of_vacuum; + assert( zFilename ); + if( zFilename[0]=='\0' ){ + /* The in-memory database. Do nothing. Return directly to avoid causing + ** an error trying to DETACH the vacuum_db (which never got attached) + ** in the exit-handler. + */ + return SQLITE_OK; } nFilename = strlen(zFilename); zTemp = sqliteMalloc( nFilename+100 ); if( zTemp==0 ){ rc = SQLITE_NOMEM; goto end_of_vacuum; } strcpy(zTemp, zFilename); - for(i=0; i<10; i++){ + i = 0; + do { zTemp[nFilename] = '-'; randomName((unsigned char*)&zTemp[nFilename+1]); - if( !sqlite3OsFileExists(zTemp) ) break; - } + } while( i<10 && sqlite3OsFileExists(zTemp) ); /* Attach the temporary database as 'vacuum_db'. The synchronous pragma ** can be set to 'off' for this file, as it is not recovered if a crash ** occurs anyway. The integrity of the database is maintained by a ** (possibly synchronous) transaction opened on the main database before @@ -134,19 +140,19 @@ ** sqlite3BtreeCopyFile() is called. ** ** An optimisation would be to use a non-journaled pager. */ zSql = sqlite3MPrintf("ATTACH '%s' AS vacuum_db;", zTemp); - execSql(db, "PRAGMA vacuum_db.synchronous = off;"); if( !zSql ){ rc = SQLITE_NOMEM; goto end_of_vacuum; } rc = execSql(db, zSql); sqliteFree(zSql); zSql = 0; if( rc!=SQLITE_OK ) goto end_of_vacuum; + execSql(db, "PRAGMA vacuum_db.synchronous = off;"); /* Begin a transaction */ rc = execSql(db, "BEGIN;"); if( rc!=SQLITE_OK ) goto end_of_vacuum; @@ -229,14 +235,18 @@ ** by manually setting the autoCommit flag to true and detaching the ** vacuum database. The vacuum_db journal file is deleted when the pager ** is closed by the DETACH. */ db->autoCommit = 1; - execSql(db, "DETACH vacuum_db;"); + if( rc==SQLITE_OK ){ + rc = execSql(db, "DETACH vacuum_db;"); + }else{ + execSql(db, "DETACH vacuum_db;"); + } if( zTemp ){ sqlite3OsDelete(zTemp); sqliteFree(zTemp); } if( zSql ) sqliteFree( zSql ); #endif return rc; } Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -41,11 +41,11 @@ ** documentation, headers files, or other derived files. The formatting ** of the code in this file is, therefore, important. See other comments ** 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.397 2004/06/30 06:30:26 danielk1977 Exp $ +** $Id: vdbe.c,v 1.398 2004/06/30 09:49:24 danielk1977 Exp $ */ #include "sqliteInt.h" #include "os.h" #include #include "vdbeInt.h" @@ -1246,10 +1246,11 @@ ctx.pColl = (CollSeq *)pOp[-1].p3; } if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; (*ctx.pFunc->xFunc)(&ctx, n, apVal); if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse; + if( sqlite3_malloc_failed ) goto no_mem; popStack(&pTos, n); /* If any auxilary data functions have been called by this user function, ** immediately call the destructor for any non-static values. */ Index: test/malloc.test ================================================================== --- test/malloc.test +++ test/malloc.test @@ -12,11 +12,11 @@ # When compiled with -DMEMORY_DEBUG=1, the SQLite library accepts a special # command (sqlite_malloc_fail N) which causes the N-th malloc to fail. This # special feature is used to see what happens in the library if a malloc # were to really fail due to an out-of-memory situation. # -# $Id: malloc.test,v 1.9 2004/06/26 13:51:34 danielk1977 Exp $ +# $Id: malloc.test,v 1.10 2004/06/30 09:49:24 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Only run these tests if memory debugging is turned on. @@ -257,11 +257,58 @@ } {1 1} } # Ensure that no file descriptors were leaked. do_test malloc-5.X { + catch {db close} + set sqlite_open_file_count +} {0} + +for {set go 1; set i 1} {$go} {incr i} { + do_test malloc-6.$i { + sqlite_malloc_fail 0 + catch {db close} + catch {file delete -force test.db} + catch {file delete -force test.db-journal} + sqlite3 db test.db + execsql { + BEGIN TRANSACTION; + CREATE TABLE t1(a); + INSERT INTO t1 VALUES(1); + INSERT INTO t1 SELECT a*2 FROM t1; + INSERT INTO t1 SELECT a*2 FROM t1; + INSERT INTO t1 SELECT a*2 FROM t1; + INSERT INTO t1 SELECT a*2 FROM t1; + INSERT INTO t1 SELECT a*2 FROM t1; + INSERT INTO t1 SELECT a*2 FROM t1; + INSERT INTO t1 SELECT a*2 FROM t1; + INSERT INTO t1 SELECT a*2 FROM t1; + INSERT INTO t1 SELECT a*2 FROM t1; + INSERT INTO t1 SELECT a*2 FROM t1; + DELETE FROM t1 where rowid%5 = 0; + COMMIT; + } + sqlite_malloc_fail $i + set v [catch {execsql { + VACUUM; + }} msg] + set leftover [lindex [sqlite_malloc_stat] 2] + if {$leftover>0} { + if {$leftover>1} {puts "\nLeftover: $leftover\nReturn=$v Message=$msg"} + set ::go 0 + set v {1 1} + } else { + set v2 [expr {$msg=="" || $msg=="out of memory"}] + if {!$v2} {puts "\nError message returned: $msg"} + lappend v $v2 + } + } {1 1} +} + +# Ensure that no file descriptors were leaked. +do_test malloc-6.X { catch {db close} set sqlite_open_file_count } {0} sqlite_malloc_fail 0 finish_test Index: test/vacuum.test ================================================================== --- test/vacuum.test +++ test/vacuum.test @@ -9,11 +9,11 @@ # #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is testing the VACUUM statement. # -# $Id: vacuum.test,v 1.21 2004/06/19 00:16:32 drh Exp $ +# $Id: vacuum.test,v 1.22 2004/06/30 09:49:24 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl proc cksum {{db db}} { @@ -197,7 +197,16 @@ do_test vacuum-6.4 { execsql { select count(*) from "abc abc" WHERE a = X'00112233'; } } {1} + +# Check what happens when an in-memory database is vacuumed. +do_test vacuum-7.0 { + sqlite3 db2 :memory: + execsql { + CREATE TABLE t1(t); + VACUUM; + } db2 +} {} # finish_test