SQLite

Check-in [6824a78bc7]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Allow VACUUM to detach the auxillary database after malloc() fails. (CVS 2804)
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 6824a78bc7b8582fc5c3a6ab05dd3ed996fc99b3
User & Date: danielk1977 2005-12-06 17:48:32.000
Context
2005-12-07
06:27
Add some tests for malloc() failure within the column_name() and column_decl() APIs. (CVS 2805) (check-in: 78f10ca0a6 user: danielk1977 tags: trunk)
2005-12-06
17:48
Allow VACUUM to detach the auxillary database after malloc() fails. (CVS 2804) (check-in: 6824a78bc7 user: danielk1977 tags: trunk)
17:19
Modify ATTACH and DETACH to execute at runtime instead of compile time. (CVS 2803) (check-in: 5e04ec694a user: danielk1977 tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/vacuum.c.
10
11
12
13
14
15
16
17
18
19

20
21
22
23
24
25
26
**
*************************************************************************
** 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.50 2005/12/06 12:53:01 danielk1977 Exp $
*/
#include "sqliteInt.h"

#include "os.h"

#ifndef SQLITE_OMIT_VACUUM
/*
** Generate a random name of 20 character in length.
*/
static void randomName(unsigned char *zBuf){







|


>







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
**
*************************************************************************
** 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.51 2005/12/06 17:48:32 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "vdbeInt.h"
#include "os.h"

#ifndef SQLITE_OMIT_VACUUM
/*
** Generate a random name of 20 character in length.
*/
static void randomName(unsigned char *zBuf){
98
99
100
101
102
103
104

105
106
107
108
109
110
111
  int nFilename;          /* number of characters  in zFilename[] */
  char *zTemp = 0;        /* a temporary file in same directory as zFilename */
  Btree *pMain;           /* The database being vacuumed */
  Btree *pTemp;
  char *zSql = 0;
  int rc2;  
  int saved_flags;       /* Saved value of the db->flags */


  /* Save the current value of the write-schema flag before setting it. */
  saved_flags = db->flags;
  db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks;

  if( !db->autoCommit ){
    sqlite3SetString(pzErrMsg, "cannot VACUUM from within a transaction", 







>







99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
  int nFilename;          /* number of characters  in zFilename[] */
  char *zTemp = 0;        /* a temporary file in same directory as zFilename */
  Btree *pMain;           /* The database being vacuumed */
  Btree *pTemp;
  char *zSql = 0;
  int rc2;  
  int saved_flags;       /* Saved value of the db->flags */
  sqlite3_stmt *pDetach = 0;

  /* Save the current value of the write-schema flag before setting it. */
  saved_flags = db->flags;
  db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks;

  if( !db->autoCommit ){
    sqlite3SetString(pzErrMsg, "cannot VACUUM from within a transaction", 
143
144
145
146
147
148
149









150
151
152
153
154
155
156
  ** enough that this loop will always terminate (and terminate quickly)
  ** that we don't even bother to set a maximum loop count.
  */
  do {
    zTemp[nFilename] = '-';
    randomName((unsigned char*)&zTemp[nFilename+1]);
  } while( sqlite3Os.xFileExists(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
  ** sqlite3BtreeCopyFile() is called.
  **







>
>
>
>
>
>
>
>
>







145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
  ** enough that this loop will always terminate (and terminate quickly)
  ** that we don't even bother to set a maximum loop count.
  */
  do {
    zTemp[nFilename] = '-';
    randomName((unsigned char*)&zTemp[nFilename+1]);
  } while( sqlite3Os.xFileExists(zTemp) );

  /* Before we even attach it, compile a DETACH statement for vacuum_db. This
  ** way, if malloc() fails we can detach the database without needing to
  ** dynamically allocate memory.
  */ 
  rc = sqlite3_prepare(db, "DETACH vacuum_db", -1, &pDetach, 0);
  if( rc!=SQLITE_OK ){
    goto end_of_vacuum;
  }

  /* 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
  ** sqlite3BtreeCopyFile() is called.
  **
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321









322
323
324
325
326
327
328
329
330
331
    if( rc!=SQLITE_OK ) goto end_of_vacuum;
    rc = sqlite3BtreeCommit(pTemp);
    if( rc!=SQLITE_OK ) goto end_of_vacuum;
    rc = sqlite3BtreeCommit(pMain);
  }

end_of_vacuum:
  /* If one of the execSql() calls above returned SQLITE_NOMEM, then the
  ** mallocFailed flag will be clear (because execSql() calls sqlite3_exec()).
  ** Fix this so the flag and return code match.
  */
  if( rc==SQLITE_NOMEM ){
    sqlite3Tsd()->mallocFailed = 1;
  }

  /* Restore the original value of db->flags */
  db->flags = saved_flags;

  /* Currently there is an SQL level transaction open on the vacuum
  ** database. No locks are held on any other files (since the main file
  ** was committed at the btree level). So it safe to end the transaction
  ** 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;

  /* TODO: We need to detach vacuum_db (if it is attached) even if malloc()
  ** failed. Right now trying to do so will cause an assert() to fail in **
  **_prepare() (because it should be impossible to call _prepare() with the **
  ** mallocFailed flag set). So really, we need to be able to detach a database
  ** without calling malloc(). Which seems plausible.
  */
  if( !sqlite3Tsd()->mallocFailed ){
    rc2 = execSql(db, "DETACH vacuum_db;");
    if( rc==SQLITE_OK ){
      rc = rc2;
    }
  }









  if( zTemp ){
    sqlite3Os.xDelete(zTemp);
    sqliteFree(zTemp);
  }
  sqliteFree( zSql );
  sqlite3ResetInternalSchema(db, 0);
#endif

  return rc;
}







<
<
<
<
<
<
<
<












|
|
<
<
<
<
|
|




>
>
>
>
>
>
>
>
>










294
295
296
297
298
299
300








301
302
303
304
305
306
307
308
309
310
311
312
313
314




315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
    if( rc!=SQLITE_OK ) goto end_of_vacuum;
    rc = sqlite3BtreeCommit(pTemp);
    if( rc!=SQLITE_OK ) goto end_of_vacuum;
    rc = sqlite3BtreeCommit(pMain);
  }

end_of_vacuum:








  /* Restore the original value of db->flags */
  db->flags = saved_flags;

  /* Currently there is an SQL level transaction open on the vacuum
  ** database. No locks are held on any other files (since the main file
  ** was committed at the btree level). So it safe to end the transaction
  ** 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;

  if( pDetach ){
    ((Vdbe *)pDetach)->expired = 0;




    sqlite3_step(pDetach);
    rc2 = sqlite3_finalize(pDetach);
    if( rc==SQLITE_OK ){
      rc = rc2;
    }
  }

  /* If one of the execSql() calls above returned SQLITE_NOMEM, then the
  ** mallocFailed flag will be clear (because execSql() calls sqlite3_exec()).
  ** Fix this so the flag and return code match.
  */
  if( rc==SQLITE_NOMEM ){
    sqlite3Tsd()->mallocFailed = 1;
  }

  if( zTemp ){
    sqlite3Os.xDelete(zTemp);
    sqliteFree(zTemp);
  }
  sqliteFree( zSql );
  sqlite3ResetInternalSchema(db, 0);
#endif

  return rc;
}
Changes to test/malloc3.test.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 2005 November 30
#
# The author disclaims copyright to this source code.  In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
#
# $Id: malloc3.test,v 1.1 2005/12/06 12:57:59 danielk1977 Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Only run these tests if memory debugging is turned on.
if {[info command sqlite_malloc_stat]==""} {
   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."











|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 2005 November 30
#
# The author disclaims copyright to this source code.  In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
#
# $Id: malloc3.test,v 1.2 2005/12/06 17:48:32 danielk1977 Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Only run these tests if memory debugging is turned on.
if {[info command sqlite_malloc_stat]==""} {
   puts "Skipping malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
    }
  } {a b c a b c 1 2 3 1 2 3}
}

# Test a simple multi-file transaction
#
file delete -force test2.db
PREP {ATTACH 'test2.db' AS aux;}
SQL {BEGIN}
SQL {CREATE TABLE aux.tbl2(x, y, z)}
SQL {INSERT INTO tbl2 VALUES(1, 2, 3)}
SQL {INSERT INTO def VALUES(4, 5, 6)}
TEST 30 {
  do_test $testid {
    execsql {







|







452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
    }
  } {a b c a b c 1 2 3 1 2 3}
}

# Test a simple multi-file transaction
#
file delete -force test2.db
SQL {ATTACH 'test2.db' AS aux;}
SQL {BEGIN}
SQL {CREATE TABLE aux.tbl2(x, y, z)}
SQL {INSERT INTO tbl2 VALUES(1, 2, 3)}
SQL {INSERT INTO def VALUES(4, 5, 6)}
TEST 30 {
  do_test $testid {
    execsql {