From: drh Date: Fri, 18 Apr 2003 02:31:04 +0000 (+0000) Subject: The VACUUM command is now functioning (again). Need to do more testing. (CVS 916) X-Git-Tag: version-3.6.10~5152 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a5f668327b8caaed04dddc09dc1c25900d4f89cf;p=thirdparty%2Fsqlite.git The VACUUM command is now functioning (again). Need to do more testing. (CVS 916) FossilOrigin-Name: 6e948d9aaea109c683ac4fcc4714e335b545d22b --- diff --git a/manifest b/manifest index acf67aaab5..afd939fe18 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index cd41037e1b..297240c28f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -1e5e00fb73c308378efd034cb291caf338c9fe84 \ No newline at end of file +6e948d9aaea109c683ac4fcc4714e335b545d22b \ No newline at end of file diff --git a/src/vacuum.c b/src/vacuum.c index dc8cd1edf3..9cf9d6b7df 100644 --- a/src/vacuum.c +++ b/src/vacuum.c @@ -14,15 +14,16 @@ ** 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; is2, 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 }