Make the VACUUM command run out of the VDBE like all other commands.

(Ticket #464).  Make the VACUUM command work even if there are VIEWs
in the SQLITE_MASTER table that come before tables they reference.
(Ticket #515) (CVS 1128)

FossilOrigin-Name: 614cbbafa180469744421f8fbe56cb392f48d05f
This commit is contained in:
drh 2003-12-07 00:24:35 +00:00
parent 447623d9aa
commit 6f8c91caa0
7 changed files with 110 additions and 71 deletions

View File

@ -1,5 +1,5 @@
C Fail\san\sATTACH\sif\sthe\sauxiliary\sdatabase\sis\slocked.\s\sTicket\s#514.\s(CVS\s1127)
D 2003-12-06T22:22:36
C Make\sthe\sVACUUM\scommand\srun\sout\sof\sthe\sVDBE\slike\sall\sother\scommands.\n(Ticket\s#464).\s\sMake\sthe\sVACUUM\scommand\swork\seven\sif\sthere\sare\sVIEWs\nin\sthe\sSQLITE_MASTER\stable\sthat\scome\sbefore\stables\sthey\sreference.\n(Ticket\s#515)\s(CVS\s1128)
D 2003-12-07T00:24:35
F Makefile.in 5cb273b7d0e945d47ee8b9ad1c2a04ce79927d2d
F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
@ -50,7 +50,7 @@ F src/select.c d79ac60ba1595ff3c94b12892e87098329776482
F src/shell.c 3b067edc098c45caca164bcad1fa79192c3ec5ae
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
F src/sqlite.h.in e6cfff01fafc8a82ce82cd8c932af421dc9adb54
F src/sqliteInt.h 882aa33ee2aed7685449b899d917a316b9cc2c44
F src/sqliteInt.h f8549cf426920e43efb105a08484768cdb73c808
F src/table.c d845cb101b5afc1f7fea083c99e3d2fa7998d895
F src/tclsqlite.c 3efac6b5861ac149c41251d4d4c420c94be5ba6a
F src/test1.c f9d5816610f7ec4168ab7b098d5207a5708712b6
@ -61,8 +61,8 @@ F src/tokenize.c d10e7f0b4d8634f6f37237b4e65314e3e5a3a34b
F src/trigger.c ce83e017b407d046e909d05373d7f8ee70f9f7f9
F src/update.c 24260b4fda00c9726d27699a0561d53c0dccc397
F src/util.c cc95dd360fac09a059b2ab98e4c333d1a2308db5
F src/vacuum.c e4724eade07e4cf8897060a8cf632dbd92408eeb
F src/vdbe.c d61f720a836a7c948356105b87b2512a9484cb3b
F src/vacuum.c 77485a64a6e4e358170f150fff681c1624a092b0
F src/vdbe.c b40c2a7002c0c8e5a226666622f487e241dadf36
F src/vdbe.h 3957844e46fea71fd030e78f6a3bd2f7e320fb43
F src/vdbeInt.h 2824bf88895b901b3a8c9e44527c67530e1c0dcb
F src/vdbeaux.c 877ae44ab42f43d38e8cd989087627508a4c98dd
@ -70,7 +70,7 @@ F src/where.c d01a3506f3c1e3a205028068c8a14d713872c633
F test/all.test 569a92a8ee88f5300c057cc4a8f50fbbc69a3242
F test/attach.test c26848402e7ac829e043e1fa5e0eb87032e5d81d
F test/attach2.test d0105f4e8b1debf0ac25ed7df986b5854620e172
F test/auth.test b7d6bdeffa804b96b7bcac2712e5f71ce014a1b8
F test/auth.test 5c4d95cdaf539c0c236e20ce1f71a93e7dde9185
F test/bigfile.test 1cd8256d4619c39bea48147d344f348823e78678
F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578
F test/bind.test 56a57043b42c4664ca705f6050e56717a8a6699a
@ -135,7 +135,7 @@ F test/trigger3.test a95ccace88291449f5eae7139ec438a42f90654d
F test/trigger4.test 542afce45774e8f8e1130b96b8675f414d6e4bd8
F test/unique.test 0e38d4cc7affeef2527720d1dafd1f6870f02f2b
F test/update.test 2ef5a6655f2966f0aef733a9f4495b3fe8e16809
F test/vacuum.test a58776ef529e9bc21980ac120d6859d4ee34b578
F test/vacuum.test 9447f1d7633b083c9b97f807fa05f9b27ada7503
F test/version.test 605fd0d7e7d571370c32b12dbf395b58953de246
F test/view.test 1ee12c6f8f4791a2c0655120d5562a49400cfe53
F test/where.test cb3a2ed062ce4b5f08aff2d08027c6a46d68c47b
@ -176,7 +176,7 @@ F www/speed.tcl 2f6b1155b99d39adb185f900456d1d592c4832b3
F www/sqlite.tcl 3c83b08cf9f18aa2d69453ff441a36c40e431604
F www/tclsqlite.tcl b9271d44dcf147a93c98f8ecf28c927307abd6da
F www/vdbe.tcl 9b9095d4495f37697fd1935d10e14c6015e80aa1
P 656c90387a4a714b4f31040ece9b0e15e30934af
R 9ba0dba1a5b0b6a0c4f493ffeeb808ce
P ac428c8d4a731678cc26cf198689814a8a56d141
R de0e25f496472fda102deb07f0a3f4c2
U drh
Z 4d2d90a1bb0f4a7af58cf43ec43a116f
Z da3da18ef1bdac6a224ddd572daad46d

View File

@ -1 +1 @@
ac428c8d4a731678cc26cf198689814a8a56d141
614cbbafa180469744421f8fbe56cb392f48d05f

View File

@ -11,7 +11,7 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.203 2003/11/27 00:48:58 drh Exp $
** @(#) $Id: sqliteInt.h,v 1.204 2003/12/07 00:24:35 drh Exp $
*/
#include "config.h"
#include "sqlite.h"
@ -1125,6 +1125,7 @@ Index *sqliteFindIndex(sqlite*,const char*, const char*);
void sqliteUnlinkAndDeleteIndex(sqlite*,Index*);
void sqliteCopy(Parse*, SrcList*, Token*, Token*, int);
void sqliteVacuum(Parse*, Token*);
int sqliteRunVacuum(char**, sqlite*);
int sqliteGlobCompare(const unsigned char*,const unsigned char*);
int sqliteLikeCompare(const unsigned char*,const unsigned char*);
char *sqliteTableNameFromToken(Token*);

View File

@ -14,7 +14,7 @@
** Most of the code in this file may be omitted by defining the
** SQLITE_OMIT_VACUUM macro.
**
** $Id: vacuum.c,v 1.8 2003/08/15 13:24:52 drh Exp $
** $Id: vacuum.c,v 1.9 2003/12/07 00:24:35 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
@ -37,7 +37,8 @@ typedef struct vacuumStruct vacuumStruct;
struct vacuumStruct {
sqlite *dbOld; /* Original database */
sqlite *dbNew; /* New database */
Parse *pParse; /* The parser context */
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 */
@ -85,16 +86,16 @@ static void appendQuoted(dynStr *p, const char *zText){
/*
** Execute statements of SQL. If an error occurs, write the error
** message into pParse->zErrMsg and return non-zero.
** message into *pzErrMsg and return non-zero.
*/
static int execsql(Parse *pParse, sqlite *db, const char *zSql){
int rc;
static int execsql(char **pzErrMsg, sqlite *db, const char *zSql){
char *zErrMsg = 0;
int rc;
/* printf("***** executing *****\n%s\n", zSql); */
rc = sqlite_exec(db, zSql, 0, 0, &zErrMsg);
if( rc ){
sqliteErrorMsg(pParse, "%s", zErrMsg);
if( zErrMsg ){
sqliteSetString(pzErrMsg, zErrMsg, (char*)0);
sqlite_freemem(zErrMsg);
}
return rc;
@ -126,7 +127,7 @@ static int vacuumCallback2(void *pArg, int argc, char **argv, char **NotUsed){
}
}
appendText(&p->s2,")", 1);
rc = execsql(p->pParse, p->dbNew, p->s2.z);
rc = execsql(p->pzErrMsg, p->dbNew, p->s2.z);
return rc;
}
@ -146,7 +147,7 @@ static int vacuumCallback1(void *pArg, int argc, char **argv, char **NotUsed){
assert( argv[0]!=0 );
assert( argv[1]!=0 );
assert( argv[2]!=0 );
rc = execsql(p->pParse, p->dbNew, argv[2]);
rc = execsql(p->pzErrMsg, p->dbNew, argv[2]);
if( rc==SQLITE_OK && strcmp(argv[0],"table")==0 ){
char *zErrMsg = 0;
p->s1.nUsed = 0;
@ -154,8 +155,9 @@ static int vacuumCallback1(void *pArg, int argc, char **argv, char **NotUsed){
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);
if( zErrMsg ){
sqliteSetString(p->pzErrMsg, zErrMsg, (char*)0);
sqlite_freemem(zErrMsg);
}
}
return rc;
@ -176,7 +178,7 @@ static int vacuumCallback3(void *pArg, int argc, char **argv, char **NotUsed){
assert( strlen(p->zPragma)<100 );
assert( strlen(argv[0])<30 );
sprintf(zBuf,"PRAGMA %s=%s;", p->zPragma, argv[0]);
rc = execsql(p->pParse, p->dbNew, zBuf);
rc = execsql(p->pzErrMsg, p->dbNew, zBuf);
return rc;
}
@ -206,16 +208,23 @@ static void randomName(char *zBuf){
** become a no-op.
*/
void sqliteVacuum(Parse *pParse, Token *pTableName){
Vdbe *v = sqliteGetVdbe(pParse);
sqliteVdbeAddOp(v, OP_Vacuum, 0, 0);
return;
}
/*
** This routine implements the OP_Vacuum opcode of the VDBE.
*/
int sqliteRunVacuum(char **pzErrMsg, sqlite *db){
#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 */
sqlite *db; /* The original database */
int rc = SQLITE_OK; /* Return code from service routines */
int i; /* Loop counter */
char *zErrMsg = 0; /* Error messages stored here */
int safety = 0; /* TRUE if safety is off */
char *zErrMsg; /* Error message */
vacuumStruct sVac; /* Information passed to callbacks */
/* These are all of the pragmas that need to be transferred over
@ -226,15 +235,10 @@ void sqliteVacuum(Parse *pParse, Token *pTableName){
/* "default_temp_store", */
};
/* Initial error checks
*/
if( pParse->explain ){
return;
}
db = pParse->db;
if( db->flags & SQLITE_InTrans ){
sqliteErrorMsg(pParse, "cannot VACUUM from within a transaction");
return;
sqliteSetString(pzErrMsg, "cannot VACUUM from within a transaction",
(char*)0);
return SQLITE_ERROR;
}
memset(&sVac, 0, sizeof(sVac));
@ -245,11 +249,11 @@ void sqliteVacuum(Parse *pParse, Token *pTableName){
if( zFilename==0 ){
/* This only happens with the in-memory database. VACUUM is a no-op
** there, so just return */
return;
return SQLITE_OK;
}
nFilename = strlen(zFilename);
zTemp = sqliteMalloc( nFilename+100 );
if( zTemp==0 ) return;
if( zTemp==0 ) return SQLITE_NOMEM;
strcpy(zTemp, zFilename);
for(i=0; i<10; i++){
zTemp[nFilename] = '-';
@ -257,30 +261,26 @@ void sqliteVacuum(Parse *pParse, Token *pTableName){
if( !sqliteOsFileExists(zTemp) ) break;
}
if( i>=10 ){
sqliteErrorMsg(pParse, "unable to create a temporary database file "
"in the same directory as the original database");
sqliteSetString(pzErrMsg, "unable to create a temporary database file "
"in the same directory as the original database", (char*)0);
goto end_of_vacuum;
}
dbNew = sqlite_open(zTemp, 0, &zErrMsg);
if( dbNew==0 ){
sqliteErrorMsg(pParse, "unable to open a temporary database at %s - %s",
zTemp, zErrMsg);
sqliteSetString(pzErrMsg, "unable to open a temporary database at ",
zTemp, " - ", zErrMsg, (char*)0);
goto end_of_vacuum;
}
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, "PRAGMA synchronous=off; BEGIN") ){
if( execsql(pzErrMsg, db, "BEGIN") ) goto end_of_vacuum;
if( execsql(pzErrMsg, dbNew, "PRAGMA synchronous=off; BEGIN") ){
goto end_of_vacuum;
}
sVac.dbOld = db;
sVac.dbNew = dbNew;
sVac.pParse = pParse;
sVac.pzErrMsg = pzErrMsg;
for(i=0; rc==SQLITE_OK && i<sizeof(zPragma)/sizeof(zPragma[0]); i++){
char zBuf[200];
assert( strlen(zPragma[i])<100 );
@ -288,32 +288,34 @@ void sqliteVacuum(Parse *pParse, Token *pTableName){
sVac.zPragma = zPragma[i];
rc = sqlite_exec(db, zBuf, vacuumCallback3, &sVac, &zErrMsg);
}
if( !rc ){
rc = sqlite_exec(db, "SELECT type, name, sql FROM sqlite_master "
"WHERE sql NOT NULL", vacuumCallback1, &sVac, &zErrMsg);
if( rc==SQLITE_OK ){
rc = sqlite_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 ){
if( rc==SQLITE_OK ){
rc = sqliteBtreeCopyFile(db->aDb[0].pBt, dbNew->aDb[0].pBt);
sqlite_exec(db, "COMMIT", 0, 0, 0);
sqlite_exec(db, "ROLLBACK", 0, 0, 0); /* In case the COMMIT failed */
sqliteResetInternalSchema(db, 0);
}
end_of_vacuum:
if( rc && pParse->zErrMsg==0 && zErrMsg!=0 ){
sqliteErrorMsg(pParse, "unable to vacuum database - %s", zErrMsg);
}
if( safety ) {
sqlite_exec(db, "COMMIT", 0, 0, 0);
sqlite_exec(db, "ROLLBACK", 0, 0, 0); /* In case the COMMIT failed */
sqliteSafetyOn(db);
if( rc && zErrMsg!=0 ){
sqliteSetString(pzErrMsg, "unable to vacuum database - ",
zErrMsg, (char*)0);
}
sqlite_exec(db, "ROLLBACK", 0, 0, 0);
if( dbNew ) sqlite_close(dbNew);
sqliteOsDelete(zTemp);
sqliteFree(zTemp);
sqliteFree(sVac.s1.z);
sqliteFree(sVac.s2.z);
if( zErrMsg ) sqlite_freemem(zErrMsg);
return;
if( rc==SQLITE_ABORT ) rc = SQLITE_ERROR;
return rc;
#endif
}

View File

@ -43,7 +43,7 @@
** in this file for details. If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code.
**
** $Id: vdbe.c,v 1.243 2003/12/06 21:43:56 drh Exp $
** $Id: vdbe.c,v 1.244 2003/12/07 00:24:35 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
@ -4651,6 +4651,19 @@ case OP_SetNext: {
break;
}
/* Opcode: Vacuum * * *
**
** Vacuum the entire database. This opcode will cause other virtual
** machines to be created and run. It may not be called from within
** a transaction.
*/
case OP_Vacuum: {
if( sqliteSafetyOff(db) ) goto abort_due_to_misuse;
rc = sqliteRunVacuum(&p->zErrMsg, db);
if( sqliteSafetyOn(db) ) goto abort_due_to_misuse;
break;
}
/* An other opcode is illegal...
*/
default: {

View File

@ -12,7 +12,7 @@
# focus of this script is testing the ATTACH and DETACH commands
# and related functionality.
#
# $Id: auth.test,v 1.11 2003/11/27 00:49:23 drh Exp $
# $Id: auth.test,v 1.12 2003/12/07 00:24:35 drh Exp $
#
set testdir [file dirname $argv0]
@ -1771,7 +1771,7 @@ do_test auth-2.10 {
}
catchsql {SELECT ROWID,b,c FROM t2}
} {1 {illegal return value (1) from the authorization function - should be SQLITE_OK, SQLITE_IGNORE, or SQLITE_DENY}}
do_test auth-2.11 {
do_test auth-2.11.1 {
proc auth {code arg1 arg2 arg3 arg4} {
if {$code=="SQLITE_READ" && $arg2=="a"} {
return SQLITE_IGNORE
@ -1780,7 +1780,7 @@ do_test auth-2.11 {
}
catchsql {SELECT * FROM t2, t3}
} {0 {{} 2 33 44 55 66 {} 8 9 44 55 66}}
do_test auth-2.11 {
do_test auth-2.11.2 {
proc auth {code arg1 arg2 arg3 arg4} {
if {$code=="SQLITE_READ" && $arg2=="x"} {
return SQLITE_IGNORE

View File

@ -11,7 +11,7 @@
# This file implements regression tests for SQLite library. The
# focus of this file is testing the VACUUM statement.
#
# $Id: vacuum.test,v 1.13 2003/09/27 01:08:38 drh Exp $
# $Id: vacuum.test,v 1.14 2003/12/07 00:24:35 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@ -28,7 +28,6 @@ proc cksum {{db db}} {
# puts $cksum-[file size test.db]
return $cksum
}
do_test vacuum-1.1 {
execsql {
BEGIN;
@ -136,7 +135,6 @@ do_test vacuum-3.1 {
# Ticket #464. Make sure VACUUM works with the sqlite_compile() API.
#
if 0 {
do_test vacuum-4.1 {
db close
set DB [sqlite db test.db]
@ -146,8 +144,33 @@ do_test vacuum-4.1 {
do_test vacuum-4.2 {
sqlite_finalize $VM
} {}
}
catch {db2 close}
# Ticket #515. VACUUM after deleting and recreating the table that
# a view refers to.
#
do_test vacuum-5.1 {
db close
file delete -force test.db
sqlite db test.db
catchsql {
CREATE TABLE Test (TestID int primary key);
INSERT INTO Test VALUES (NULL);
CREATE VIEW viewTest AS SELECT * FROM Test;
BEGIN;
CREATE TEMP TABLE tempTest (TestID int primary key, Test2 int NULL);
INSERT INTO tempTest SELECT TestID, 1 FROM Test;
DROP TABLE Test;
CREATE TABLE Test(TestID int primary key, Test2 int NULL);
INSERT INTO Test SELECT * FROM tempTest;
COMMIT;
VACUUM;
}
} {0 {}}
do_test vacuum-5.2 {
catchsql {
VACUUM;
}
} {0 {}}
# finish_test