aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordanielk1977 <danielk1977@noemail.net>2004-05-29 10:23:19 +0000
committerdanielk1977 <danielk1977@noemail.net>2004-05-29 10:23:19 +0000
commit3df6b2570f3b6adff9e22038a8594cd6deba0fcd (patch)
treead5e38c502bebc1032792a938032ae311990914e /src
parente7d00f5825ed22c50cafa59cebfba73f223311e8 (diff)
downloadsqlite-3df6b2570f3b6adff9e22038a8594cd6deba0fcd.tar.gz
sqlite-3df6b2570f3b6adff9e22038a8594cd6deba0fcd.zip
Fix vacuum so that it works with blobs. (CVS 1490)
FossilOrigin-Name: 4feb4b9a71ce7a92924d2358a7ccecb4cca19223
Diffstat (limited to 'src')
-rw-r--r--src/btree.c4
-rw-r--r--src/build.c15
-rw-r--r--src/main.c63
-rw-r--r--src/parse.y7
-rw-r--r--src/trigger.c6
-rw-r--r--src/vacuum.c374
6 files changed, 196 insertions, 273 deletions
diff --git a/src/btree.c b/src/btree.c
index ce482fd0d..21676365c 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -9,7 +9,7 @@
** May you share freely, never taking more than you give.
**
*************************************************************************
-** $Id: btree.c,v 1.147 2004/05/26 00:01:54 drh Exp $
+** $Id: btree.c,v 1.148 2004/05/29 10:23:19 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
@@ -4150,7 +4150,7 @@ const char *sqlite3BtreeGetFilename(Btree *pBt){
** 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.
+** If anything goes wrong, the transaction on pBtTo is rolled back.
*/
int sqlite3BtreeCopyFile(Btree *pBtTo, Btree *pBtFrom){
int rc = SQLITE_OK;
diff --git a/src/build.c b/src/build.c
index 8710897e8..ace0a6bc7 100644
--- a/src/build.c
+++ b/src/build.c
@@ -23,7 +23,7 @@
** ROLLBACK
** PRAGMA
**
-** $Id: build.c,v 1.198 2004/05/29 02:37:19 danielk1977 Exp $
+** $Id: build.c,v 1.199 2004/05/29 10:23:19 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@@ -566,14 +566,11 @@ void sqlite3StartTable(
}
/* Make sure the new table name does not collide with an existing
- ** index or table name. Issue an error message if it does.
- **
- ** If we are re-reading the sqlite_master table because of a schema
- ** change and a new permanent table is found whose name collides with
- ** an existing temporary table, that is not an error.
+ ** index or table name in the same database. Issue an error message if
+ ** it does.
*/
- pTable = sqlite3FindTable(db, zName, 0);
- if( pTable!=0 && (pTable->iDb==iDb || !db->init.busy) ){
+ pTable = sqlite3FindTable(db, zName, db->aDb[iDb].zName);
+ if( pTable ){
sqlite3ErrorMsg(pParse, "table %T already exists", pName);
sqliteFree(zName);
return;
@@ -1691,7 +1688,7 @@ void sqlite3CreateIndex(
Table *pTSameName; /* A table with same name as the index */
zName = sqliteStrNDup(pName->z, pName->n);
if( zName==0 ) goto exit_create_index;
- if( (pISameName = sqlite3FindIndex(db, zName, 0))!=0 ){
+ if( (pISameName = sqlite3FindIndex(db, zName, db->aDb[iDb].zName))!=0 ){
sqlite3ErrorMsg(pParse, "index %s already exists", zName);
goto exit_create_index;
}
diff --git a/src/main.c b/src/main.c
index 580b30888..b3fbebc70 100644
--- a/src/main.c
+++ b/src/main.c
@@ -14,7 +14,7 @@
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
-** $Id: main.c,v 1.195 2004/05/27 23:56:16 danielk1977 Exp $
+** $Id: main.c,v 1.196 2004/05/29 10:23:19 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
@@ -247,16 +247,28 @@ static int sqlite3InitOne(sqlite *db, int iDb, char **pzErrMsg){
memset(meta, 0, sizeof(meta));
}
db->aDb[iDb].schema_cookie = meta[0];
- if( iDb==0 ){
- db->next_cookie = meta[0];
- db->file_format = meta[1];
- if( meta[4] ){
- /* If meta[4] is still zero, then we are opening a previously empty
- ** file. Leave db->enc to the default value set by the sqlite3_open()
- ** call in this case.
- */
+
+ /* If opening a non-empty database, check the text encoding. For the
+ ** main database, set sqlite3.enc to the encoding of the main database.
+ ** For an attached db, it is an error if the encoding is not the same
+ ** as sqlite3.enc.
+ */
+ if( meta[4] ){ /* text encoding */
+ if( iDb==0 ){
+ /* If opening the main database, set db->enc. */
db->enc = (u8)meta[4];
+ }else{
+ /* If opening an attached database, the encoding much match db->enc */
+ if( meta[4]!=db->enc ){
+ sqlite3BtreeCloseCursor(curMain);
+ sqlite3SetString(pzErrMsg, "attached databases must use the same"
+ " text encoding as main database", (char*)0);
+ return SQLITE_ERROR;
+ }
}
+ }
+
+ if( iDb==0 ){
size = meta[2];
if( size==0 ){ size = MAX_PAGES; }
db->cache_size = size;
@@ -266,40 +278,35 @@ static int sqlite3InitOne(sqlite *db, int iDb, char **pzErrMsg){
}
if( db->safety_level==0 ) db->safety_level = 2;
- /*
- ** file_format==1 Version 3.0.0.
- */
+ /* FIX ME: Every struct Db will need a next_cookie */
+ db->next_cookie = meta[0];
+ db->file_format = meta[1];
if( db->file_format==0 ){
/* This happens if the database was initially empty */
db->file_format = 1;
- }else if( db->file_format>1 ){
- sqlite3BtreeCloseCursor(curMain);
- sqlite3SetString(pzErrMsg, "unsupported file format", (char*)0);
- return SQLITE_ERROR;
}
- }else if( db->file_format!=meta[1] ){
- if( meta[1]==0 ){
- sqlite3SetString(pzErrMsg, "cannot attach empty database: ",
- db->aDb[iDb].zName, (char*)0);
- }else{
- sqlite3SetString(pzErrMsg, "incompatible file format in auxiliary "
- "database: ", db->aDb[iDb].zName, (char*)0);
- }
- sqlite3BtreeClose(db->aDb[iDb].pBt);
- db->aDb[iDb].pBt = 0;
- return SQLITE_FORMAT;
}
+
+ /*
+ ** file_format==1 Version 3.0.0.
+ */
+ if( meta[1]>1 ){
+ sqlite3BtreeCloseCursor(curMain);
+ sqlite3SetString(pzErrMsg, "unsupported file format", (char*)0);
+ return SQLITE_ERROR;
+ }
+
sqlite3BtreeSetCacheSize(db->aDb[iDb].pBt, db->cache_size);
sqlite3BtreeSetSafetyLevel(db->aDb[iDb].pBt, meta[3]==0 ? 2 : meta[3]);
/* Read the schema information out of the schema tables
*/
assert( db->init.busy );
- sqlite3SafetyOff(db);
if( rc==SQLITE_EMPTY ){
/* For an empty database, there is nothing to read */
rc = SQLITE_OK;
}else{
+ sqlite3SafetyOff(db);
if( iDb==0 ){
/* This SQL statement tries to read the temp.* schema from the
** sqlite_temp_master table. It might return SQLITE_EMPTY.
diff --git a/src/parse.y b/src/parse.y
index 4b117c233..b22e4772a 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -14,7 +14,7 @@
** the parser. Lemon will also generate a header file containing
** numeric codes for all of the tokens.
**
-** @(#) $Id: parse.y,v 1.123 2004/05/29 02:37:19 danielk1977 Exp $
+** @(#) $Id: parse.y,v 1.124 2004/05/29 10:23:20 danielk1977 Exp $
*/
%token_prefix TK_
%token_type {Token}
@@ -782,17 +782,18 @@ plus_opt ::= .
//////////////////////////// The CREATE TRIGGER command /////////////////////
-cmd ::= CREATE(A) trigger_decl BEGIN trigger_cmd_list(S) END(Z). {
+cmd ::= CREATE trigger_decl(A) BEGIN trigger_cmd_list(S) END(Z). {
Token all;
all.z = A.z;
all.n = (Z.z - A.z) + Z.n;
sqlite3FinishTrigger(pParse, S, &all);
}
-trigger_decl ::= temp(T) TRIGGER nm(B) dbnm(Z) trigger_time(C) trigger_event(D)
+trigger_decl(A) ::= temp(T) TRIGGER nm(B) dbnm(Z) trigger_time(C) trigger_event(D)
ON nm(E) dbnm(DB) foreach_clause(F) when_clause(G). {
SrcList *pTab = sqlite3SrcListAppend(0, &E, &DB);
sqlite3BeginTrigger(pParse, &B, &Z, C, D.a, D.b, pTab, F, G, T);
+ A = (Z.n==0?B:Z);
}
%type trigger_time {int}
diff --git a/src/trigger.c b/src/trigger.c
index e9e31b2db..fa701e2d6 100644
--- a/src/trigger.c
+++ b/src/trigger.c
@@ -213,7 +213,9 @@ void sqlite3FinishTrigger(
{ OP_String, 0, 0, 0 }, /* 2: trigger name */
{ OP_String, 0, 0, 0 }, /* 3: table name */
{ OP_Integer, 0, 0, 0 },
- { OP_String, 0, 0, 0 }, /* 5: SQL */
+ { OP_String, 0, 0, "CREATE TRIGGER "},
+ { OP_String, 0, 0, 0 }, /* 6: SQL */
+ { OP_Concat, 2, 0, 0 },
{ OP_MakeRecord, 5, 0, "tttit" },
{ OP_PutIntKey, 0, 0, 0 },
};
@@ -228,7 +230,7 @@ void sqlite3FinishTrigger(
addr = sqlite3VdbeAddOpList(v, ArraySize(insertTrig), insertTrig);
sqlite3VdbeChangeP3(v, addr+2, nt->name, 0);
sqlite3VdbeChangeP3(v, addr+3, nt->table, 0);
- sqlite3VdbeChangeP3(v, addr+5, pAll->z, pAll->n);
+ sqlite3VdbeChangeP3(v, addr+6, pAll->z, pAll->n);
if( nt->iDb==0 ){
sqlite3ChangeCookie(db, v, 0);
}
diff --git a/src/vacuum.c b/src/vacuum.c
index 0e441b400..fcbfface7 100644
--- a/src/vacuum.c
+++ b/src/vacuum.c
@@ -14,186 +14,60 @@
** Most of the code in this file may be omitted by defining the
** SQLITE_OMIT_VACUUM macro.
**
-** $Id: vacuum.c,v 1.16 2004/05/22 09:21:21 danielk1977 Exp $
+** $Id: vacuum.c,v 1.17 2004/05/29 10:23:20 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
-/*
-** A structure for holding a dynamic string - a string that can grow
-** without bound.
-*/
-typedef struct dynStr dynStr;
-struct dynStr {
- char *z; /* Text of the string in space obtained from sqliteMalloc() */
- int nAlloc; /* Amount of space allocated to z[] */
- int nUsed; /* Next unused slot in z[] */
-};
-
-/*
-** A structure that holds the vacuum context
-*/
-typedef struct vacuumStruct vacuumStruct;
-struct vacuumStruct {
- sqlite *dbOld; /* Original database */
- sqlite *dbNew; /* New database */
- char **pzErrMsg; /* Write errors here */
- int rc; /* Set to non-zero on an error */
- const char *zTable; /* Name of a table being copied */
- const char *zPragma; /* Pragma to execute with results */
- dynStr s1, s2; /* Two dynamic strings */
-};
-
#if !defined(SQLITE_OMIT_VACUUM) || SQLITE_OMIT_VACUUM
/*
-** Append text to a dynamic string
+** Generate a random name of 20 character in length.
*/
-static void appendText(dynStr *p, const char *zText, int nText){
- if( nText<0 ) nText = strlen(zText);
- if( p->z==0 || p->nUsed + nText + 1 >= p->nAlloc ){
- char *zNew;
- p->nAlloc = p->nUsed + nText + 1000;
- zNew = sqliteRealloc(p->z, p->nAlloc);
- if( zNew==0 ){
- sqliteFree(p->z);
- memset(p, 0, sizeof(*p));
- return;
- }
- p->z = zNew;
+static void randomName(unsigned char *zBuf){
+ static const unsigned char zChars[] =
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789";
+ int i;
+ sqlite3Randomness(20, zBuf);
+ for(i=0; i<20; i++){
+ zBuf[i] = zChars[ zBuf[i]%(sizeof(zChars)-1) ];
}
- memcpy(&p->z[p->nUsed], zText, nText+1);
- p->nUsed += nText;
}
/*
-** Append text to a dynamic string, having first put the text in quotes.
+** Execute zSql on database db. Return an error code.
*/
-static void appendQuoted(dynStr *p, const char *zText){
- int i, j;
- appendText(p, "'", 1);
- for(i=j=0; zText[i]; i++){
- if( zText[i]=='\'' ){
- appendText(p, &zText[j], i-j+1);
- j = i + 1;
- appendText(p, "'", 1);
- }
- }
- if( j<i ){
- appendText(p, &zText[j], i-j);
+static int execSql(sqlite3 *db, const char *zSql){
+ sqlite3_stmt *pStmt;
+ if( SQLITE_OK!=sqlite3_prepare(db, zSql, -1, &pStmt, 0) ){
+ return sqlite3_errcode(db);
}
- appendText(p, "'", 1);
+ while( SQLITE_ROW==sqlite3_step(pStmt) );
+ return sqlite3_finalize(pStmt);
}
/*
-** Execute statements of SQL. If an error occurs, write the error
-** message into *pzErrMsg and return non-zero.
+** Execute zSql on database db. The statement returns exactly
+** one column. Execute this as SQL on the same database.
*/
-static int execsql(char **pzErrMsg, sqlite *db, const char *zSql){
- char *zErrMsg = 0;
+static int execExecSql(sqlite3 *db, const char *zSql){
+ sqlite3_stmt *pStmt;
int rc;
- /* printf("***** executing *****\n%s\n", zSql); */
- rc = sqlite3_exec(db, zSql, 0, 0, &zErrMsg);
- if( zErrMsg ){
- sqlite3SetString(pzErrMsg, zErrMsg, (char*)0);
- sqlite3_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;
- const char *zSep = "(";
- int i;
+ rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
+ if( rc!=SQLITE_OK ) return rc;
- if( argv==0 ) return 0;
- 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]);
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
+ rc = execSql(db, sqlite3_column_text(pStmt, 0));
+ if( rc!=SQLITE_OK ){
+ sqlite3_finalize(pStmt);
+ return rc;
}
}
- appendText(&p->s2,")", 1);
- p->rc = execsql(p->pzErrMsg, p->dbNew, p->s2.z);
- return p->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 );
- if( argv==0 ) return 0;
- assert( argv[0]!=0 );
- assert( argv[1]!=0 );
- assert( argv[2]!=0 );
- rc = execsql(p->pzErrMsg, 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 = sqlite3_exec(p->dbOld, p->s1.z, vacuumCallback2, p, &zErrMsg);
- if( zErrMsg ){
- sqlite3SetString(p->pzErrMsg, zErrMsg, (char*)0);
- sqlite3_freemem(zErrMsg);
- }
- }
- if( rc!=SQLITE_ABORT ) p->rc = rc;
- return rc;
-}
-
-/*
-** This callback is used to transfer PRAGMA settings from one database
-** to the other. The value in argv[0] should be passed to a pragma
-** identified by ((vacuumStruct*)pArg)->zPragma.
-*/
-static int vacuumCallback3(void *pArg, int argc, char **argv, char **NotUsed){
- vacuumStruct *p = (vacuumStruct*)pArg;
- char zBuf[200];
- assert( argc==1 );
- if( argv==0 ) return 0;
- assert( argv[0]!=0 );
- assert( strlen(p->zPragma)<100 );
- assert( strlen(argv[0])<30 );
- sprintf(zBuf,"PRAGMA %s=%s;", p->zPragma, argv[0]);
- p->rc = execsql(p->pzErrMsg, p->dbNew, zBuf);
- return p->rc;
+ return sqlite3_finalize(pStmt);
}
-/*
-** Generate a random name of 20 character in length.
-*/
-static void randomName(unsigned char *zBuf){
- static const unsigned char zChars[] =
- "abcdefghijklmnopqrstuvwxyz"
- "0123456789";
- int i;
- sqlite3Randomness(20, zBuf);
- for(i=0; i<20; i++){
- zBuf[i] = zChars[ zBuf[i]%(sizeof(zChars)-1) ];
- }
-}
#endif
/*
@@ -216,108 +90,150 @@ void sqlite3Vacuum(Parse *pParse, Token *pTableName){
** This routine implements the OP_Vacuum opcode of the VDBE.
*/
int sqlite3RunVacuum(char **pzErrMsg, sqlite *db){
+ int rc = SQLITE_OK; /* Return code from service routines */
#if !defined(SQLITE_OMIT_VACUUM) || 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 */
- sqlite *dbNew = 0; /* The new vacuumed database */
- int rc = SQLITE_OK; /* Return code from service routines */
int i; /* Loop counter */
- char *zErrMsg; /* Error message */
- vacuumStruct sVac; /* Information passed to callbacks */
- /* These are all of the pragmas that need to be transferred over
- ** to the new database */
- static const char *zPragma[] = {
- "default_synchronous",
- "default_cache_size",
- /* "default_temp_store", */
- };
+ char *zSql = 0;
+ sqlite3_stmt *pStmt = 0;
if( db->flags & SQLITE_InTrans ){
sqlite3SetString(pzErrMsg, "cannot VACUUM from within a transaction",
(char*)0);
- return SQLITE_ERROR;
+ rc = SQLITE_ERROR;
+ goto end_of_vacuum;
}
- 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.
+ /* Get the full pathname of the database file and create a
+ ** temporary filename in the same directory as the original file.
*/
zFilename = sqlite3BtreeGetFilename(db->aDb[0].pBt);
if( zFilename==0 ){
- /* This only happens with the in-memory database. VACUUM is a no-op
- ** there, so just return */
- return SQLITE_OK;
+ /* The in-memory database. Do nothing. */
+ goto end_of_vacuum;
}
nFilename = strlen(zFilename);
zTemp = sqliteMalloc( nFilename+100 );
- if( zTemp==0 ) return SQLITE_NOMEM;
+ if( zTemp==0 ){
+ rc = SQLITE_NOMEM;
+ goto end_of_vacuum;
+ }
strcpy(zTemp, zFilename);
for(i=0; i<10; i++){
zTemp[nFilename] = '-';
randomName((unsigned char*)&zTemp[nFilename+1]);
if( !sqlite3OsFileExists(zTemp) ) break;
}
- if( i>=10 ){
- sqlite3SetString(pzErrMsg, "unable to create a temporary database file "
- "in the same directory as the original database", (char*)0);
- goto end_of_vacuum;
- }
-
- if( SQLITE_OK!=sqlite3_open(zTemp, &dbNew, 0) ){
- sqlite3SetString(pzErrMsg, "unable to open a temporary database at ",
- zTemp, " - ", sqlite3_errmsg(dbNew), (char*)0);
- goto end_of_vacuum;
- }
- if( (rc = execsql(pzErrMsg, db, "BEGIN"))!=0 ) goto end_of_vacuum;
- if( (rc = execsql(pzErrMsg, dbNew, "PRAGMA synchronous=off; BEGIN"))!=0 ){
+ /* Attach the temporary database as 'vacuum' */
+ zSql = sqlite3MPrintf("ATTACH '%s' AS vacuum_db;", zTemp);
+ if( !zSql ){
+ rc = SQLITE_NOMEM;
goto end_of_vacuum;
}
-
- sVac.dbOld = db;
- sVac.dbNew = dbNew;
- sVac.pzErrMsg = pzErrMsg;
- for(i=0; rc==SQLITE_OK && i<sizeof(zPragma)/sizeof(zPragma[0]); i++){
- char zBuf[200];
- assert( strlen(zPragma[i])<100 );
- sprintf(zBuf, "PRAGMA %s;", zPragma[i]);
- sVac.zPragma = zPragma[i];
- rc = sqlite3_exec(db, zBuf, vacuumCallback3, &sVac, &zErrMsg);
- }
- if( rc==SQLITE_OK ){
- rc = sqlite3_exec(db,
- "SELECT type, name, sql FROM sqlite_master "
- "WHERE sql NOT NULL AND type!='view' "
- "UNION ALL "
- "SELECT type, name, sql FROM sqlite_master "
- "WHERE sql NOT NULL AND type=='view'",
- vacuumCallback1, &sVac, &zErrMsg);
- }
- if( rc==SQLITE_OK ){
- rc = sqlite3BtreeCopyFile(db->aDb[0].pBt, dbNew->aDb[0].pBt);
- sqlite3_exec(db, "COMMIT", 0, 0, 0);
- sqlite3ResetInternalSchema(db, 0);
- }
+ rc = execSql(db, zSql);
+ sqliteFree(zSql);
+ zSql = 0;
+ if( rc!=SQLITE_OK ) goto end_of_vacuum;
-end_of_vacuum:
- if( rc && zErrMsg!=0 ){
- sqlite3SetString(pzErrMsg, "unable to vacuum database - ",
- zErrMsg, (char*)0);
- }
- sqlite3_exec(db, "ROLLBACK", 0, 0, 0);
- if( dbNew ) sqlite3_close(dbNew);
- sqlite3OsDelete(zTemp);
- sqliteFree(zTemp);
- sqliteFree(sVac.s1.z);
- sqliteFree(sVac.s2.z);
- if( dbNew ) sqlite3_close(dbNew);
- if( zErrMsg ) sqlite3_freemem(zErrMsg);
- if( rc==SQLITE_ABORT ) sVac.rc = SQLITE_ERROR;
- return sVac.rc;
-#endif
-}
+ /* Begin a transaction */
+ rc = execSql(db, "BEGIN;");
+ if( rc!=SQLITE_OK ) goto end_of_vacuum;
+ /* Query the schema of the main database. Create a mirror schema
+ ** in the temporary database.
+ */
+ rc = execExecSql(db,
+ "SELECT 'CREATE ' || type || ' vacuum_db.' || "
+ "substr(sql, length(type)+9, 1000000) "
+ "FROM sqlite_master "
+ "WHERE type != 'trigger' AND sql IS NOT NULL "
+ "ORDER BY (type != 'table');"
+ );
+ if( rc!=SQLITE_OK ) goto end_of_vacuum;
+
+ /* Loop through the tables in the main database. For each, do
+ ** an "INSERT INTO vacuum_db.xxx SELECT * FROM xxx;" to copy
+ ** the contents to the temporary database.
+ */
+ rc = execExecSql(db,
+ "SELECT 'INSERT INTO vacuum_db.' || name "
+ "|| ' SELECT * FROM ' || name || ';'"
+ "FROM sqlite_master "
+ "WHERE type = 'table';"
+ );
+ if( rc!=SQLITE_OK ) goto end_of_vacuum;
+
+ /* Copy the triggers from the main database to the temporary database.
+ ** This was deferred before in case the triggers interfered with copying
+ ** the data. It's possible the indices should be deferred until this
+ ** point also.
+ */
+ rc = execExecSql(db,
+ "SELECT 'CREATE ' || type || ' vacuum_db.' || "
+ "substr(sql, length(type)+9, 1000000) "
+ "FROM sqlite_master "
+ "WHERE type = 'trigger' AND sql IS NOT NULL;"
+ );
+ if( rc!=SQLITE_OK ) goto end_of_vacuum;
+
+
+ /* At this point, unless the main db was completely empty, there is now a
+ ** transaction open on the vacuum database, but not on the main database.
+ ** Open a btree level transaction on the main database. This allows a
+ ** call to sqlite3BtreeCopyFile(). The main database btree level
+ ** transaction is then committed, so the SQL level never knows it was
+ ** opened for writing. This way, the SQL transaction used to create the
+ ** temporary database never needs to be committed.
+ */
+ /* FIX ME: The above will be the case shortly. But for now, a transaction
+ ** will have been started on the main database file by the 'BEGIN'.
+ */
+/*
+ rc = sqlite3BtreeBeginTrans(db->aDb[0].pBt);
+ if( rc!=SQLITE_OK ) goto end_of_vacuum;
+*/
+ if( db->aDb[db->nDb-1].inTrans ){
+ Btree *pTemp = db->aDb[db->nDb-1].pBt;
+ Btree *pMain = db->aDb[0].pBt;
+ u32 meta;
+
+ /* Copy Btree meta values 3 and 4. These correspond to SQL layer meta
+ ** values 2 and 3, the default values of a couple of pragmas.
+ */
+ rc = sqlite3BtreeGetMeta(pMain, 3, &meta);
+ if( rc!=SQLITE_OK ) goto end_of_vacuum;
+ rc = sqlite3BtreeUpdateMeta(pTemp, 3, meta);
+ if( rc!=SQLITE_OK ) goto end_of_vacuum;
+ rc = sqlite3BtreeGetMeta(pMain, 4, &meta);
+ if( rc!=SQLITE_OK ) goto end_of_vacuum;
+ rc = sqlite3BtreeUpdateMeta(pTemp, 4, meta);
+ if( rc!=SQLITE_OK ) goto end_of_vacuum;
+
+ rc = sqlite3BtreeCopyFile(pMain, pTemp);
+
+ /* FIX ME: Remove the main btree from the transaction so that it is not
+ ** rolled back. This won't be required once the new 'auto-commit'
+ ** model is in place.
+ */
+ rc = sqlite3BtreeCommit(pMain);
+ db->aDb[0].inTrans = 0;
+ }
+
+end_of_vacuum:
+ execSql(db, "DETACH vacuum_db;");
+ execSql(db, "ROLLBACK;");
+ if( zTemp ){
+ sqlite3OsDelete(zTemp);
+ sqliteFree(zTemp);
+ }
+ if( zSql ) sqliteFree( zSql );
+ if( pStmt ) sqlite3_finalize( pStmt );
+#endif
+ return rc;
+}