]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Make VACUUM work even if multiple processes have the database open at once. (CVS...
authordrh <drh@noemail.net>
Fri, 25 Apr 2003 13:22:51 +0000 (13:22 +0000)
committerdrh <drh@noemail.net>
Fri, 25 Apr 2003 13:22:51 +0000 (13:22 +0000)
FossilOrigin-Name: caa960289f3d1f5e8f35a94e9e4321996c211ed2

manifest
manifest.uuid
src/btree.c
src/btree.h
src/btree_rb.c
src/os.c
src/pager.c
src/pager.h
src/vacuum.c
test/vacuum.test

index e0674fc2d1e5c4a5b313d005e4ecc9a22191e36b..b7cddccf0b731b08b731c830b4be5ab4f3640900 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Explicit\scasts\sto\ssquelch\sbogus\swarnings\sfor\svc++.\s\sTicket\s#194.\s(CVS\s932)
-D 2003-04-25T03:13:25
+C Make\sVACUUM\swork\seven\sif\smultiple\sprocesses\shave\sthe\sdatabase\sopen\sat\sonce.\s(CVS\s933)
+D 2003-04-25T13:22:52
 F Makefile.in 004acec253ecdde985c8ecd5b7c9accdb210378f
 F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
@@ -21,9 +21,9 @@ F sqlite.1 83f4a9d37bdf2b7ef079a82d54eaf2e3509ee6ea
 F sqlite.pc.in 30552343140c53304c2a658c080fbe810cd09ca2
 F src/attach.c 7ebc7487de43e357a64226f8abef81f2669f2183
 F src/auth.c a4afd27964fb9f661147115790c8ae2ee230ebcc
-F src/btree.c 3de765f186a5d07d8764f970ecd07d83ccfdc51d
-F src/btree.h dc899dd3a10ec9a0c9b51308610d2f53e36b4820
-F src/btree_rb.c b427e6f2df7807cd338636259efadb4e43dba669
+F src/btree.c a9e8aa96c2af6a81e442719d0cbc1a71bb9b3e08
+F src/btree.h 6488448db856bb92729b1325b932864c9aa163f5
+F src/btree_rb.c 8e00e40be264716e1929987878672e55d9e0e76d
 F src/build.c d5a26baeffa5bc49b4b7009a7723c6ab7e1b02d9
 F src/copy.c 44b13fd4d2444fb53bff8a5ecee1c5f6f86a8263
 F src/delete.c 23d33fd8967c6cc492943bbecea93be6491edc6a
@@ -35,10 +35,10 @@ F src/hash.h cd0433998bc1a3759d244e1637fe5a3c13b53bf8
 F src/insert.c 19882be1edc4b1629b8f3097e2615164f2c9cecb
 F src/main.c 5e4d4d081d82840a743c57269ca3c32640cefc06
 F src/md5.c fe4f9c9c6f71dfc26af8da63e4d04489b1430565
-F src/os.c 7274951ed6894f383cb889342267ded07caf339b
+F src/os.c e56853eaea5dab258ab1ccb77b4743b453516e3a
 F src/os.h 9e5bbddff123187295e3d00d49af06192cd1cd49
-F src/pager.c df4c81350cbd80c1ab48341ae0768ba78d99ad49
-F src/pager.h e3702f7d384921f6cd5ce0b3ed589185433e9f6c
+F src/pager.c 18066f5057500dccc2948c258ea5ffd4139bbf7d
+F src/pager.h 5da62c83443f26b1792cfd72c96c422f91aadd31
 F src/parse.y 15ae47e7dd84304c1c6ae9205558405127977541
 F src/pragma.c 118fe400d71b7fdcc03580d5eab6bb5aa00772a5
 F src/printf.c fc5fdef6e92ad205005263661fe9716f55a49f3e
@@ -58,7 +58,7 @@ F src/tokenize.c 067d1a477a94af7712ca74e09aaa6bd0f7299527
 F src/trigger.c e763f4015c96e06b694184ead5754985c1dfdae0
 F src/update.c b7fa7c427b74aee6db56ecfa09e5e151e6f9fa6a
 F src/util.c 87635cfdfffa056a8d3147719357aa442374f78c
-F src/vacuum.c 67199f3d626aed21940b3b428c25979a98fda03d
+F src/vacuum.c 44420de0f02cc66a673469fee1f33b6d08bb717e
 F src/vdbe.c f0868ac926d98395d28c2a29119364ff11b77852
 F src/vdbe.h 985c24f312d10f9ef8f9a8b8ea62fcdf68e82f21
 F src/where.c f632cd30f013163484a4d60c249d36fe31f5be12
@@ -125,7 +125,7 @@ F test/trigger3.test 870afef7997a5b86bf3ea893ce0c2e85d6356c72
 F test/trigger4.test 9a5c1406344d743020c2753ae8d6dfe6eb75f818
 F test/unique.test 22a46df72a3e0a3fd1a2d39e96fb59f18448dd5f
 F test/update.test 198360dfa14e65354dbcc66d5b98d8070780e42b
-F test/vacuum.test baf8e0c44587322da5669996ffc55daf8cc60266
+F test/vacuum.test 9262504c37e9d796e3d8d6c1730e878c8fa8787a
 F test/version.test 605fd0d7e7d571370c32b12dbf395b58953de246
 F test/view.test d356f445d481c04ffa6036a4c61cb8ba70289f69
 F test/where.test d719129a052280fe245a2ddcbd09bcc0b8c17ce4
@@ -165,7 +165,7 @@ F www/speed.tcl cb4c10a722614aea76d2c51f32ee43400d5951be
 F www/sqlite.tcl ae3dcfb077e53833b59d4fcc94d8a12c50a44098
 F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
-P 5afb88008fed253e6d1fc0ed5172370b61d3727b
-R f2421cf3a297d027e70ffbccdb3ba489
+P cb808c14bc14e7bd1cfff134ae5206ace451f2df
+R 304a2370652d1a06037c5b9e50c2e06c
 U drh
-Z 38db8e55b84708279a57f38314d421c3
+Z a3c697bfebd34cfeb9979168f63ba185
index 2ff41377e9ad6339e71e86c8db1dfc7f57d243df..fa0eaace1c8fb91cb56ab748a2078f48ec153f43 100644 (file)
@@ -1 +1 @@
-cb808c14bc14e7bd1cfff134ae5206ace451f2df
\ No newline at end of file
+caa960289f3d1f5e8f35a94e9e4321996c211ed2
\ No newline at end of file
index 2ec0ed7002a0024e64038c60faf3de4d13c7559e..46a27e7c2ab59c24bce5268c65023f0974d19846 100644 (file)
@@ -9,7 +9,7 @@
 **    May you share freely, never taking more than you give.
 **
 *************************************************************************
-** $Id: btree.c,v 1.90 2003/04/25 03:13:25 drh Exp $
+** $Id: btree.c,v 1.91 2003/04/25 13:22:52 drh Exp $
 **
 ** This file implements a external (disk-based) database using BTrees.
 ** For a detailed discussion of BTrees, refer to
@@ -3490,10 +3490,34 @@ static const char *fileBtreeGetFilename(Btree *pBt){
 }
 
 /*
-** Change the name of the underlying database file.
+** Copy the complete content of pBtFrom into pBtTo.  A transaction
+** must be active for both files.
+**
+** The size of file pBtFrom may be reduced by this operation.
+** If anything goes wrong, the transaction on pBtFrom is rolled back.
 */
-static int fileBtreeChangeFilename(Btree *pBt, const char *zNew){
-  return sqlitepager_rename(pBt->pPager, zNew);
+static int fileBtreeCopyFile(Btree *pBtTo, Btree *pBtFrom){
+  int rc = SQLITE_OK;
+  Pgno i, nPage;
+
+  if( !pBtTo->inTrans || !pBtFrom->inTrans ) return SQLITE_ERROR;
+  if( pBtTo->needSwab!=pBtFrom->needSwab ) return SQLITE_ERROR;
+  if( pBtTo->pCursor ) return SQLITE_BUSY;
+  memcpy(pBtTo->page1, pBtFrom->page1, SQLITE_PAGE_SIZE);
+  sqlitepager_overwrite(pBtTo->pPager, 1, pBtFrom->page1);
+  nPage = sqlitepager_pagecount(pBtFrom->pPager);
+  for(i=2; i<=nPage; i++){
+    void *pPage;
+    rc = sqlitepager_get(pBtFrom->pPager, i, &pPage);
+    if( rc ) break;
+    sqlitepager_overwrite(pBtTo->pPager, i, pPage);
+    sqlitepager_unref(pPage);
+  }
+  if( !rc ) rc = sqlitepager_truncate(pBtTo->pPager, nPage);
+  if( rc ){
+    fileBtreeRollback(pBtTo);
+  }
+  return rc;  
 }
 
 /*
@@ -3521,7 +3545,7 @@ static BtOps sqliteBtreeOps = {
     fileBtreeUpdateMeta,
     fileBtreeIntegrityCheck,
     fileBtreeGetFilename,
-    fileBtreeChangeFilename,
+    fileBtreeCopyFile,
 #ifdef SQLITE_TEST
     fileBtreePageDump,
     fileBtreePager
index 46e42fa636ec00c16c525b466d0531964b99a1a5..0052e65c2a8cd8b736490ca0df8d8b80fea8e532 100644 (file)
@@ -13,7 +13,7 @@
 ** 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 $
+** @(#) $Id: btree.h,v 1.33 2003/04/25 13:22:53 drh Exp $
 */
 #ifndef _BTREE_H_
 #define _BTREE_H_
@@ -56,7 +56,7 @@ struct BtOps {
     int (*UpdateMeta)(Btree*, int*);
     char *(*IntegrityCheck)(Btree*, int*, int);
     const char *(*GetFilename)(Btree*);
-    int (*ChangeFilename)(Btree*, const char *zNew);
+    int (*CopyFile)(Btree*,Btree*);
 #ifdef SQLITE_TEST
     int (*PageDump)(Btree*, int, int);
     struct Pager *(*Pager)(Btree*);
@@ -141,8 +141,7 @@ int sqliteRBtreeOpen(const char *zFilename, int mode, int nPg, Btree **ppBtree);
 #define sqliteBtreeIntegrityCheck(pBt, aRoot, nRoot)\
                 (btOps(pBt)->IntegrityCheck(pBt, aRoot, nRoot))
 #define sqliteBtreeGetFilename(pBt)       (btOps(pBt)->GetFilename(pBt))
-#define sqliteBtreeChangeFilename(pBt, zNew)\
-                (btOps(pBt)->ChangeFilename(pBt, zNew))
+#define sqliteBtreeCopyFile(pBt1, pBt2)   (btOps(pBt1)->CopyFile(pBt1, pBt2))
 
 #ifdef SQLITE_TEST
 #define sqliteBtreePageDump(pBt, pgno, recursive)\
index 0167a1f9d2909e783a6d77d846b2a0457a514dd6..f072332c5778113c52419056dee705ee20fad2c3 100644 (file)
@@ -9,7 +9,7 @@
 **    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 $
+** $Id: btree_rb.c,v 1.9 2003/04/25 13:22:53 drh Exp $
 **
 ** This file implements an in-core database using Red-Black balanced
 ** binary trees.
@@ -1331,10 +1331,10 @@ static const char *memBtreeGetFilename(Btree *pBt){
 }
 
 /*
-** Change the name of the underlying database file.
+** The copy file function is not implemented for the in-memory database
 */
-static int memBtreeChangeFilename(Btree *pBt, const char *zNew){
-  return SQLITE_OK;
+static int memBtreeCopyFile(Btree *pBt, Btree *pBt2){
+  return SQLITE_INTERNAL;  /* Not implemented */
 }
 
 static BtOps sqliteBtreeOps = {
@@ -1356,7 +1356,7 @@ static BtOps sqliteBtreeOps = {
     memBtreeUpdateMeta,
     memBtreeIntegrityCheck,
     memBtreeGetFilename,
-    memBtreeChangeFilename,
+    memBtreeCopyFile,
 
 #ifdef SQLITE_TEST
     memBtreePageDump,
index 086424bba630164d52ba32c62f1770bc69b1bbb8..eed9eef1a315e9debc4e54c4619f22c11fcd7406 100644 (file)
--- a/src/os.c
+++ b/src/os.c
@@ -282,6 +282,7 @@ int sqliteOsFileExists(const char *zFilename){
 }
 
 
+#if 0 /* NOT USED */
 /*
 ** Change the name of an existing file.
 */
@@ -304,7 +305,7 @@ int sqliteOsFileRename(const char *zOldName, const char *zNewName){
   return SQLITE_ERROR;
 #endif
 }
-
+#endif /* NOT USED */
 
 /*
 ** Attempt to open a file for both reading and writing.  If that
index cfbbaf7ee48d5033a6793e2a7419431fe10f43b7..373aceaf79d1ab1204a0fc791827c4a26e3e69f3 100644 (file)
@@ -18,7 +18,7 @@
 ** file simultaneously, or one process from reading the database while
 ** another is writing.
 **
-** @(#) $Id: pager.c,v 1.81 2003/04/06 20:52:32 drh Exp $
+** @(#) $Id: pager.c,v 1.82 2003/04/25 13:22:53 drh Exp $
 */
 #include "os.h"         /* Must be first to enable large file support */
 #include "sqliteInt.h"
@@ -927,6 +927,28 @@ int sqlitepager_pagecount(Pager *pPager){
   return n;
 }
 
+/*
+** Forward declaration
+*/
+static int syncAllPages(Pager*);
+
+/*
+** Truncate the file to the number of pages specified.
+*/
+int sqlitepager_truncate(Pager *pPager, Pgno nPage){
+  int rc;
+  if( pPager->dbSize<0 ) sqlitepager_pagecount(pPager);
+  if( nPage>=pPager->dbSize ){
+    return SQLITE_OK;
+  }
+  syncAllPages(pPager);
+  rc = sqliteOsTruncate(&pPager->fd, SQLITE_PAGE_SIZE*(off_t)nPage);
+  if( rc==SQLITE_OK ){
+    pPager->dbSize = nPage;
+  }
+  return rc;
+}
+
 /*
 ** Shutdown the page cache.  Free all memory and close all files.
 **
@@ -2037,43 +2059,6 @@ const char *sqlitepager_filename(Pager *pPager){
   return pPager->zFilename;
 }
 
-/*
-** Rename the database file
-*/
-int sqlitepager_rename(Pager *pPager, const char *zNewName){
-  char *zNew;
-  char *zJournal;
-  int nName;
-  int rc;
-
-  nName = strlen(zNewName);
-  zNew = sqliteMalloc( nName*2 + 30 );
-  if( zNew==0 ){
-    return SQLITE_NOMEM;
-  }
-  memcpy(zNew, zNewName, nName+1);
-  zJournal = &zNew[nName+1];
-  memcpy(zJournal, zNew, nName);
-  strcpy(&zJournal[nName], "-journal");
-  if( pPager->journalOpen ){
-    rc = sqliteOsFileRename(pPager->zJournal, zJournal);
-    if( rc ){
-      sqliteFree(zNew);
-      return rc;
-    }
-  }
-  rc = sqliteOsFileRename(pPager->zFilename, zNew);
-  if( rc ){
-    sqliteFree(zNew);
-    return rc;
-  }
-  if( pPager->zFilename!=(char*)&pPager[1] ){
-    sqliteFree(pPager->zFilename);
-  }
-  pPager->zFilename = zNew;
-  return SQLITE_OK;
-}
-
 #ifdef SQLITE_TEST
 /*
 ** Print a listing of all referenced pages and their ref count.
index 63963173c176a0d8ba3086bf7bac0c27618e0297..e3a97d9bda1026d2021ab1610313e50674c27c99 100644 (file)
@@ -13,7 +13,7 @@
 ** subsystem.  The page cache subsystem reads and writes a file a page
 ** at a time and provides a journal for rollback.
 **
-** @(#) $Id: pager.h,v 1.22 2003/04/06 20:44:45 drh Exp $
+** @(#) $Id: pager.h,v 1.23 2003/04/25 13:22:53 drh Exp $
 */
 
 /*
@@ -61,6 +61,7 @@ int sqlitepager_write(void*);
 int sqlitepager_iswriteable(void*);
 int sqlitepager_overwrite(Pager *pPager, Pgno pgno, void*);
 int sqlitepager_pagecount(Pager*);
+int sqlitepager_truncate(Pager*,Pgno);
 int sqlitepager_begin(void*);
 int sqlitepager_commit(Pager*);
 int sqlitepager_rollback(Pager*);
index bdc6a3770fb1683c90c3271815ac95bd04473f17..35d12c490759edeb208c5a91839a3391da4f57e6 100644 (file)
@@ -14,7 +14,7 @@
 ** 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 $
+** $Id: vacuum.c,v 1.5 2003/04/25 13:22:53 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -209,14 +209,13 @@ void sqliteVacuum(Parse *pParse, Token *pTableName){
   const char *zFilename;  /* full pathname of the database file */
   int nFilename;          /* number of characters  in zFilename[] */
   char *zTemp = 0;        /* a temporary file in same directory as zFilename */
-  char *zTemp2;           /* Another temp file in the same directory */
   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;
+  int rc = SQLITE_OK;     /* Return code from service routines */
+  int i;                  /* Loop counter */
+  char *zErrMsg = 0;      /* Error messages stored here */
+  int safety = 0;         /* TRUE if safety is off */
+  vacuumStruct sVac;      /* Information passed to callbacks */
 
   /* These are all of the pragmas that need to be transferred over
   ** to the new database */
@@ -248,20 +247,16 @@ void sqliteVacuum(Parse *pParse, Token *pTableName){
     return;
   }
   nFilename = strlen(zFilename);
-  zTemp = sqliteMalloc( 2*(nFilename+40) );
+  zTemp = sqliteMalloc( nFilename+100 );
   if( zTemp==0 ) return;
-  zTemp2 = &zTemp[nFilename+40];
   strcpy(zTemp, zFilename);
-  strcpy(zTemp2, zFilename);
   for(i=0; i<10; i++){
     zTemp[nFilename] = '-';
     randomName(&zTemp[nFilename+1]);
-    zTemp2[nFilename] = '-';
-    randomName(&zTemp2[nFilename+1]);
-    if( !sqliteOsFileExists(zTemp) && !sqliteOsFileExists(zTemp2) ) break;
+    if( !sqliteOsFileExists(zTemp) ) break;
   }
   if( i>=10 ){
-    sqliteErrorMsg(pParse, "unable to create a temporary database files "
+    sqliteErrorMsg(pParse, "unable to create a temporary database file "
        "in the same directory as the original database");
     goto end_of_vacuum;
   }
@@ -279,7 +274,9 @@ void sqliteVacuum(Parse *pParse, Token *pTableName){
   }
   safety = 1;
   if( execsql(pParse, db, "BEGIN") ) goto end_of_vacuum;
-  if( execsql(pParse, dbNew, "BEGIN") ) goto end_of_vacuum;
+  if( execsql(pParse, dbNew, "PRAGMA synchronous=off; BEGIN") ){
+    goto end_of_vacuum;
+  }
   sVac.dbOld = db;
   sVac.dbNew = dbNew;
   sVac.pParse = pParse;
@@ -291,55 +288,33 @@ void sqliteVacuum(Parse *pParse, Token *pTableName){
     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);
-    sqliteErrorMsg(pParse, "unable to rename database file");
-    goto end_of_vacuum;
+  if( rc==SQLITE_OK ){
+    rc = sqlite_exec(db, "SELECT type, name, sql FROM sqlite_master "
+             "WHERE sql NOT NULL", vacuumCallback1, &sVac, &zErrMsg);
   }
-  if( execsql(pParse, dbNew, "COMMIT;") ){
-    sqliteOsDelete(zFilename);
-    sqliteOsFileRename(zTemp2, zFilename);
+  if( rc ){
+    if( pParse->zErrMsg==0 ){
+      sqliteErrorMsg(pParse, "unable to vacuum database - %s", zErrMsg);
+    }
     goto end_of_vacuum;
   }
-  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");
-  }
+  rc = sqliteBtreeCopyFile(db->aDb[0].pBt, dbNew->aDb[0].pBt);
+  sqlite_exec(db, "COMMIT", 0, 0, 0);
   sqliteResetInternalSchema(db, 0);
 
 end_of_vacuum:
   sqlite_exec(db, "COMMIT", 0, 0, 0);
-  if( safety) {
+  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
 }
index 7fef069c0c127054e8d01fbf5cf42493248373ec..26c7f69990172d424d9ee5e8c08670177609f44a 100644 (file)
 # 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 $
+# $Id: vacuum.test,v 1.9 2003/04/25 13:22:53 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
+proc cksum {{db db}} {
+  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
+    append txt $prag-[$db eval "PRAGMA $prag"]\n
   }
-  # set fd [open $filename w]
-  # puts $fd $txt
-  # close $fd
-  return [string length $txt]-[md5 $txt]
+  set cksum [string length $txt]-[md5 $txt]
+  puts $cksum-[file size test.db]
+  return $cksum
 }
 
 do_test vacuum-1.1 {
@@ -49,14 +48,14 @@ do_test vacuum-1.1 {
     DROP TABLE t2;
   }
   set ::size1 [file size test.db]
-  set ::cksum [cksum vacuum1.txt]
+  set ::cksum [cksum]
   expr {$::cksum!=""}
 } {1}
 do_test vacuum-1.2 {
   execsql {
     VACUUM;
   }
-  cksum vacuum2.txt
+  cksum
 } $cksum
 do_test vacuum-1.3 {
   expr {[file size test.db]<$::size1}
@@ -74,14 +73,14 @@ do_test vacuum-1.4 {
     DROP TABLE t2;
   }
   set ::size1 [file size test.db]
-  set ::cksum [cksum vacuum3.txt]
+  set ::cksum [cksum]
   expr {$::cksum!=""}
 } {1}
 do_test vacuum-1.5 {
   execsql {
     VACUUM;
   }
-  cksum vacuum4.txt
+  cksum
 } $cksum
 do_test vacuum-1.6 {
   expr {[file size test.db]<$::size1}
@@ -94,6 +93,34 @@ do_test vacuum-2.1 {
     COMMIT;
   }
 } {1 {cannot VACUUM from within a transaction}}
-execsql COMMIT
+catch {db eval COMMIT}
+do_test vacuum-2.2 {
+  sqlite db2 test.db
+  execsql {
+    BEGIN;
+    CREATE TABLE t4 AS SELECT * FROM t1;
+    CREATE TABLE t5 AS SELECT * FROM t1;
+    COMMIT;
+    DROP TABLE t4;
+    DROP TABLE t5;
+  } db2
+  set ::cksum [cksum db2]
+  catchsql {
+    VACUUM
+  }
+} {1 {database schema has changed}}
+do_test vacuum-2.3 {
+  execsql {
+    VACUUM;
+  }
+  cksum
+} $cksum
+do_test vacuum-2.4 {
+  catch {db2 eval {SELECT count(*) FROM sqlite_master}}
+  cksum db2
+} $cksum
+
+
+catch {db2 close}
 
 # finish_test