The VACUUM command is now functioning (again). Need to do more testing. (CVS 916)

FossilOrigin-Name: 6e948d9aaea109c683ac4fcc4714e335b545d22b
This commit is contained in:
drh
2003-04-18 02:31:04 +00:00
parent a69d91681d
commit a5f668327b
3 changed files with 141 additions and 48 deletions
+6 -6
View File
@@ -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
+1 -1
View File
@@ -1 +1 @@
1e5e00fb73c308378efd034cb291caf338c9fe84
6e948d9aaea109c683ac4fcc4714e335b545d22b
+134 -41
View File
@@ -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 vacuumCallback(void *pArg, int argc, char **argv, char **NotUsed){
dynStr *p = (dynStr*)pArg;
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 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");
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 0;
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( rc ){
sqliteErrorMsg(pParse, "unable to vacuum database - %s", 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 ){
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
}