]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
The VACUUM command is now functioning (again). Need to do more testing. (CVS 916)
authordrh <drh@noemail.net>
Fri, 18 Apr 2003 02:31:04 +0000 (02:31 +0000)
committerdrh <drh@noemail.net>
Fri, 18 Apr 2003 02:31:04 +0000 (02:31 +0000)
FossilOrigin-Name: 6e948d9aaea109c683ac4fcc4714e335b545d22b

manifest
manifest.uuid
src/vacuum.c

index acf67aaab588cb2801ea60e9acbde61de5774a4e..afd939fe18f55e7eeb87b9fba552af51efcd7739 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\striggers\sto\swork\sin\san\sATTACHed\sdatabase.\s\sTicket\s#295.\s(CVS\s915)
-D 2003-04-17T22:57:53
+C The\sVACUUM\scommand\sis\snow\sfunctioning\s(again).\s\sNeed\sto\sdo\smore\stesting.\s(CVS\s916)
+D 2003-04-18T02:31:04
 F Makefile.in df3a4db41a7450468b5fe934d9dd8f723b631249
 F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
@@ -58,7 +58,7 @@ F src/tokenize.c a88cfb6f698d047e14d5064fa6c4ecb709bf8fa4
 F src/trigger.c 45b67f6c4338245288e4662c6a5b802ae3a66e5d
 F src/update.c 7f1aa8912876a682a692676f8adb215ddffad295
 F src/util.c 13c338a7d0e1e6290ca227edb0d6d7be6a7c7127
-F src/vacuum.c ac65e9578506a0cdf70ece2668e5b22f4895477c
+F src/vacuum.c e24781e38db36d1c9f578b6b3613bf0989ebd63c
 F src/vdbe.c d453e8c95c9fac5a5e067c5c58243b3ae75699fc
 F src/vdbe.h 985c24f312d10f9ef8f9a8b8ea62fcdf68e82f21
 F src/where.c e5733f7d5e9cc4ed3590dc3401f779e7b7bb8127
@@ -162,7 +162,7 @@ F www/speed.tcl cb4c10a722614aea76d2c51f32ee43400d5951be
 F www/sqlite.tcl ae3dcfb077e53833b59d4fcc94d8a12c50a44098
 F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
-P 98ef6110068e5ed3cd77a14b004f890b79b731f7
-R 8d0229a2d7c6c86f6fe4145a683ab8e2
+P 1e5e00fb73c308378efd034cb291caf338c9fe84
+R 6d712256ba5bd6d3c2dab4df16be375d
 U drh
-Z a65dc41f80624ed136df0a6b0a99456f
+Z 2b3763770265c14f822420062ba64ba6
index cd41037e1bdaab49f48149b5586db7ff433ad3d6..297240c28f8ac1c3697c43ba0504a9e06ffd112e 100644 (file)
@@ -1 +1 @@
-1e5e00fb73c308378efd034cb291caf338c9fe84
\ No newline at end of file
+6e948d9aaea109c683ac4fcc4714e335b545d22b
\ No newline at end of file
index dc8cd1edf3bda7ea438f027a76569c7123839d6b..9cf9d6b7df50aaa751647ee33f8ce315032095ff 100644 (file)
 ** Most of the code in this file may be omitted by defining the
 ** SQLITE_OMIT_VACUUM macro.
 **
-** $Id: vacuum.c,v 1.2 2003/04/15 01:19:49 drh Exp $
+** $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
 
 /*
 ** A structure for holding a dynamic string - a string that can grow
-** without bound.
+** without bound. 
 */
 typedef struct dynStr dynStr;
 struct dynStr {
@@ -31,7 +32,19 @@ struct dynStr {
   int nUsed;      /* Next unused slot in z[] */
 };
 
-#ifndef SQLITE_OMIT_VACUUM
+/*
+** A structure that holds the vacuum context
+*/
+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
 */
@@ -59,7 +72,7 @@ static void appendQuoted(dynStr *p, const char *zText){
   int i, j;
   appendText(p, "'", 1);
   for(i=j=0; zText[i]; i++){
-    if( zText[i]='\'' ){
+    if( zText[i]=='\'' ){
       appendText(p, &zText[j], i-j+1);
       j = i + 1;
       appendText(p, "'", 1);
@@ -72,30 +85,79 @@ static void appendQuoted(dynStr *p, const char *zText){
 }
 
 /*
-** This is an SQLite callback that is invoked once for each row in
-** the SQLITE_MASTER table of the database being vacuumed.  The three
-** parameters are the type of entry, the name of the entry, and the SQL
-** text for the entry.
-**
-** Append SQL text to the dynStr that will make a copy of the structure
-** identified by this row.
+** Execute statements of SQL.  If an error occurs, write the error
+** message into pParse->zErrMsg and return non-zero.
+*/
+static int execsql(Parse *pParse, sqlite *db, const char *zSql){ 
+  int rc;
+  char *zErrMsg = 0;
+
+  /* printf("***** executing *****\n%s\n", zSql); */
+  rc = sqlite_exec(db, zSql, 0, 0, &zErrMsg);
+  if( rc ){
+    sqliteErrorMsg(pParse, "%s", zErrMsg);
+    sqlite_freemem(zErrMsg);
+  }
+  return rc;
+}
+
+/*
+** This is the second stage callback.  Each invocation contains all the
+** data for a single row of a single table in the original database.  This
+** routine must write that information into the new database.
 */
-static int vacuumCallback(void *pArg, int argc, char **argv, char **NotUsed){
-  dynStr *p = (dynStr*)pArg;
+static int vacuumCallback2(void *pArg, int argc, char **argv, char **NotUsed){
+  vacuumStruct *p = (vacuumStruct*)pArg;
+  int rc = 0;
+  const char *zSep = "(";
+  int i;
+
+  p->s2.nUsed = 0;
+  appendText(&p->s2, "INSERT INTO ", -1);
+  appendQuoted(&p->s2, p->zTable);
+  appendText(&p->s2, " VALUES", -1);
+  for(i=0; i<argc; i++){
+    appendText(&p->s2, zSep, 1);
+    zSep = ",";
+    if( argv[i]==0 ){
+      appendText(&p->s2, "NULL", 4);
+    }else{
+      appendQuoted(&p->s2, argv[i]);
+    }
+  }
+  appendText(&p->s2,")", 1);
+  rc = execsql(p->pParse, p->dbNew, p->s2.z);
+  return rc;
+}
+
+/*
+** This is the first stage callback.  Each invocation contains three
+** arguments where are taken from the SQLITE_MASTER table of the original
+** database:  (1) the entry type, (2) the entry name, and (3) the SQL for
+** the entry.  In all cases, execute the SQL of the third argument.
+** For tables, run a query to select all entries in that table and 
+** transfer them to the second-stage callback.
+*/
+static int vacuumCallback1(void *pArg, int argc, char **argv, char **NotUsed){
+  vacuumStruct *p = (vacuumStruct*)pArg;
+  int rc = 0;
   assert( argc==3 );
   assert( argv[0]!=0 );
   assert( argv[1]!=0 );
   assert( argv[2]!=0 );
-  appendText(p, argv[2], -1);
-  appendText(p, ";\n", 2);
-  if( strcmp(argv[0],"table")==0 ){
-    appendText(p, "INSERT INTO ", -1);
-    appendQuoted(p, argv[1]);
-    appendText(p, " SELECT * FROM ", -1);
-    appendQuoted(p, argv[1]);
-    appendText(p, ";\n");
-  }
-  return 0;
+  rc = execsql(p->pParse, p->dbNew, argv[2]);
+  if( rc==SQLITE_OK && strcmp(argv[0],"table")==0 ){
+    char *zErrMsg = 0;
+    p->s1.nUsed = 0;
+    appendText(&p->s1, "SELECT * FROM ", -1);
+    appendQuoted(&p->s1, argv[1]);
+    p->zTable = argv[1];
+    rc = sqlite_exec(p->dbOld, p->s1.z, vacuumCallback2, p, &zErrMsg);
+    if( rc && p->pParse->zErrMsg==0 ){
+      sqliteErrorMsg(p->pParse, "%s", zErrMsg);
+    }
+  }
+  return rc;
 }
 
 /*
@@ -124,18 +186,18 @@ static void randomName(char *zBuf){
 ** become a no-op.
 */
 void sqliteVacuum(Parse *pParse, Token *pTableName){
-#ifndef SQLITE_OMIT_VACUUM
+#ifdef SQLITE_OMIT_VACUUM
   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 *dbOld = 0;      /* Alternative connection to original database */
   sqlite *db;             /* The original database */
-  int rc;
+  int rc, i;
   char *zErrMsg = 0;
   char *zSql = 0;
-  dynStr sStr;
+  int safety = 0;
+  vacuumStruct sVac;
 
   /* Initial error checks
   */
@@ -147,7 +209,7 @@ void sqliteVacuum(Parse *pParse, Token *pTableName){
     sqliteErrorMsg(pParse, "cannot VACUUM from within a transaction");
     return;
   }
-  memset(&sStr, 0, sizeof(sStr));
+  memset(&sVac, 0, sizeof(sVac));
 
   /* Get the full pathname of the database file and create two
   ** temporary filenames in the same directory as the original file.
@@ -167,6 +229,7 @@ void sqliteVacuum(Parse *pParse, Token *pTableName){
   for(i=0; i<10; i++){
     zTemp[nFilename] = '-';
     randomName(&zTemp[nFilename+1]);
+    zTemp2[nFilename] = '-';
     randomName(&zTemp2[nFilename+1]);
     if( !sqliteOsFileExists(zTemp) && !sqliteOsFileExists(zTemp2) ) break;
   }
@@ -183,27 +246,57 @@ void sqliteVacuum(Parse *pParse, Token *pTableName){
        zTemp, zErrMsg);
     goto end_of_vacuum;
   }
-  appendText(&sStr, "ATTACH DATABASE ", -1);
-  appendQuoted(&sStr, zFilename);
-  appendText(&sStr, " AS orig;\nBEGIN;\n", -1);
-  if( execsql(pParse, dbNew, sStr.z) ) goto end_of_vacuum;
-  sStr.nUsed = 0;
-  rc = sqlite_exec(dbNew, "SELECT type, name, sql FROM sqlite_master "
-           "WHERE sql NOT NULL", vacuumCallback, &sStr, &zErrMsg);
+  if( sqliteSafetyOff(db) ){
+    sqliteErrorMsg(pParse, "library routines called out of sequence");
+    goto end_of_vacuum;
+  }
+  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 ){
-    sqliteErrorMsg(pParse, "unable to vacuum database - %s", zErrMsg);
+    if( pParse->zErrMsg==0 ){
+      sqliteErrorMsg(pParse, "unable to vacuum database - %s", zErrMsg);
+    }
     goto end_of_vacuum;
   }
-  appendText(&sStr, "COMMIT;\n", -1);
-  if( execsql(pParse, dbNew, sStr.z) ) 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);
+    sqliteErrorMsg(pParse, "unable to rename database file");
+    goto end_of_vacuum;
+  }
+  if( execsql(pParse, dbNew, "COMMIT;") ){
+    sqliteOsDelete(zFilename);
+    sqliteOsFileRename(zTemp2, zFilename);
+    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);
+  }
 
-  
 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(sStr.z);
+  sqliteFree(sVac.s1.z);
+  sqliteFree(sVac.s2.z);
   if( zErrMsg ) sqlite_freemem(zErrMsg);
-  if( dbNew ) sqlite_close(dbNew);
 #endif
 }