/ Check-in [5afb8800]
Login

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

Overview
Comment:Get VACUUM working again - with test cases. Some other minor cleanups. (CVS 931)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 5afb88008fed253e6d1fc0ed5172370b61d3727b
User & Date: drh 2003-04-25 02:43:08
Context
2003-04-25
03:13
Explicit casts to squelch bogus warnings for vc++. Ticket #194. (CVS 932) check-in: cb808c14 user: drh tags: trunk
02:43
Get VACUUM working again - with test cases. Some other minor cleanups. (CVS 931) check-in: 5afb8800 user: drh tags: trunk
2003-04-24
01:45
Fix some issues with INSTEAD OF triggers. (CVS 930) check-in: 206b1739 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/btree.h.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
..
91
92
93
94
95
96
97

98
99
100
101
102
103
104
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the interface that the sqlite B-Tree file
** subsystem.  See comments in the source code for a detailed description
** of what each interface routine does.
**
** @(#) $Id: btree.h,v 1.31 2003/04/16 01:28:16 drh Exp $
*/
#ifndef _BTREE_H_
#define _BTREE_H_

/*
** Forward declarations of structure
*/
................................................................................
/*
** The number of 4-byte "meta" values contained on the first page of each
** database file.
*/
#define SQLITE_N_BTREE_META 10

int sqliteBtreeOpen(const char *zFilename, int mode, int nPg, Btree **ppBtree);


#define btOps(pBt) (*((BtOps **)(pBt)))
#define btCOps(pCur) (*((BtCursorOps **)(pCur)))

#define sqliteBtreeClose(pBt)              (btOps(pBt)->Close(pBt))
#define sqliteBtreeSetCacheSize(pBt, sz)   (btOps(pBt)->SetCacheSize(pBt, sz))
#define sqliteBtreeSetSafetyLevel(pBt, sl) (btOps(pBt)->SetSafetyLevel(pBt, sl))







|







 







>







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
..
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the interface that the sqlite B-Tree file
** subsystem.  See comments in the source code for a detailed description
** of what each interface routine does.
**
** @(#) $Id: btree.h,v 1.32 2003/04/25 02:43:08 drh Exp $
*/
#ifndef _BTREE_H_
#define _BTREE_H_

/*
** Forward declarations of structure
*/
................................................................................
/*
** The number of 4-byte "meta" values contained on the first page of each
** database file.
*/
#define SQLITE_N_BTREE_META 10

int sqliteBtreeOpen(const char *zFilename, int mode, int nPg, Btree **ppBtree);
int sqliteRBtreeOpen(const char *zFilename, int mode, int nPg, Btree **ppBtree);

#define btOps(pBt) (*((BtOps **)(pBt)))
#define btCOps(pCur) (*((BtCursorOps **)(pCur)))

#define sqliteBtreeClose(pBt)              (btOps(pBt)->Close(pBt))
#define sqliteBtreeSetCacheSize(pBt, sz)   (btOps(pBt)->SetCacheSize(pBt, sz))
#define sqliteBtreeSetSafetyLevel(pBt, sl) (btOps(pBt)->SetSafetyLevel(pBt, sl))

Changes to src/btree_rb.c.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
....
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
** 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: btree_rb.c,v 1.7 2003/04/24 01:45:04 drh Exp $
**
** This file implements an in-core database using Red-Black balanced
** binary trees.
**
** It was contributed to SQLite by anonymous on 2003-Feb-04 23:24:49 UTC.
*/
#include "btree.h"
................................................................................
}
#endif

/*
** Return the full pathname of the underlying database file.
*/
static const char *memBtreeGetFilename(Btree *pBt){
  return ":memory:";
}

/*
** Change the name of the underlying database file.
*/
static int memBtreeChangeFilename(Btree *pBt, const char *zNew){
  return SQLITE_OK;







|







 







|







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
....
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
** 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: btree_rb.c,v 1.8 2003/04/25 02:43:08 drh Exp $
**
** This file implements an in-core database using Red-Black balanced
** binary trees.
**
** It was contributed to SQLite by anonymous on 2003-Feb-04 23:24:49 UTC.
*/
#include "btree.h"
................................................................................
}
#endif

/*
** Return the full pathname of the underlying database file.
*/
static const char *memBtreeGetFilename(Btree *pBt){
  return 0;  /* A NULL return indicates there is no underlying file */
}

/*
** Change the name of the underlying database file.
*/
static int memBtreeChangeFilename(Btree *pBt, const char *zNew){
  return SQLITE_OK;

Changes to src/pragma.c.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
67
68
69
70
71
72
73


74
75
76
77
78
79
80
81
82
83
**    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.
**
*************************************************************************
** This file contains code used to implement the PRAGMA command.
**
** $Id: pragma.c,v 1.4 2003/04/22 20:30:39 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** Interpret the given string as a boolean value.
*/
................................................................................

/*
** Interpret the given string as a temp db location. Return 1 for file
** backed temporary databases, 2 for the Red-Black tree in memory database
** and 0 to use the compile-time default.
*/
static int getTempStore(char *z){


  if (sqliteStrICmp(z, "file") == 0) {
    return 1;
  }else if(sqliteStrICmp(z, "memory") == 0) {
    return 2;
  }else{
    return 0;
  }
}

/*







|







 







>
>
|

|







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
**    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.
**
*************************************************************************
** This file contains code used to implement the PRAGMA command.
**
** $Id: pragma.c,v 1.5 2003/04/25 02:43:08 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>

/*
** Interpret the given string as a boolean value.
*/
................................................................................

/*
** Interpret the given string as a temp db location. Return 1 for file
** backed temporary databases, 2 for the Red-Black tree in memory database
** and 0 to use the compile-time default.
*/
static int getTempStore(char *z){
  if( z[0]>='0' || z[0]<='2' ){
    return z[0] - '0';
  }else if( sqliteStrICmp(z, "file")==0 ){
    return 1;
  }else if( sqliteStrICmp(z, "memory")==0 ){
    return 2;
  }else{
    return 0;
  }
}

/*

Changes to src/vacuum.c.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
37
38
39
40
41
42
43

44
45
46
47
48
49
50
...
155
156
157
158
159
160
161


















162
163
164
165
166
167
168
...
194
195
196
197
198
199
200








201
202
203
204
205
206
207
...
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266



267
268
269
270
271
272
273
...
281
282
283
284
285
286
287






288
289
290
291
292
293
294
295
296
297
298
299
300







301
302
**
*************************************************************************
** 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.3 2003/04/18 02:31:04 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"

#define SQLITE_OMIT_VACUUM 1

/*
................................................................................
*/
typedef struct vacuumStruct vacuumStruct;
struct vacuumStruct {
  sqlite *dbOld;       /* Original database */
  sqlite *dbNew;       /* New database */
  Parse *pParse;       /* The parser context */
  const char *zTable;  /* Name of a table being copied */

  dynStr s1, s2;       /* Two dynamic strings */
};

#ifdef SQLITE_OMIT_VACUUM
/*
** Append text to a dynamic string
*/
................................................................................
    rc = sqlite_exec(p->dbOld, p->s1.z, vacuumCallback2, p, &zErrMsg);
    if( rc && p->pParse->zErrMsg==0 ){
      sqliteErrorMsg(p->pParse, "%s", zErrMsg);
    }
  }
  return rc;
}



















/*
** Generate a random name of 20 character in length.
*/
static void randomName(char *zBuf){
  static const char zChars[] =
    "abcdefghijklmnopqrstuvwxyz"
................................................................................
  sqlite *dbNew = 0;      /* The new vacuumed database */
  sqlite *db;             /* The original database */
  int rc, i;
  char *zErrMsg = 0;
  char *zSql = 0;
  int safety = 0;
  vacuumStruct sVac;









  /* Initial error checks
  */
  if( pParse->explain ){
    return;
  }
  db = pParse->db;
................................................................................
  }
  safety = 1;
  if( execsql(pParse, db, "BEGIN") ) goto end_of_vacuum;
  if( execsql(pParse, dbNew, "BEGIN") ) goto end_of_vacuum;
  sVac.dbOld = db;
  sVac.dbNew = dbNew;
  sVac.pParse = pParse;
  rc = sqlite_exec(db, "SELECT type, name, sql FROM sqlite_master "
           "WHERE sql NOT NULL", vacuumCallback1, &sVac, &zErrMsg);
  if( rc ){
    if( pParse->zErrMsg==0 ){
      sqliteErrorMsg(pParse, "unable to vacuum database - %s", zErrMsg);
    }
    goto end_of_vacuum;
  }




  if( sqliteOsFileRename(zFilename, zTemp2) ){
    sqliteErrorMsg(pParse, "unable to rename database file");
    goto end_of_vacuum;
  }
  if( sqliteOsFileRename(zTemp, zFilename) ){
    sqliteOsFileRename(zTemp2, zFilename);
................................................................................
  }
  execsql(pParse, db, "COMMIT;");  /* Nothing was written so its gotta work */
  sqlite_close(dbNew);
  dbNew = 0;
  if( sqliteOsDelete(zTemp2) ){
    sqliteErrorMsg(pParse, "unable to delete old database: %s", zTemp2);
  }







end_of_vacuum:
  sqlite_exec(db, "COMMIT", 0, 0, 0);
  if( safety) {
    sqliteSafetyOn(db);
  }
  if( dbNew ) sqlite_close(dbNew);
  sqliteOsDelete(zTemp);
  sqliteFree(zTemp);
  sqliteFree(zSql);
  sqliteFree(sVac.s1.z);
  sqliteFree(sVac.s2.z);
  if( zErrMsg ) sqlite_freemem(zErrMsg);







#endif
}







|







 







>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>







 







|
|
|
|
|
|
|
|
>
>
>







 







>
>
>
>
>
>













>
>
>
>
>
>
>


10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
...
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
...
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
...
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
...
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
340
341
342
343
344
345
**
*************************************************************************
** 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.4 2003/04/25 02:43:08 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"

#define SQLITE_OMIT_VACUUM 1

/*
................................................................................
*/
typedef struct vacuumStruct vacuumStruct;
struct vacuumStruct {
  sqlite *dbOld;       /* Original database */
  sqlite *dbNew;       /* New database */
  Parse *pParse;       /* The parser context */
  const char *zTable;  /* Name of a table being copied */
  const char *zPragma; /* Pragma to execute with results */
  dynStr s1, s2;       /* Two dynamic strings */
};

#ifdef SQLITE_OMIT_VACUUM
/*
** Append text to a dynamic string
*/
................................................................................
    rc = sqlite_exec(p->dbOld, p->s1.z, vacuumCallback2, p, &zErrMsg);
    if( rc && p->pParse->zErrMsg==0 ){
      sqliteErrorMsg(p->pParse, "%s", zErrMsg);
    }
  }
  return rc;
}

/*
** This callback is used to transfer PRAGMA settings from one database
** to the other.  The value in argv[0] should be passed to a pragma
** identified by ((vacuumStruct*)pArg)->zPragma.
*/
static int vacuumCallback3(void *pArg, int argc, char **argv, char **NotUsed){
  vacuumStruct *p = (vacuumStruct*)pArg;
  int rc = 0;
  char zBuf[200];
  assert( argc==1 );
  assert( argv[0]!=0 );
  assert( strlen(p->zPragma)<100 );
  assert( strlen(argv[0])<30 );
  sprintf(zBuf,"PRAGMA %s=%s;", p->zPragma, argv[0]);
  rc = execsql(p->pParse, p->dbNew, zBuf);
  return rc;
}

/*
** Generate a random name of 20 character in length.
*/
static void randomName(char *zBuf){
  static const char zChars[] =
    "abcdefghijklmnopqrstuvwxyz"
................................................................................
  sqlite *dbNew = 0;      /* The new vacuumed database */
  sqlite *db;             /* The original database */
  int rc, i;
  char *zErrMsg = 0;
  char *zSql = 0;
  int safety = 0;
  vacuumStruct sVac;

  /* These are all of the pragmas that need to be transferred over
  ** to the new database */
  static const char *zPragma[] = {
     "default_synchronous",
     "default_cache_size",
     /* "default_temp_store", */
  };

  /* Initial error checks
  */
  if( pParse->explain ){
    return;
  }
  db = pParse->db;
................................................................................
  }
  safety = 1;
  if( execsql(pParse, db, "BEGIN") ) goto end_of_vacuum;
  if( execsql(pParse, dbNew, "BEGIN") ) goto end_of_vacuum;
  sVac.dbOld = db;
  sVac.dbNew = dbNew;
  sVac.pParse = pParse;
  for(i=0; i<sizeof(zPragma)/sizeof(zPragma[0]); i++){
    char zBuf[200];
    assert( strlen(zPragma[i])<100 );
    sprintf(zBuf, "PRAGMA %s;", zPragma[i]);
    sVac.zPragma = zPragma[i];
    rc = sqlite_exec(db, zBuf, vacuumCallback3, &sVac, &zErrMsg);
    if( rc ) goto vacuum_error;
  }
  rc = sqlite_exec(db, "SELECT type, name, sql FROM sqlite_master "
           "WHERE sql NOT NULL", vacuumCallback1, &sVac, &zErrMsg);
  if( rc ) goto vacuum_error;

  if( sqliteOsFileRename(zFilename, zTemp2) ){
    sqliteErrorMsg(pParse, "unable to rename database file");
    goto end_of_vacuum;
  }
  if( sqliteOsFileRename(zTemp, zFilename) ){
    sqliteOsFileRename(zTemp2, zFilename);
................................................................................
  }
  execsql(pParse, db, "COMMIT;");  /* Nothing was written so its gotta work */
  sqlite_close(dbNew);
  dbNew = 0;
  if( sqliteOsDelete(zTemp2) ){
    sqliteErrorMsg(pParse, "unable to delete old database: %s", zTemp2);
  }
  sqliteBtreeClose(db->aDb[0].pBt);
  zTemp2[nFilename] = 0;
  if( sqliteBtreeOpen(zTemp2, 0, MAX_PAGES, &db->aDb[0].pBt) ){
     sqliteErrorMsg(pParse, "unable to reopen database after vacuuming");
  }
  sqliteResetInternalSchema(db, 0);

end_of_vacuum:
  sqlite_exec(db, "COMMIT", 0, 0, 0);
  if( safety) {
    sqliteSafetyOn(db);
  }
  if( dbNew ) sqlite_close(dbNew);
  sqliteOsDelete(zTemp);
  sqliteFree(zTemp);
  sqliteFree(zSql);
  sqliteFree(sVac.s1.z);
  sqliteFree(sVac.s2.z);
  if( zErrMsg ) sqlite_freemem(zErrMsg);
  return;

vacuum_error:
  if( pParse->zErrMsg==0 ){
    sqliteErrorMsg(pParse, "unable to vacuum database - %s", zErrMsg);
  }
  goto end_of_vacuum;
#endif
}

Changes to test/vacuum.test.

7
8
9
10
11
12
13
14
15
16
17
18
19
20


21











































































22
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this file is testing the VACUUM statement.
#
# $Id: vacuum.test,v 1.7 2002/03/03 02:49:52 drh Exp $

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

# The vacuum command no longer functions.  There is
# nothing to test.














































































# finish_test







|




|
|
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this file is testing the VACUUM statement.
#
# $Id: vacuum.test,v 1.8 2003/04/25 02:43:08 drh Exp $

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

proc cksum {filename} {
  set txt [db eval {SELECT name, type, sql FROM sqlite_master}]\n
  foreach tbl [db eval {SELECT name FROM sqlite_master WHERE type='table'}] {
    append txt [db eval "SELECT * FROM $tbl"]\n
  }
  foreach prag {default_synchronous default_cache_size} {
    append txt $prag-[db eval "PRAGMA $prag"]\n
  }
  # set fd [open $filename w]
  # puts $fd $txt
  # close $fd
  return [string length $txt]-[md5 $txt]
}

do_test vacuum-1.1 {
  execsql {
    BEGIN;
    CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
    INSERT INTO t1 VALUES(NULL,randstr(10,100),randstr(5,50));
    INSERT INTO t1 VALUES(123456,randstr(10,100),randstr(5,50));
    INSERT INTO t1 SELECT NULL, b||'-'||rowid, c||'-'||rowid FROM t1;
    INSERT INTO t1 SELECT NULL, b||'-'||rowid, c||'-'||rowid FROM t1;
    INSERT INTO t1 SELECT NULL, b||'-'||rowid, c||'-'||rowid FROM t1;
    INSERT INTO t1 SELECT NULL, b||'-'||rowid, c||'-'||rowid FROM t1;
    INSERT INTO t1 SELECT NULL, b||'-'||rowid, c||'-'||rowid FROM t1;
    INSERT INTO t1 SELECT NULL, b||'-'||rowid, c||'-'||rowid FROM t1;
    INSERT INTO t1 SELECT NULL, b||'-'||rowid, c||'-'||rowid FROM t1;
    CREATE INDEX i1 ON t1(b,c);
    CREATE TABLE t2 AS SELECT * FROM t1;
    COMMIT;
    DROP TABLE t2;
  }
  set ::size1 [file size test.db]
  set ::cksum [cksum vacuum1.txt]
  expr {$::cksum!=""}
} {1}
do_test vacuum-1.2 {
  execsql {
    VACUUM;
  }
  cksum vacuum2.txt
} $cksum
do_test vacuum-1.3 {
  expr {[file size test.db]<$::size1}
} {1}
do_test vacuum-1.4 {
  execsql {
    BEGIN;
    CREATE TABLE t2 AS SELECT * FROM t1;
    CREATE TABLE t3 AS SELECT * FROM t1;
    CREATE VIEW v1 AS SELECT b, c FROM t3;
    CREATE TRIGGER r1 AFTER DELETE ON t2 BEGIN
      SELECT 1;
    END;
    COMMIT;
    DROP TABLE t2;
  }
  set ::size1 [file size test.db]
  set ::cksum [cksum vacuum3.txt]
  expr {$::cksum!=""}
} {1}
do_test vacuum-1.5 {
  execsql {
    VACUUM;
  }
  cksum vacuum4.txt
} $cksum
do_test vacuum-1.6 {
  expr {[file size test.db]<$::size1}
} {1}

do_test vacuum-2.1 {
  catchsql {
    BEGIN;
    VACUUM;
    COMMIT;
  }
} {1 {cannot VACUUM from within a transaction}}
execsql COMMIT

# finish_test