The VACUUM command is now functioning (again). Need to do more testing. (CVS 916)
FossilOrigin-Name: 6e948d9aaea109c683ac4fcc4714e335b545d22b
This commit is contained in:
@@ -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
@@ -1 +1 @@
|
||||
1e5e00fb73c308378efd034cb291caf338c9fe84
|
||||
6e948d9aaea109c683ac4fcc4714e335b545d22b
|
||||
+134
-41
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user