Merge recent enhancements from trunk.

FossilOrigin-Name: b17872363b60edab05a5d382a44038aad91e4d9f
This commit is contained in:
drh 2016-08-19 15:41:24 +00:00
commit b684fd5c4a
12 changed files with 229 additions and 140 deletions

View File

@ -1,5 +1,5 @@
C Fix\sa\sSQL\sNULL\shandling\sbug\sin\sthe\svector\sIN\soperator\scode\sgeneration. C Merge\srecent\senhancements\sfrom\strunk.
D 2016-08-18T19:04:57.401 D 2016-08-19T15:41:24.228
F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088 F Makefile.in cfd8fb987cd7a6af046daa87daa146d5aad0e088
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a F Makefile.msc d66d0395c38571aab3804f8db0fa20707ae4609a
@ -331,7 +331,7 @@ F src/btmutex.c bc87dd3b062cc26edfe79918de2200ccb8d41e73
F src/btree.c 2551bd3ecb8b8988fb8b23aabadfb214dbc38e46 F src/btree.c 2551bd3ecb8b8988fb8b23aabadfb214dbc38e46
F src/btree.h 075c45707c0f8f8af118f739f36df8098a08b7da F src/btree.h 075c45707c0f8f8af118f739f36df8098a08b7da
F src/btreeInt.h c18b7d2a3494695133e4e60ee36061d37f45d9a5 F src/btreeInt.h c18b7d2a3494695133e4e60ee36061d37f45d9a5
F src/build.c c38fd92a8d886a5b9397267bf63b76ebad05c4d5 F src/build.c d32cacbb59a403b68e1c2ec962ca31b6f3aad4fc
F src/callback.c 2e76147783386374bf01b227f752c81ec872d730 F src/callback.c 2e76147783386374bf01b227f752c81ec872d730
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7 F src/ctime.c e77f3dc297b4b65c96da78b4ae4272fdfae863d7
@ -346,7 +346,7 @@ F src/global.c c45ea22aff29334f6a9ec549235ac3357c970015
F src/hash.c 55b5fb474100cee0b901edaf203e26c970940f36 F src/hash.c 55b5fb474100cee0b901edaf203e26c970940f36
F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4 F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4
F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da
F src/insert.c d5cd8315c0577e86e17dda9239b45cffa81022c0 F src/insert.c a255eb795cf475e7a0659297144fc80f70eb4e30
F src/legacy.c 75d3023be8f0d2b99d60f905090341a03358c58e F src/legacy.c 75d3023be8f0d2b99d60f905090341a03358c58e
F src/loadext.c dd7a2b77902cc66c22555aef02e1a682554b7aec F src/loadext.c dd7a2b77902cc66c22555aef02e1a682554b7aec
F src/main.c 8dc7adfeace35ee1f4c489b503ee8fe6bc1fa802 F src/main.c 8dc7adfeace35ee1f4c489b503ee8fe6bc1fa802
@ -373,23 +373,23 @@ F src/os_win.c 520f23475f1de530c435d30b67b7b15fe90874b0
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
F src/pager.c 40928c450320da78bb4bd3ae82818f4239e19b7e F src/pager.c 40928c450320da78bb4bd3ae82818f4239e19b7e
F src/pager.h 966d2769e76ae347c8a32c4165faf6e6cb64546d F src/pager.h 966d2769e76ae347c8a32c4165faf6e6cb64546d
F src/parse.y d240b1518fa9c5cb02b222e1fac8e07d950c9ed9 F src/parse.y 3d02d64f333c4371c4f08fbd79a64964fa27b576
F src/pcache.c 5583c8ade4b05075a60ba953ef471d1c1a9c05df F src/pcache.c 5583c8ade4b05075a60ba953ef471d1c1a9c05df
F src/pcache.h 2cedcd8407eb23017d92790b112186886e179490 F src/pcache.h 2cedcd8407eb23017d92790b112186886e179490
F src/pcache1.c 4bb7a6a5300c67d0b033d25adb509c120c03e812 F src/pcache1.c 4bb7a6a5300c67d0b033d25adb509c120c03e812
F src/pragma.c d932ba278654617cdd281f88a790a3185fca7c44 F src/pragma.c d932ba278654617cdd281f88a790a3185fca7c44
F src/pragma.h 64c78a648751b9f4f297276c4eb7507b14b4628c F src/pragma.h 64c78a648751b9f4f297276c4eb7507b14b4628c
F src/prepare.c a668988f324961397305b8352cf6bd87776fb347 F src/prepare.c 0fcf16eaacc90c1059055519a76b75b516a59a88
F src/printf.c a5f0ca08ddede803c241266abb46356ec748ded1 F src/printf.c a5f0ca08ddede803c241266abb46356ec748ded1
F src/random.c ba2679f80ec82c4190062d756f22d0c358180696 F src/random.c ba2679f80ec82c4190062d756f22d0c358180696
F src/resolve.c d67b9a5cc33339256e2088c5a722745fc2ff5219 F src/resolve.c d67b9a5cc33339256e2088c5a722745fc2ff5219
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
F src/select.c 3a5ddce5d3773c42e4bd96ee93e758c22cbc59ae F src/select.c cf90abce32567023c940ecabf5707c4714c61c92
F src/shell.c 79dda477be6c96eba6e952a934957ad36f87acc7 F src/shell.c 79dda477be6c96eba6e952a934957ad36f87acc7
F src/sqlite.h.in 0f7580280d1b009b507d8beec1ff0f197ba0cc99 F src/sqlite.h.in 0f7580280d1b009b507d8beec1ff0f197ba0cc99
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 8648034aa702469afb553231677306cc6492a1ae F src/sqlite3ext.h 8648034aa702469afb553231677306cc6492a1ae
F src/sqliteInt.h f4079dcc83ec216d34531792626206a1a35463bc F src/sqliteInt.h 02b7f4bd9f945fbc46b05f7914b9c43d89517c59
F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247 F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247
F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1 F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1
F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9 F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9
@ -449,8 +449,8 @@ F src/trigger.c 11e20b3b12c847b3b9055594c0f1631266bb53fc
F src/update.c 8179e699dbd45b92934fd02d3d8e3732e8da8802 F src/update.c 8179e699dbd45b92934fd02d3d8e3732e8da8802
F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c
F src/util.c 810ec3f22e2d1b62e66c30fe3621ebdedd23584d F src/util.c 810ec3f22e2d1b62e66c30fe3621ebdedd23584d
F src/vacuum.c f6f10c88f8af5feb6b3632e5e4133e4482819c4f F src/vacuum.c 913970b9d86dd6c2b8063ef1af421880f1464ec3
F src/vdbe.c 9a1c5a2d1980ca6a111ce8649e06ed8c790ee1fc F src/vdbe.c 766d363ef6520c671eef165098a24c0c6ef36d89
F src/vdbe.h 67bc551f7faf04c33493892e4b378aada823ed10 F src/vdbe.h 67bc551f7faf04c33493892e4b378aada823ed10
F src/vdbeInt.h c59381049af5c7751a83456c39b80d1a6fde1f9d F src/vdbeInt.h c59381049af5c7751a83456c39b80d1a6fde1f9d
F src/vdbeapi.c a32d61b7dd05e6890d8fd44d2805f55e2f5ba9f3 F src/vdbeapi.c a32d61b7dd05e6890d8fd44d2805f55e2f5ba9f3
@ -655,7 +655,7 @@ F test/e_select2.test aceb80ab927d46fba5ce7586ebabf23e2bb0604f
F test/e_totalchanges.test b12ee5809d3e63aeb83238dd501a7bca7fd72c10 F test/e_totalchanges.test b12ee5809d3e63aeb83238dd501a7bca7fd72c10
F test/e_update.test f46c2554d915c9197548681e8d8c33a267e84528 F test/e_update.test f46c2554d915c9197548681e8d8c33a267e84528
F test/e_uri.test 25385396082b67fd02ae0038b95a3b3575fe0519 F test/e_uri.test 25385396082b67fd02ae0038b95a3b3575fe0519
F test/e_vacuum.test 120f29ea56bdce4d43279527ece894ab5d1729d3 F test/e_vacuum.test 9e5e47e4059a779c777f47e0f560fc82c99336df
F test/e_wal.test ae9a593207a77d711443ee69ffe081fda9243625 F test/e_wal.test ae9a593207a77d711443ee69ffe081fda9243625
F test/e_walauto.test 248af31e73c98df23476a22bdb815524c9dc3ba8 F test/e_walauto.test 248af31e73c98df23476a22bdb815524c9dc3ba8
F test/e_walckpt.test 28c371a6bb5e5fe7f31679c1df1763a19d19e8a0 F test/e_walckpt.test 28c371a6bb5e5fe7f31679c1df1763a19d19e8a0
@ -1334,6 +1334,7 @@ F test/vacuum.test ce91c39f7f91a4273bf620efad21086b5aa6ef1d
F test/vacuum2.test aa048abee196c16c9ba308465494009057b79f9b F test/vacuum2.test aa048abee196c16c9ba308465494009057b79f9b
F test/vacuum3.test 77ecdd54592b45a0bcb133339f99f1ae0ae94d0d F test/vacuum3.test 77ecdd54592b45a0bcb133339f99f1ae0ae94d0d
F test/vacuum4.test d3f8ecff345f166911568f397d2432c16d2867d9 F test/vacuum4.test d3f8ecff345f166911568f397d2432c16d2867d9
F test/vacuum5.test 99d4a0629252f9ef2fc642caefe92436a744db3a
F test/vacuummem.test e53a3fdca4612a99c515e1afe7934728a2383764 F test/vacuummem.test e53a3fdca4612a99c515e1afe7934728a2383764
F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102 F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102
F test/veryquick.test 57ab846bacf7b90cf4e9a672721ea5c5b669b661 F test/veryquick.test 57ab846bacf7b90cf4e9a672721ea5c5b669b661
@ -1517,7 +1518,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 3b27a5da100037f75a4efc15e0354a6aa94194f8 P 936146b12e27784f15a68fe65732c6d92c3a12f3 083f9e6270fa4faa402b91231271da4f3915c79f
R cb7673431cbb14ee2064b1a631b51486 R b0cf551e47740f01d348f132bf9f7791
U drh U drh
Z e68c3ca3548a25a2524e065bf2465c19 Z 4f11e98ced958b5a8510961832e75233

View File

@ -1 +1 @@
936146b12e27784f15a68fe65732c6d92c3a12f3 b17872363b60edab05a5d382a44038aad91e4d9f

View File

@ -773,7 +773,7 @@ int sqlite3TwoPartName(
return -1; return -1;
} }
}else{ }else{
assert( db->init.iDb==0 || db->init.busy ); assert( db->init.iDb==0 || db->init.busy || (db->flags & SQLITE_Vacuum)!=0);
iDb = db->init.iDb; iDb = db->init.iDb;
*pUnqual = pName1; *pUnqual = pName1;
} }
@ -2011,7 +2011,7 @@ void sqlite3EndTable(
/* Check to see if we need to create an sqlite_sequence table for /* Check to see if we need to create an sqlite_sequence table for
** keeping track of autoincrement keys. ** keeping track of autoincrement keys.
*/ */
if( p->tabFlags & TF_Autoincrement ){ if( (p->tabFlags & TF_Autoincrement)!=0 ){
Db *pDb = &db->aDb[iDb]; Db *pDb = &db->aDb[iDb];
assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
if( pDb->pSchema->pSeqTab==0 ){ if( pDb->pSchema->pSeqTab==0 ){

View File

@ -200,7 +200,9 @@ static int readsTable(Parse *p, int iDb, Table *pTab){
/* /*
** Locate or create an AutoincInfo structure associated with table pTab ** Locate or create an AutoincInfo structure associated with table pTab
** which is in database iDb. Return the register number for the register ** which is in database iDb. Return the register number for the register
** that holds the maximum rowid. ** that holds the maximum rowid. Return zero if pTab is not an AUTOINCREMENT
** table. (Also return zero when doing a VACUUM since we do not want to
** update the AUTOINCREMENT counters during a VACUUM.)
** **
** There is at most one AutoincInfo structure per table even if the ** There is at most one AutoincInfo structure per table even if the
** same table is autoincremented multiple times due to inserts within ** same table is autoincremented multiple times due to inserts within
@ -223,7 +225,9 @@ static int autoIncBegin(
Table *pTab /* The table we are writing to */ Table *pTab /* The table we are writing to */
){ ){
int memId = 0; /* Register holding maximum rowid */ int memId = 0; /* Register holding maximum rowid */
if( pTab->tabFlags & TF_Autoincrement ){ if( (pTab->tabFlags & TF_Autoincrement)!=0
&& (pParse->db->flags & SQLITE_Vacuum)==0
){
Parse *pToplevel = sqlite3ParseToplevel(pParse); Parse *pToplevel = sqlite3ParseToplevel(pParse);
AutoincInfo *pInfo; AutoincInfo *pInfo;

View File

@ -1293,8 +1293,8 @@ cmd ::= DROP INDEX ifexists(E) fullname(X). {sqlite3DropIndex(pParse, X, E);}
// //
%ifndef SQLITE_OMIT_VACUUM %ifndef SQLITE_OMIT_VACUUM
%ifndef SQLITE_OMIT_ATTACH %ifndef SQLITE_OMIT_ATTACH
cmd ::= VACUUM. {sqlite3Vacuum(pParse);} cmd ::= VACUUM. {sqlite3Vacuum(pParse,0);}
cmd ::= VACUUM nm. {sqlite3Vacuum(pParse);} cmd ::= VACUUM nm(X). {sqlite3Vacuum(pParse,&X);}
%endif SQLITE_OMIT_ATTACH %endif SQLITE_OMIT_ATTACH
%endif SQLITE_OMIT_VACUUM %endif SQLITE_OMIT_VACUUM

View File

@ -73,6 +73,7 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
** structures that describe the table, index, or view. ** structures that describe the table, index, or view.
*/ */
int rc; int rc;
u8 saved_iDb = db->init.iDb;
sqlite3_stmt *pStmt; sqlite3_stmt *pStmt;
TESTONLY(int rcp); /* Return code from sqlite3_prepare() */ TESTONLY(int rcp); /* Return code from sqlite3_prepare() */
@ -83,7 +84,8 @@ int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
TESTONLY(rcp = ) sqlite3_prepare(db, argv[2], -1, &pStmt, 0); TESTONLY(rcp = ) sqlite3_prepare(db, argv[2], -1, &pStmt, 0);
rc = db->errCode; rc = db->errCode;
assert( (rc&0xFF)==(rcp&0xFF) ); assert( (rc&0xFF)==(rcp&0xFF) );
db->init.iDb = 0; db->init.iDb = saved_iDb;
assert( saved_iDb==0 || (db->flags & SQLITE_Vacuum)!=0 );
if( SQLITE_OK!=rc ){ if( SQLITE_OK!=rc ){
if( db->init.orphanTrigger ){ if( db->init.orphanTrigger ){
assert( iDb==1 ); assert( iDb==1 );

View File

@ -1441,7 +1441,7 @@ static const char *columnTypeImpl(
zOrigTab = pTab->zName; zOrigTab = pTab->zName;
if( pNC->pParse ){ if( pNC->pParse ){
int iDb = sqlite3SchemaToIndex(pNC->pParse->db, pTab->pSchema); int iDb = sqlite3SchemaToIndex(pNC->pParse->db, pTab->pSchema);
zOrigDb = pNC->pParse->db->aDb[iDb].zName; zOrigDb = pNC->pParse->db->aDb[iDb].zDbSName;
} }
#else #else
if( iCol<0 ){ if( iCol<0 ){

View File

@ -3703,8 +3703,8 @@ Table *sqlite3LocateTableItem(Parse*,u32 flags,struct SrcList_item *);
Index *sqlite3FindIndex(sqlite3*,const char*, const char*); Index *sqlite3FindIndex(sqlite3*,const char*, const char*);
void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*); void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*);
void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*); void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*);
void sqlite3Vacuum(Parse*); void sqlite3Vacuum(Parse*,Token*);
int sqlite3RunVacuum(char**, sqlite3*); int sqlite3RunVacuum(char**, sqlite3*, int);
char *sqlite3NameFromToken(sqlite3*, Token*); char *sqlite3NameFromToken(sqlite3*, Token*);
int sqlite3ExprCompare(Expr*, Expr*, int); int sqlite3ExprCompare(Expr*, Expr*, int);
int sqlite3ExprListCompare(ExprList*, ExprList*, int); int sqlite3ExprListCompare(ExprList*, ExprList*, int);

View File

@ -18,57 +18,52 @@
#include "vdbeInt.h" #include "vdbeInt.h"
#if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH) #if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH)
/*
** Finalize a prepared statement. If there was an error, store the
** text of the error message in *pzErrMsg. Return the result code.
*/
static int vacuumFinalize(sqlite3 *db, sqlite3_stmt *pStmt, char **pzErrMsg){
int rc;
rc = sqlite3VdbeFinalize((Vdbe*)pStmt);
if( rc ){
sqlite3SetString(pzErrMsg, db, sqlite3_errmsg(db));
}
return rc;
}
/* /*
** Execute zSql on database db. Return an error code. ** Execute zSql on database db.
**
** If zSql returns rows, then each row will have exactly one
** column. (This will only happen if zSql begins with "SELECT".)
** Take each row of result and call execSql() again recursively.
**
** The execSqlF() routine does the same thing, except it accepts
** a format string as its third argument
*/ */
static int execSql(sqlite3 *db, char **pzErrMsg, const char *zSql){ static int execSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
sqlite3_stmt *pStmt;
VVA_ONLY( int rc; )
if( !zSql ){
return SQLITE_NOMEM_BKPT;
}
if( SQLITE_OK!=sqlite3_prepare(db, zSql, -1, &pStmt, 0) ){
sqlite3SetString(pzErrMsg, db, sqlite3_errmsg(db));
return sqlite3_errcode(db);
}
VVA_ONLY( rc = ) sqlite3_step(pStmt);
assert( rc!=SQLITE_ROW || (db->flags&SQLITE_CountRows) );
return vacuumFinalize(db, pStmt, pzErrMsg);
}
/*
** Execute zSql on database db. The statement returns exactly
** one column. Execute this as SQL on the same database.
*/
static int execExecSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
sqlite3_stmt *pStmt; sqlite3_stmt *pStmt;
int rc; int rc;
rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); /* printf("SQL: [%s]\n", zSql); fflush(stdout); */
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
if( rc!=SQLITE_OK ) return rc; if( rc!=SQLITE_OK ) return rc;
while( SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){
while( SQLITE_ROW==sqlite3_step(pStmt) ){ const char *zSubSql = (const char*)sqlite3_column_text(pStmt,0);
rc = execSql(db, pzErrMsg, (char*)sqlite3_column_text(pStmt, 0)); assert( sqlite3_strnicmp(zSql,"SELECT",6)==0 );
if( rc!=SQLITE_OK ){ if( zSubSql ){
vacuumFinalize(db, pStmt, pzErrMsg); assert( zSubSql[0]!='S' );
return rc; rc = execSql(db, pzErrMsg, zSubSql);
if( rc!=SQLITE_OK ) break;
} }
} }
assert( rc!=SQLITE_ROW );
return vacuumFinalize(db, pStmt, pzErrMsg); if( rc==SQLITE_DONE ) rc = SQLITE_OK;
if( rc ){
sqlite3SetString(pzErrMsg, db, sqlite3_errmsg(db));
}
(void)sqlite3_finalize(pStmt);
return rc;
}
static int execSqlF(sqlite3 *db, char **pzErrMsg, const char *zSql, ...){
char *z;
va_list ap;
int rc;
va_start(ap, zSql);
z = sqlite3VMPrintf(db, zSql, ap);
va_end(ap);
if( z==0 ) return SQLITE_NOMEM;
rc = execSql(db, pzErrMsg, z);
sqlite3DbFree(db, z);
return rc;
} }
/* /*
@ -101,11 +96,12 @@ static int execExecSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
** transient would cause the database file to appear to be deleted ** transient would cause the database file to appear to be deleted
** following reboot. ** following reboot.
*/ */
void sqlite3Vacuum(Parse *pParse){ void sqlite3Vacuum(Parse *pParse, Token *pNm){
Vdbe *v = sqlite3GetVdbe(pParse); Vdbe *v = sqlite3GetVdbe(pParse);
if( v ){ int iDb = pNm ? sqlite3TwoPartName(pParse, pNm, pNm, &pNm) : 0;
sqlite3VdbeAddOp2(v, OP_Vacuum, 0, 0); if( v && (iDb>=2 || iDb==0) ){
sqlite3VdbeUsesBtree(v, 0); sqlite3VdbeAddOp1(v, OP_Vacuum, iDb);
sqlite3VdbeUsesBtree(v, iDb);
} }
return; return;
} }
@ -113,11 +109,10 @@ void sqlite3Vacuum(Parse *pParse){
/* /*
** This routine implements the OP_Vacuum opcode of the VDBE. ** This routine implements the OP_Vacuum opcode of the VDBE.
*/ */
int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
int rc = SQLITE_OK; /* Return code from service routines */ int rc = SQLITE_OK; /* Return code from service routines */
Btree *pMain; /* The database being vacuumed */ Btree *pMain; /* The database being vacuumed */
Btree *pTemp; /* The temporary database we vacuum into */ Btree *pTemp; /* The temporary database we vacuum into */
char *zSql = 0; /* SQL statements */
int saved_flags; /* Saved value of the db->flags */ int saved_flags; /* Saved value of the db->flags */
int saved_nChange; /* Saved value of db->nChange */ int saved_nChange; /* Saved value of db->nChange */
int saved_nTotalChange; /* Saved value of db->nTotalChange */ int saved_nTotalChange; /* Saved value of db->nTotalChange */
@ -126,6 +121,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
int isMemDb; /* True if vacuuming a :memory: database */ int isMemDb; /* True if vacuuming a :memory: database */
int nRes; /* Bytes of reserved space at the end of each page */ int nRes; /* Bytes of reserved space at the end of each page */
int nDb; /* Number of attached databases */ int nDb; /* Number of attached databases */
const char *zDbMain; /* Schema name of database to vacuum */
if( !db->autoCommit ){ if( !db->autoCommit ){
sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction"); sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction");
@ -143,11 +139,13 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
saved_nChange = db->nChange; saved_nChange = db->nChange;
saved_nTotalChange = db->nTotalChange; saved_nTotalChange = db->nTotalChange;
saved_mTrace = db->mTrace; saved_mTrace = db->mTrace;
db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_PreferBuiltin; db->flags |= (SQLITE_WriteSchema | SQLITE_IgnoreChecks
db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder); | SQLITE_PreferBuiltin | SQLITE_Vacuum);
db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder | SQLITE_CountRows);
db->mTrace = 0; db->mTrace = 0;
pMain = db->aDb[0].pBt; zDbMain = db->aDb[iDb].zDbSName;
pMain = db->aDb[iDb].pBt;
isMemDb = sqlite3PagerIsMemdb(sqlite3BtreePager(pMain)); isMemDb = sqlite3PagerIsMemdb(sqlite3BtreePager(pMain));
/* Attach the temporary database as 'vacuum_db'. The synchronous pragma /* Attach the temporary database as 'vacuum_db'. The synchronous pragma
@ -165,18 +163,12 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
** to write the journal header file. ** to write the journal header file.
*/ */
nDb = db->nDb; nDb = db->nDb;
if( sqlite3TempInMemory(db) ){ rc = execSql(db, pzErrMsg, "ATTACH''AS vacuum_db");
zSql = "ATTACH ':memory:' AS vacuum_db;";
}else{
zSql = "ATTACH '' AS vacuum_db;";
}
rc = execSql(db, pzErrMsg, zSql);
if( db->nDb>nDb ){
pDb = &db->aDb[db->nDb-1];
assert( strcmp(pDb->zDbSName,"vacuum_db")==0 );
}
if( rc!=SQLITE_OK ) goto end_of_vacuum; if( rc!=SQLITE_OK ) goto end_of_vacuum;
pTemp = db->aDb[db->nDb-1].pBt; assert( (db->nDb-1)==nDb );
pDb = &db->aDb[nDb];
assert( strcmp(pDb->zDbSName,"vacuum_db")==0 );
pTemp = pDb->pBt;
/* The call to execSql() to attach the temp database has left the file /* The call to execSql() to attach the temp database has left the file
** locked (as there was more than one active statement when the transaction ** locked (as there was more than one active statement when the transaction
@ -197,16 +189,15 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
} }
#endif #endif
sqlite3BtreeSetCacheSize(pTemp, db->aDb[0].pSchema->cache_size); sqlite3BtreeSetCacheSize(pTemp, db->aDb[iDb].pSchema->cache_size);
sqlite3BtreeSetSpillSize(pTemp, sqlite3BtreeSetSpillSize(pMain,0)); sqlite3BtreeSetSpillSize(pTemp, sqlite3BtreeSetSpillSize(pMain,0));
rc = execSql(db, pzErrMsg, "PRAGMA vacuum_db.synchronous=OFF"); sqlite3BtreeSetPagerFlags(pTemp, PAGER_SYNCHRONOUS_OFF);
if( rc!=SQLITE_OK ) goto end_of_vacuum;
/* Begin a transaction and take an exclusive lock on the main database /* Begin a transaction and take an exclusive lock on the main database
** file. This is done before the sqlite3BtreeGetPageSize(pMain) call below, ** file. This is done before the sqlite3BtreeGetPageSize(pMain) call below,
** to ensure that we do not try to change the page-size on a WAL database. ** to ensure that we do not try to change the page-size on a WAL database.
*/ */
rc = execSql(db, pzErrMsg, "BEGIN;"); rc = execSql(db, pzErrMsg, "BEGIN");
if( rc!=SQLITE_OK ) goto end_of_vacuum; if( rc!=SQLITE_OK ) goto end_of_vacuum;
rc = sqlite3BtreeBeginTrans(pMain, 2); rc = sqlite3BtreeBeginTrans(pMain, 2);
if( rc!=SQLITE_OK ) goto end_of_vacuum; if( rc!=SQLITE_OK ) goto end_of_vacuum;
@ -233,64 +224,48 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
/* Query the schema of the main database. Create a mirror schema /* Query the schema of the main database. Create a mirror schema
** in the temporary database. ** in the temporary database.
*/ */
rc = execExecSql(db, pzErrMsg, db->init.iDb = nDb; /* force new CREATE statements into vacuum_db */
"SELECT 'CREATE TABLE vacuum_db.' || substr(sql,14) " rc = execSqlF(db, pzErrMsg,
" FROM sqlite_master WHERE type='table' AND name!='sqlite_sequence'" "SELECT sql FROM \"%w\".sqlite_master"
" AND coalesce(rootpage,1)>0" " WHERE type='table'AND name<>'sqlite_sequence'"
" AND coalesce(rootpage,1)>0",
zDbMain
); );
if( rc!=SQLITE_OK ) goto end_of_vacuum; if( rc!=SQLITE_OK ) goto end_of_vacuum;
rc = execExecSql(db, pzErrMsg, rc = execSqlF(db, pzErrMsg,
"SELECT 'CREATE INDEX vacuum_db.' || substr(sql,14)" "SELECT sql FROM \"%w\".sqlite_master"
" FROM sqlite_master WHERE sql LIKE 'CREATE INDEX %' "); " WHERE type='index' AND length(sql)>10",
if( rc!=SQLITE_OK ) goto end_of_vacuum; zDbMain
rc = execExecSql(db, pzErrMsg, );
"SELECT 'CREATE UNIQUE INDEX vacuum_db.' || substr(sql,21) "
" FROM sqlite_master WHERE sql LIKE 'CREATE UNIQUE INDEX %'");
if( rc!=SQLITE_OK ) goto end_of_vacuum; if( rc!=SQLITE_OK ) goto end_of_vacuum;
db->init.iDb = 0;
/* Loop through the tables in the main database. For each, do /* Loop through the tables in the main database. For each, do
** an "INSERT INTO vacuum_db.xxx SELECT * FROM main.xxx;" to copy ** an "INSERT INTO vacuum_db.xxx SELECT * FROM main.xxx;" to copy
** the contents to the temporary database. ** the contents to the temporary database.
*/ */
assert( (db->flags & SQLITE_Vacuum)==0 ); rc = execSqlF(db, pzErrMsg,
db->flags |= SQLITE_Vacuum; "SELECT'INSERT INTO vacuum_db.'||quote(name)"
rc = execExecSql(db, pzErrMsg, "||' SELECT*FROM\"%w\".'||quote(name)"
"SELECT 'INSERT INTO vacuum_db.' || quote(name) " "FROM vacuum_db.sqlite_master "
"|| ' SELECT * FROM main.' || quote(name) || ';'" "WHERE type='table'AND coalesce(rootpage,1)>0",
"FROM main.sqlite_master " zDbMain
"WHERE type = 'table' AND name!='sqlite_sequence' "
" AND coalesce(rootpage,1)>0"
); );
assert( (db->flags & SQLITE_Vacuum)!=0 ); assert( (db->flags & SQLITE_Vacuum)!=0 );
db->flags &= ~SQLITE_Vacuum; db->flags &= ~SQLITE_Vacuum;
if( rc!=SQLITE_OK ) goto end_of_vacuum; if( rc!=SQLITE_OK ) goto end_of_vacuum;
/* Copy over the sequence table
*/
rc = execExecSql(db, pzErrMsg,
"SELECT 'DELETE FROM vacuum_db.' || quote(name) || ';' "
"FROM vacuum_db.sqlite_master WHERE name='sqlite_sequence' "
);
if( rc!=SQLITE_OK ) goto end_of_vacuum;
rc = execExecSql(db, pzErrMsg,
"SELECT 'INSERT INTO vacuum_db.' || quote(name) "
"|| ' SELECT * FROM main.' || quote(name) || ';' "
"FROM vacuum_db.sqlite_master WHERE name=='sqlite_sequence';"
);
if( rc!=SQLITE_OK ) goto end_of_vacuum;
/* Copy the triggers, views, and virtual tables from the main database /* Copy the triggers, views, and virtual tables from the main database
** over to the temporary database. None of these objects has any ** over to the temporary database. None of these objects has any
** associated storage, so all we have to do is copy their entries ** associated storage, so all we have to do is copy their entries
** from the SQLITE_MASTER table. ** from the SQLITE_MASTER table.
*/ */
rc = execSql(db, pzErrMsg, rc = execSqlF(db, pzErrMsg,
"INSERT INTO vacuum_db.sqlite_master " "INSERT INTO vacuum_db.sqlite_master"
" SELECT type, name, tbl_name, rootpage, sql" " SELECT*FROM \"%w\".sqlite_master"
" FROM main.sqlite_master" " WHERE type IN('view','trigger')"
" WHERE type='view' OR type='trigger'" " OR(type='table'AND rootpage=0)",
" OR (type='table' AND rootpage=0)" zDbMain
); );
if( rc ) goto end_of_vacuum; if( rc ) goto end_of_vacuum;
@ -344,6 +319,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
end_of_vacuum: end_of_vacuum:
/* Restore the original value of db->flags */ /* Restore the original value of db->flags */
db->init.iDb = 0;
db->flags = saved_flags; db->flags = saved_flags;
db->nChange = saved_nChange; db->nChange = saved_nChange;
db->nTotalChange = saved_nTotalChange; db->nTotalChange = saved_nTotalChange;

View File

@ -6298,15 +6298,14 @@ case OP_JournalMode: { /* out2 */
#endif /* SQLITE_OMIT_PRAGMA */ #endif /* SQLITE_OMIT_PRAGMA */
#if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH) #if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH)
/* Opcode: Vacuum * * * * * /* Opcode: Vacuum P1 * * * *
** **
** Vacuum the entire database. This opcode will cause other virtual ** Vacuum the entire database P1. P1 is 0 for "main", and 2 or more
** machines to be created and run. It may not be called from within ** for an attached database. The "temp" database may not be vacuumed.
** a transaction.
*/ */
case OP_Vacuum: { case OP_Vacuum: {
assert( p->readOnly==0 ); assert( p->readOnly==0 );
rc = sqlite3RunVacuum(&p->zErrMsg, db); rc = sqlite3RunVacuum(&p->zErrMsg, db, pOp->p1);
if( rc ) goto abort_due_to_error; if( rc ) goto abort_due_to_error;
break; break;
} }

View File

@ -202,12 +202,8 @@ do_execsql_test e_vacuum-2.1.1 {
} {} } {}
set original_size [file size test.db2] set original_size [file size test.db2]
# Try everything we can think of to get the aux database vacuumed: # Vacuuming the main database does not affect aux
do_execsql_test e_vacuum-2.1.3 { VACUUM } {} do_execsql_test e_vacuum-2.1.3 { VACUUM } {}
do_execsql_test e_vacuum-2.1.4 { VACUUM aux } {}
do_execsql_test e_vacuum-2.1.5 { VACUUM 'test.db2' } {}
# Despite our efforts, space in the aux database has not been reclaimed:
do_test e_vacuum-2.1.6 { expr {[file size test.db2]==$::original_size} } 1 do_test e_vacuum-2.1.6 { expr {[file size test.db2]==$::original_size} } 1
# EVIDENCE-OF: R-17495-17419 The VACUUM command may change the ROWIDs of # EVIDENCE-OF: R-17495-17419 The VACUUM command may change the ROWIDs of

111
test/vacuum5.test Normal file
View File

@ -0,0 +1,111 @@
# 2016-08-19
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# This file implements a test for VACUUM on attached databases.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# If the VACUUM statement is disabled in the current build, skip all
# the tests in this file.
#
ifcapable !vacuum {
finish_test
return
}
forcedelete test2.db test3.db
do_execsql_test vacuum5-1.1 {
CREATE TABLE main.t1(a,b);
WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<1000)
INSERT INTO t1(a,b) SELECT x, randomblob(1000) FROM c;
CREATE TEMP TABLE ttemp(x,y);
INSERT INTO ttemp SELECT * FROM t1;
ATTACH 'test2.db' AS x2;
ATTACH 'test3.db' AS x3;
CREATE TABLE x2.t2(c,d);
INSERT INTO t2 SELECT * FROM t1;
CREATE TABLE x3.t3(e,f);
INSERT INTO t3 SELECT * FROM t1;
DELETE FROM t1 WHERE (rowid%3)!=0;
DELETE FROM t2 WHERE (rowid%4)!=0;
DELETE FROM t3 WHERE (rowid%5)!=0;
PRAGMA main.integrity_check;
PRAGMA x2.integrity_check;
PRAGMA x3.integrity_check;
} {ok ok ok}
set size1 [file size test.db]
set size2 [file size test2.db]
set size3 [file size test3.db]
do_execsql_test vacuum5-1.2.1 {
VACUUM main;
} {}
do_test vacuum5-1.2.2 {
expr {[file size test.db]<$size1}
} {1}
do_test vacuum5-1.2.3 {
file size test2.db
} $size2
do_test vacuum5-1.2.4 {
file size test3.db
} $size3
set size1 [file size test.db]
do_execsql_test vacuum-1.2.5 {
DELETE FROM t1;
PRAGMA main.integrity_check;
} {ok}
do_execsql_test vacuum5-1.3.1 {
VACUUM x2;
} {}
do_test vacuum5-1.3.2 {
file size test.db
} $size1
do_test vacuum5-1.3.3 {
expr {[file size test2.db]<$size2}
} 1
do_test vacuum5-1.3.4 {
file size test3.db
} $size3
set size2 [file size test2.db]
do_execsql_test vacuum-1.3.5 {
DELETE FROM t2;
PRAGMA x2.integrity_check;
} {ok}
do_execsql_test vacuum5-1.4.1 {
VACUUM x3;
} {}
do_test vacuum5-1.3.2 {
file size test.db
} $size1
do_test vacuum5-1.3.3 {
file size test2.db
} $size2
do_test vacuum5-1.3.4 {
expr {[file size test3.db]<$size3}
} 1
# VACUUM is a no-op on the TEMP table
#
set sizeTemp [db one {PRAGMA temp.page_count}]
do_execsql_test vacuum5-1.4.1 {
VACUUM temp;
} {}
do_execsql_test vacuum5-1.4.2 {
PRAGMA temp.page_count;
} $sizeTemp
do_catchsql_test vacuum5-2.0 {
VACUUM olaf;
} {1 {unknown database olaf}}