1c11d4f93Sdrh /*
2c11d4f93Sdrh ** 2003 April 6
3c11d4f93Sdrh **
4c11d4f93Sdrh ** The author disclaims copyright to this source code. In place of
5c11d4f93Sdrh ** a legal notice, here is a blessing:
6c11d4f93Sdrh **
7c11d4f93Sdrh ** May you do good and not evil.
8c11d4f93Sdrh ** May you find forgiveness for yourself and forgive others.
9c11d4f93Sdrh ** May you share freely, never taking more than you give.
10c11d4f93Sdrh **
11c11d4f93Sdrh *************************************************************************
12c11d4f93Sdrh ** This file contains code used to implement the VACUUM command.
13c11d4f93Sdrh **
14c11d4f93Sdrh ** Most of the code in this file may be omitted by defining the
15c11d4f93Sdrh ** SQLITE_OMIT_VACUUM macro.
16c11d4f93Sdrh */
17c11d4f93Sdrh #include "sqliteInt.h"
18f4208043Sdanielk1977 #include "vdbeInt.h"
19c11d4f93Sdrh
20fdbcdee5Sdrh #if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH)
21cda455b7Sdrh
22cda455b7Sdrh /*
239ef5e770Sdrh ** Execute zSql on database db.
249ef5e770Sdrh **
256a754dc7Sdrh ** If zSql returns rows, then each row will have exactly one
266a754dc7Sdrh ** column. (This will only happen if zSql begins with "SELECT".)
276a754dc7Sdrh ** Take each row of result and call execSql() again recursively.
289ef5e770Sdrh **
299ef5e770Sdrh ** The execSqlF() routine does the same thing, except it accepts
309ef5e770Sdrh ** a format string as its third argument
313df6b257Sdanielk1977 */
execSql(sqlite3 * db,char ** pzErrMsg,const char * zSql)32cda455b7Sdrh static int execSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
333df6b257Sdanielk1977 sqlite3_stmt *pStmt;
343df6b257Sdanielk1977 int rc;
353df6b257Sdanielk1977
369ef5e770Sdrh /* printf("SQL: [%s]\n", zSql); fflush(stdout); */
379ef5e770Sdrh rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
383df6b257Sdanielk1977 if( rc!=SQLITE_OK ) return rc;
396a754dc7Sdrh while( SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){
409ef5e770Sdrh const char *zSubSql = (const char*)sqlite3_column_text(pStmt,0);
416a754dc7Sdrh assert( sqlite3_strnicmp(zSql,"SELECT",6)==0 );
4234b27edcSdrh /* The secondary SQL must be one of CREATE TABLE, CREATE INDEX,
4334b27edcSdrh ** or INSERT. Historically there have been attacks that first
441e32bed3Sdrh ** corrupt the sqlite_schema.sql field with other kinds of statements
4534b27edcSdrh ** then run VACUUM to get those statements to execute at inappropriate
4634b27edcSdrh ** times. */
4734b27edcSdrh if( zSubSql
4834b27edcSdrh && (strncmp(zSubSql,"CRE",3)==0 || strncmp(zSubSql,"INS",3)==0)
4934b27edcSdrh ){
509ef5e770Sdrh rc = execSql(db, pzErrMsg, zSubSql);
519ef5e770Sdrh if( rc!=SQLITE_OK ) break;
529ef5e770Sdrh }
539ef5e770Sdrh }
546a754dc7Sdrh assert( rc!=SQLITE_ROW );
556a754dc7Sdrh if( rc==SQLITE_DONE ) rc = SQLITE_OK;
569ef5e770Sdrh if( rc ){
579ef5e770Sdrh sqlite3SetString(pzErrMsg, db, sqlite3_errmsg(db));
589ef5e770Sdrh }
599ef5e770Sdrh (void)sqlite3_finalize(pStmt);
603df6b257Sdanielk1977 return rc;
613df6b257Sdanielk1977 }
execSqlF(sqlite3 * db,char ** pzErrMsg,const char * zSql,...)629ef5e770Sdrh static int execSqlF(sqlite3 *db, char **pzErrMsg, const char *zSql, ...){
639ef5e770Sdrh char *z;
649ef5e770Sdrh va_list ap;
659ef5e770Sdrh int rc;
669ef5e770Sdrh va_start(ap, zSql);
679ef5e770Sdrh z = sqlite3VMPrintf(db, zSql, ap);
689ef5e770Sdrh va_end(ap);
699ef5e770Sdrh if( z==0 ) return SQLITE_NOMEM;
709ef5e770Sdrh rc = execSql(db, pzErrMsg, z);
719ef5e770Sdrh sqlite3DbFree(db, z);
729ef5e770Sdrh return rc;
733df6b257Sdanielk1977 }
743df6b257Sdanielk1977
75c11d4f93Sdrh /*
768b8d28ddSdrh ** The VACUUM command is used to clean up the database,
77c11d4f93Sdrh ** collapse free space, etc. It is modelled after the VACUUM command
788b8d28ddSdrh ** in PostgreSQL. The VACUUM command works as follows:
79c11d4f93Sdrh **
808b8d28ddSdrh ** (1) Create a new transient database file
818b8d28ddSdrh ** (2) Copy all content from the database being vacuumed into
828b8d28ddSdrh ** the new transient database file
838b8d28ddSdrh ** (3) Copy content from the transient database back into the
848b8d28ddSdrh ** original database.
858b8d28ddSdrh **
868b8d28ddSdrh ** The transient database requires temporary disk space approximately
878b8d28ddSdrh ** equal to the size of the original database. The copy operation of
888b8d28ddSdrh ** step (3) requires additional temporary disk space approximately equal
898b8d28ddSdrh ** to the size of the original database for the rollback journal.
908b8d28ddSdrh ** Hence, temporary disk space that is approximately 2x the size of the
9160ec914cSpeter.d.reid ** original database is required. Every page of the database is written
928b8d28ddSdrh ** approximately 3 times: Once for step (2) and twice for step (3).
938b8d28ddSdrh ** Two writes per page are required in step (3) because the original
948b8d28ddSdrh ** database content must be written into the rollback journal prior to
958b8d28ddSdrh ** overwriting the database with the vacuumed content.
968b8d28ddSdrh **
978b8d28ddSdrh ** Only 1x temporary space and only 1x writes would be required if
9886a11b8aSdrh ** the copy of step (3) were replaced by deleting the original database
998b8d28ddSdrh ** and renaming the transient database as the original. But that will
1008b8d28ddSdrh ** not work if other processes are attached to the original database.
1018b8d28ddSdrh ** And a power loss in between deleting the original and renaming the
1028b8d28ddSdrh ** transient would cause the database file to appear to be deleted
1038b8d28ddSdrh ** following reboot.
104c11d4f93Sdrh */
sqlite3Vacuum(Parse * pParse,Token * pNm,Expr * pInto)1052f6239edSdrh void sqlite3Vacuum(Parse *pParse, Token *pNm, Expr *pInto){
1064adee20fSdanielk1977 Vdbe *v = sqlite3GetVdbe(pParse);
107eeea412aSdrh int iDb = 0;
1082f6239edSdrh if( v==0 ) goto build_vacuum_end;
10929e78006Sdrh if( pParse->nErr ) goto build_vacuum_end;
110eeea412aSdrh if( pNm ){
111eeea412aSdrh #ifndef SQLITE_BUG_COMPATIBLE_20160819
112eeea412aSdrh /* Default behavior: Report an error if the argument to VACUUM is
113eeea412aSdrh ** not recognized */
114eeea412aSdrh iDb = sqlite3TwoPartName(pParse, pNm, pNm, &pNm);
1152f6239edSdrh if( iDb<0 ) goto build_vacuum_end;
116eeea412aSdrh #else
117eeea412aSdrh /* When SQLITE_BUG_COMPATIBLE_20160819 is defined, unrecognized arguments
118eeea412aSdrh ** to VACUUM are silently ignored. This is a back-out of a bug fix that
119eeea412aSdrh ** occurred on 2016-08-19 (https://www.sqlite.org/src/info/083f9e6270).
120eeea412aSdrh ** The buggy behavior is required for binary compatibility with some
121eeea412aSdrh ** legacy applications. */
122eeea412aSdrh iDb = sqlite3FindDb(pParse->db, pNm);
123eeea412aSdrh if( iDb<0 ) iDb = 0;
124eeea412aSdrh #endif
125eeea412aSdrh }
126eeea412aSdrh if( iDb!=1 ){
1272f6239edSdrh int iIntoReg = 0;
128ee751fabSdrh if( pInto && sqlite3ResolveSelfReference(pParse,0,0,pInto,0)==0 ){
1292f6239edSdrh iIntoReg = ++pParse->nMem;
1302f6239edSdrh sqlite3ExprCode(pParse, pInto, iIntoReg);
1312f6239edSdrh }
1322f6239edSdrh sqlite3VdbeAddOp2(v, OP_Vacuum, iDb, iIntoReg);
1339ef5e770Sdrh sqlite3VdbeUsesBtree(v, iDb);
134b0b7db91Sdrh }
1352f6239edSdrh build_vacuum_end:
1362f6239edSdrh sqlite3ExprDelete(pParse->db, pInto);
1376f8c91caSdrh return;
1386f8c91caSdrh }
1396f8c91caSdrh
1406f8c91caSdrh /*
1416f8c91caSdrh ** This routine implements the OP_Vacuum opcode of the VDBE.
1426f8c91caSdrh */
sqlite3RunVacuum(char ** pzErrMsg,sqlite3 * db,int iDb,sqlite3_value * pOut)14367752575Sdrh SQLITE_NOINLINE int sqlite3RunVacuum(
1442f6239edSdrh char **pzErrMsg, /* Write error message here */
1452f6239edSdrh sqlite3 *db, /* Database connection */
1462f6239edSdrh int iDb, /* Which attached DB to vacuum */
147d0f820a7Sdrh sqlite3_value *pOut /* Write results here, if not NULL. VACUUM INTO */
1482f6239edSdrh ){
1493df6b257Sdanielk1977 int rc = SQLITE_OK; /* Return code from service routines */
150f2a611c9Sdrh Btree *pMain; /* The database being vacuumed */
1510058d844Sdrh Btree *pTemp; /* The temporary database we vacuum into */
15270d5dfbaSdrh u32 saved_mDbFlags; /* Saved value of db->mDbFlags */
15370d5dfbaSdrh u64 saved_flags; /* Saved value of db->flags */
1542c718873Sdan i64 saved_nChange; /* Saved value of db->nChange */
1552c718873Sdan i64 saved_nTotalChange; /* Saved value of db->nTotalChange */
156d0f820a7Sdrh u32 saved_openFlags; /* Saved value of db->openFlags */
1573d2a529dSdrh u8 saved_mTrace; /* Saved trace settings */
1580203bde9Sdanielk1977 Db *pDb = 0; /* Database to detach at end of vacuum */
15943996e85Sdanielk1977 int isMemDb; /* True if vacuuming a :memory: database */
160acd07818Sdrh int nRes; /* Bytes of reserved space at the end of each page */
161acd07818Sdrh int nDb; /* Number of attached databases */
1629ef5e770Sdrh const char *zDbMain; /* Schema name of database to vacuum */
1632f6239edSdrh const char *zOut; /* Name of output file */
164*80b30f99Sdan u32 pgflags = PAGER_SYNCHRONOUS_OFF; /* sync flags for output db */
1653a3f38e0Sdanielk1977
166663d56d4Sdrh if( !db->autoCommit ){
167663d56d4Sdrh sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction");
168eacc8816Sdrh return SQLITE_ERROR; /* IMP: R-12218-18073 */
169663d56d4Sdrh }
1704f7d3a5fSdrh if( db->nVdbeActive>1 ){
171099d1470Sdan sqlite3SetString(pzErrMsg, db,"cannot VACUUM - SQL statements in progress");
172eacc8816Sdrh return SQLITE_ERROR; /* IMP: R-15610-35227 */
173099d1470Sdan }
174d0f820a7Sdrh saved_openFlags = db->openFlags;
1752f6239edSdrh if( pOut ){
1762f6239edSdrh if( sqlite3_value_type(pOut)!=SQLITE_TEXT ){
1772f6239edSdrh sqlite3SetString(pzErrMsg, db, "non-text filename");
1782f6239edSdrh return SQLITE_ERROR;
1792f6239edSdrh }
1802f6239edSdrh zOut = (const char*)sqlite3_value_text(pOut);
181d0f820a7Sdrh db->openFlags &= ~SQLITE_OPEN_READONLY;
182d0f820a7Sdrh db->openFlags |= SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE;
1832f6239edSdrh }else{
1842f6239edSdrh zOut = "";
1852f6239edSdrh }
186663d56d4Sdrh
18775cbd984Sdan /* Save the current value of the database flags so that it can be
18875cbd984Sdan ** restored before returning. Then set the writable-schema flag, and
18975cbd984Sdan ** disable CHECK and foreign key constraints. */
1900cd2d4c9Sdrh saved_flags = db->flags;
19167752575Sdrh saved_mDbFlags = db->mDbFlags;
192e63b2c21Sdrh saved_nChange = db->nChange;
193e63b2c21Sdrh saved_nTotalChange = db->nTotalChange;
1943d2a529dSdrh saved_mTrace = db->mTrace;
1958257aa8dSdrh db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks;
19667752575Sdrh db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum;
197d5b44d60Sdrh db->flags &= ~(u64)(SQLITE_ForeignKeys | SQLITE_ReverseOrder
1980f0d3ddfSdrh | SQLITE_Defensive | SQLITE_CountRows);
1991637a517Sdrh db->mTrace = 0;
20045a304eeSdrh
2019ef5e770Sdrh zDbMain = db->aDb[iDb].zDbSName;
2029ef5e770Sdrh pMain = db->aDb[iDb].pBt;
20343996e85Sdanielk1977 isMemDb = sqlite3PagerIsMemdb(sqlite3BtreePager(pMain));
2043df6b257Sdanielk1977
20546c43eddSdanielk1977 /* Attach the temporary database as 'vacuum_db'. The synchronous pragma
20646c43eddSdanielk1977 ** can be set to 'off' for this file, as it is not recovered if a crash
20746c43eddSdanielk1977 ** occurs anyway. The integrity of the database is maintained by a
20846c43eddSdanielk1977 ** (possibly synchronous) transaction opened on the main database before
20946c43eddSdanielk1977 ** sqlite3BtreeCopyFile() is called.
21046c43eddSdanielk1977 **
21146c43eddSdanielk1977 ** An optimisation would be to use a non-journaled pager.
21246979886Sdrh ** (Later:) I tried setting "PRAGMA vacuum_db.journal_mode=OFF" but
21346979886Sdrh ** that actually made the VACUUM run slower. Very little journalling
21446979886Sdrh ** actually occurs when doing a vacuum since the vacuum_db is initially
21546979886Sdrh ** empty. Only the journal header is written. Apparently it takes more
21646979886Sdrh ** time to parse and run the PRAGMA to turn journalling off than it does
21746979886Sdrh ** to write the journal header file.
21846c43eddSdanielk1977 */
219acd07818Sdrh nDb = db->nDb;
2202f6239edSdrh rc = execSqlF(db, pzErrMsg, "ATTACH %Q AS vacuum_db", zOut);
221d0f820a7Sdrh db->openFlags = saved_openFlags;
222acd07818Sdrh if( rc!=SQLITE_OK ) goto end_of_vacuum;
2239ef5e770Sdrh assert( (db->nDb-1)==nDb );
2249ef5e770Sdrh pDb = &db->aDb[nDb];
2259ef5e770Sdrh assert( strcmp(pDb->zDbSName,"vacuum_db")==0 );
2269ef5e770Sdrh pTemp = pDb->pBt;
2272f6239edSdrh if( pOut ){
2287464f578Sdrh sqlite3_file *id = sqlite3PagerFile(sqlite3BtreePager(pTemp));
2297464f578Sdrh i64 sz = 0;
2307464f578Sdrh if( id->pMethods!=0 && (sqlite3OsFileSize(id, &sz)!=SQLITE_OK || sz>0) ){
2317464f578Sdrh rc = SQLITE_ERROR;
2327464f578Sdrh sqlite3SetString(pzErrMsg, db, "output file already exists");
2337464f578Sdrh goto end_of_vacuum;
2347464f578Sdrh }
23567752575Sdrh db->mDbFlags |= DBFLAG_VacuumInto;
236*80b30f99Sdan
237*80b30f99Sdan /* For a VACUUM INTO, the pager-flags are set to the same values as
238*80b30f99Sdan ** they are for the database being vacuumed, except that PAGER_CACHESPILL
239*80b30f99Sdan ** is always set. */
240*80b30f99Sdan pgflags = db->aDb[iDb].safety_level | (db->flags & PAGER_FLAGS_MASK);
2417464f578Sdrh }
24245248de3Sdrh nRes = sqlite3BtreeGetRequestedReserve(pMain);
243cdb7a0feSdrh
2449ef5e770Sdrh sqlite3BtreeSetCacheSize(pTemp, db->aDb[iDb].pSchema->cache_size);
2459d608759Sdrh sqlite3BtreeSetSpillSize(pTemp, sqlite3BtreeSetSpillSize(pMain,0));
246*80b30f99Sdan sqlite3BtreeSetPagerFlags(pTemp, pgflags|PAGER_CACHESPILL);
247f602963dSdan
248f602963dSdan /* Begin a transaction and take an exclusive lock on the main database
249f602963dSdan ** file. This is done before the sqlite3BtreeGetPageSize(pMain) call below,
250f602963dSdan ** to ensure that we do not try to change the page-size on a WAL database.
251f602963dSdan */
2529ef5e770Sdrh rc = execSql(db, pzErrMsg, "BEGIN");
253f602963dSdan if( rc!=SQLITE_OK ) goto end_of_vacuum;
2542f6239edSdrh rc = sqlite3BtreeBeginTrans(pMain, pOut==0 ? 2 : 0, 0);
255f602963dSdan if( rc!=SQLITE_OK ) goto end_of_vacuum;
256f602963dSdan
257811bdbd2Sdrh /* Do not attempt to change the page size for a WAL database */
2580b9b4301Sdrh if( sqlite3PagerGetJournalMode(sqlite3BtreePager(pMain))
259cfb52496Sdrh ==PAGER_JOURNALMODE_WAL
260cfb52496Sdrh && pOut==0
261cfb52496Sdrh ){
262811bdbd2Sdrh db->nextPagesize = 0;
263811bdbd2Sdrh }
264811bdbd2Sdrh
265ce4869f8Sdrh if( sqlite3BtreeSetPageSize(pTemp, sqlite3BtreeGetPageSize(pMain), nRes, 0)
266ce4869f8Sdrh || (!isMemDb && sqlite3BtreeSetPageSize(pTemp, db->nextPagesize, nRes, 0))
2675dc348a2Sdrh || NEVER(db->mallocFailed)
268f653d782Sdanielk1977 ){
269fad3039cSmistachkin rc = SQLITE_NOMEM_BKPT;
270884c5b32Sdanielk1977 goto end_of_vacuum;
271884c5b32Sdanielk1977 }
2723df6b257Sdanielk1977
27342741be9Sdanielk1977 #ifndef SQLITE_OMIT_AUTOVACUUM
274ddac25c7Sdrh sqlite3BtreeSetAutoVacuum(pTemp, db->nextAutovac>=0 ? db->nextAutovac :
275ddac25c7Sdrh sqlite3BtreeGetAutoVacuum(pMain));
27642741be9Sdanielk1977 #endif
27742741be9Sdanielk1977
2783df6b257Sdanielk1977 /* Query the schema of the main database. Create a mirror schema
2793df6b257Sdanielk1977 ** in the temporary database.
2803df6b257Sdanielk1977 */
2819ef5e770Sdrh db->init.iDb = nDb; /* force new CREATE statements into vacuum_db */
2829ef5e770Sdrh rc = execSqlF(db, pzErrMsg,
2831e32bed3Sdrh "SELECT sql FROM \"%w\".sqlite_schema"
2849ef5e770Sdrh " WHERE type='table'AND name<>'sqlite_sequence'"
28534b27edcSdrh " AND coalesce(rootpage,1)>0",
2869ef5e770Sdrh zDbMain
28766b224cbSdrh );
28827c77438Sdanielk1977 if( rc!=SQLITE_OK ) goto end_of_vacuum;
2899ef5e770Sdrh rc = execSqlF(db, pzErrMsg,
2901e32bed3Sdrh "SELECT sql FROM \"%w\".sqlite_schema"
29134b27edcSdrh " WHERE type='index'",
2929ef5e770Sdrh zDbMain
2939ef5e770Sdrh );
29427c77438Sdanielk1977 if( rc!=SQLITE_OK ) goto end_of_vacuum;
2959ef5e770Sdrh db->init.iDb = 0;
2963df6b257Sdanielk1977
2973df6b257Sdanielk1977 /* Loop through the tables in the main database. For each, do
298397308dfSdrh ** an "INSERT INTO vacuum_db.xxx SELECT * FROM main.xxx;" to copy
2993df6b257Sdanielk1977 ** the contents to the temporary database.
3003df6b257Sdanielk1977 */
3019ef5e770Sdrh rc = execSqlF(db, pzErrMsg,
302bd26f925Sdanielk1977 "SELECT'INSERT INTO vacuum_db.'||quote(name)"
3039ef5e770Sdrh "||' SELECT*FROM\"%w\".'||quote(name)"
3041e32bed3Sdrh "FROM vacuum_db.sqlite_schema "
3059ef5e770Sdrh "WHERE type='table'AND coalesce(rootpage,1)>0",
3069ef5e770Sdrh zDbMain
3073df6b257Sdanielk1977 );
3088257aa8dSdrh assert( (db->mDbFlags & DBFLAG_Vacuum)!=0 );
30967752575Sdrh db->mDbFlags &= ~DBFLAG_Vacuum;
3103df6b257Sdanielk1977 if( rc!=SQLITE_OK ) goto end_of_vacuum;
3113df6b257Sdanielk1977
312fdd48a76Sdrh /* Copy the triggers, views, and virtual tables from the main database
313fdd48a76Sdrh ** over to the temporary database. None of these objects has any
314fdd48a76Sdrh ** associated storage, so all we have to do is copy their entries
315346a70caSdrh ** from the schema table.
3163df6b257Sdanielk1977 */
3179ef5e770Sdrh rc = execSqlF(db, pzErrMsg,
3181e32bed3Sdrh "INSERT INTO vacuum_db.sqlite_schema"
3191e32bed3Sdrh " SELECT*FROM \"%w\".sqlite_schema"
3209ef5e770Sdrh " WHERE type IN('view','trigger')"
3219ef5e770Sdrh " OR(type='table'AND rootpage=0)",
3229ef5e770Sdrh zDbMain
3233df6b257Sdanielk1977 );
324fdd48a76Sdrh if( rc ) goto end_of_vacuum;
32513bff815Sdrh
326c5f20a00Sdan /* At this point, there is a write transaction open on both the
327c5f20a00Sdan ** vacuum database and the main database. Assuming no error occurs,
328c5f20a00Sdan ** both transactions are closed by this block - the main database
329c5f20a00Sdan ** transaction by sqlite3BtreeCopyFile() and the other by an explicit
330c5f20a00Sdan ** call to sqlite3BtreeCommit().
3313df6b257Sdanielk1977 */
3325dc348a2Sdrh {
3333df6b257Sdanielk1977 u32 meta;
3348cbd373cSdrh int i;
3358cbd373cSdrh
3368cbd373cSdrh /* This array determines which meta meta values are preserved in the
3378cbd373cSdrh ** vacuum. Even entries are the meta value number and odd entries
3388cbd373cSdrh ** are an increment to apply to the meta value after the vacuum.
3398cbd373cSdrh ** The increment is used to increase the schema cookie so that other
3408cbd373cSdrh ** connections to the same database will know to reread the schema.
3418cbd373cSdrh */
3428cbd373cSdrh static const unsigned char aCopy[] = {
3430d19f7acSdanielk1977 BTREE_SCHEMA_VERSION, 1, /* Add one to the old schema cookie */
3440d19f7acSdanielk1977 BTREE_DEFAULT_CACHE_SIZE, 0, /* Preserve the default page cache size */
3450d19f7acSdanielk1977 BTREE_TEXT_ENCODING, 0, /* Preserve the text encoding */
3460d19f7acSdanielk1977 BTREE_USER_VERSION, 0, /* Preserve the user version */
347b8a67ec8Sdrh BTREE_APPLICATION_ID, 0, /* Preserve the application id */
3488cbd373cSdrh };
3493df6b257Sdanielk1977
35099744fa4Sdrh assert( SQLITE_TXN_WRITE==sqlite3BtreeTxnState(pTemp) );
35199744fa4Sdrh assert( pOut!=0 || SQLITE_TXN_WRITE==sqlite3BtreeTxnState(pMain) );
3521d850a72Sdanielk1977
3538cbd373cSdrh /* Copy Btree meta values */
35400e13613Sdanielk1977 for(i=0; i<ArraySize(aCopy); i+=2){
3555dc348a2Sdrh /* GetMeta() and UpdateMeta() cannot fail in this context because
3565dc348a2Sdrh ** we already have page 1 loaded into cache and marked dirty. */
357602b466eSdanielk1977 sqlite3BtreeGetMeta(pMain, aCopy[i], &meta);
3588cbd373cSdrh rc = sqlite3BtreeUpdateMeta(pTemp, aCopy[i], meta+aCopy[i+1]);
3595dc348a2Sdrh if( NEVER(rc!=SQLITE_OK) ) goto end_of_vacuum;
3608cbd373cSdrh }
3613df6b257Sdanielk1977
3622f6239edSdrh if( pOut==0 ){
3633df6b257Sdanielk1977 rc = sqlite3BtreeCopyFile(pMain, pTemp);
364b0b7db91Sdrh }
365369f27ebSdanielk1977 if( rc!=SQLITE_OK ) goto end_of_vacuum;
366c9ac5caaSdrh rc = sqlite3BtreeCommit(pTemp);
367c9ac5caaSdrh if( rc!=SQLITE_OK ) goto end_of_vacuum;
36806249db1Sdanielk1977 #ifndef SQLITE_OMIT_AUTOVACUUM
3692f6239edSdrh if( pOut==0 ){
37006249db1Sdanielk1977 sqlite3BtreeSetAutoVacuum(pMain, sqlite3BtreeGetAutoVacuum(pTemp));
371b0b7db91Sdrh }
37206249db1Sdanielk1977 #endif
3732e6d11bcSdrh }
37413bff815Sdrh
3755dc348a2Sdrh assert( rc==SQLITE_OK );
3762f6239edSdrh if( pOut==0 ){
37741724ebcSdrh nRes = sqlite3BtreeGetRequestedReserve(pTemp);
378ce4869f8Sdrh rc = sqlite3BtreeSetPageSize(pMain, sqlite3BtreeGetPageSize(pTemp), nRes,1);
379b0b7db91Sdrh }
380f653d782Sdanielk1977
38113bff815Sdrh end_of_vacuum:
3820cd2d4c9Sdrh /* Restore the original value of db->flags */
3839ef5e770Sdrh db->init.iDb = 0;
3848257aa8dSdrh db->mDbFlags = saved_mDbFlags;
3850cd2d4c9Sdrh db->flags = saved_flags;
386e63b2c21Sdrh db->nChange = saved_nChange;
387e63b2c21Sdrh db->nTotalChange = saved_nTotalChange;
3883d2a529dSdrh db->mTrace = saved_mTrace;
389e937df81Sdrh sqlite3BtreeSetPageSize(pMain, -1, 0, 1);
3903a3f38e0Sdanielk1977
39146c43eddSdanielk1977 /* Currently there is an SQL level transaction open on the vacuum
39246c43eddSdanielk1977 ** database. No locks are held on any other files (since the main file
39346c43eddSdanielk1977 ** was committed at the btree level). So it safe to end the transaction
39446c43eddSdanielk1977 ** by manually setting the autoCommit flag to true and detaching the
39546c43eddSdanielk1977 ** vacuum database. The vacuum_db journal file is deleted when the pager
39646c43eddSdanielk1977 ** is closed by the DETACH.
39746c43eddSdanielk1977 */
39846c43eddSdanielk1977 db->autoCommit = 1;
399261919ccSdanielk1977
4000203bde9Sdanielk1977 if( pDb ){
4010203bde9Sdanielk1977 sqlite3BtreeClose(pDb->pBt);
4020203bde9Sdanielk1977 pDb->pBt = 0;
4030203bde9Sdanielk1977 pDb->pSchema = 0;
404261919ccSdanielk1977 }
405f4208043Sdanielk1977
40603faf63bSdrh /* This both clears the schemas and reduces the size of the db->aDb[]
40703faf63bSdrh ** array. */
40881028a45Sdrh sqlite3ResetAllSchemasOfConnection(db);
40989dec819Sdrh
4103df6b257Sdanielk1977 return rc;
4113df6b257Sdanielk1977 }
412c7792fa0Sdrh
413fdbcdee5Sdrh #endif /* SQLITE_OMIT_VACUUM && SQLITE_OMIT_ATTACH */
414