Index: third_party/sqlite/src/src/vacuum.c |
diff --git a/third_party/sqlite/src/src/vacuum.c b/third_party/sqlite/src/src/vacuum.c |
index 5a4ed32052c52ce6b0a24f70926440e77637c4b1..4d0c0976a182a01a0195b047dc5289bca1095cb4 100644 |
--- a/third_party/sqlite/src/src/vacuum.c |
+++ b/third_party/sqlite/src/src/vacuum.c |
@@ -45,7 +45,7 @@ static int execSql(sqlite3 *db, char **pzErrMsg, const char *zSql){ |
return sqlite3_errcode(db); |
} |
VVA_ONLY( rc = ) sqlite3_step(pStmt); |
- assert( rc!=SQLITE_ROW ); |
+ assert( rc!=SQLITE_ROW || (db->flags&SQLITE_CountRows) ); |
return vacuumFinalize(db, pStmt, pzErrMsg); |
} |
@@ -72,19 +72,40 @@ static int execExecSql(sqlite3 *db, char **pzErrMsg, const char *zSql){ |
} |
/* |
-** The non-standard VACUUM command is used to clean up the database, |
+** The VACUUM command is used to clean up the database, |
** collapse free space, etc. It is modelled after the VACUUM command |
-** in PostgreSQL. |
+** in PostgreSQL. The VACUUM command works as follows: |
** |
-** In version 1.0.x of SQLite, the VACUUM command would call |
-** gdbm_reorganize() on all the database tables. But beginning |
-** with 2.0.0, SQLite no longer uses GDBM so this command has |
-** become a no-op. |
+** (1) Create a new transient database file |
+** (2) Copy all content from the database being vacuumed into |
+** the new transient database file |
+** (3) Copy content from the transient database back into the |
+** original database. |
+** |
+** The transient database requires temporary disk space approximately |
+** equal to the size of the original database. The copy operation of |
+** step (3) requires additional temporary disk space approximately equal |
+** to the size of the original database for the rollback journal. |
+** Hence, temporary disk space that is approximately 2x the size of the |
+** original database is required. Every page of the database is written |
+** approximately 3 times: Once for step (2) and twice for step (3). |
+** Two writes per page are required in step (3) because the original |
+** database content must be written into the rollback journal prior to |
+** overwriting the database with the vacuumed content. |
+** |
+** Only 1x temporary space and only 1x writes would be required if |
+** the copy of step (3) were replace by deleting the original database |
+** and renaming the transient database as the original. But that will |
+** not work if other processes are attached to the original database. |
+** And a power loss in between deleting the original and renaming the |
+** transient would cause the database file to appear to be deleted |
+** following reboot. |
*/ |
void sqlite3Vacuum(Parse *pParse){ |
Vdbe *v = sqlite3GetVdbe(pParse); |
if( v ){ |
sqlite3VdbeAddOp2(v, OP_Vacuum, 0, 0); |
+ sqlite3VdbeUsesBtree(v, 0); |
} |
return; |
} |
@@ -110,7 +131,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ |
sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction"); |
return SQLITE_ERROR; |
} |
- if( db->activeVdbeCnt>1 ){ |
+ if( db->nVdbeActive>1 ){ |
sqlite3SetString(pzErrMsg, db,"cannot VACUUM - SQL statements in progress"); |
return SQLITE_ERROR; |
} |
@@ -176,6 +197,18 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ |
} |
#endif |
+ rc = execSql(db, pzErrMsg, "PRAGMA vacuum_db.synchronous=OFF"); |
+ if( rc!=SQLITE_OK ) goto end_of_vacuum; |
+ |
+ /* Begin a transaction and take an exclusive lock on the main database |
+ ** 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. |
+ */ |
+ rc = execSql(db, pzErrMsg, "BEGIN;"); |
+ if( rc!=SQLITE_OK ) goto end_of_vacuum; |
+ rc = sqlite3BtreeBeginTrans(pMain, 2); |
+ if( rc!=SQLITE_OK ) goto end_of_vacuum; |
+ |
/* Do not attempt to change the page size for a WAL database */ |
if( sqlite3PagerGetJournalMode(sqlite3BtreePager(pMain)) |
==PAGER_JOURNALMODE_WAL ){ |
@@ -189,27 +222,19 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ |
rc = SQLITE_NOMEM; |
goto end_of_vacuum; |
} |
- rc = execSql(db, pzErrMsg, "PRAGMA vacuum_db.synchronous=OFF"); |
- if( rc!=SQLITE_OK ){ |
- goto end_of_vacuum; |
- } |
#ifndef SQLITE_OMIT_AUTOVACUUM |
sqlite3BtreeSetAutoVacuum(pTemp, db->nextAutovac>=0 ? db->nextAutovac : |
sqlite3BtreeGetAutoVacuum(pMain)); |
#endif |
- /* Begin a transaction */ |
- rc = execSql(db, pzErrMsg, "BEGIN EXCLUSIVE;"); |
- 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, pzErrMsg, |
"SELECT 'CREATE TABLE vacuum_db.' || substr(sql,14) " |
" FROM sqlite_master WHERE type='table' AND name!='sqlite_sequence'" |
- " AND rootpage>0" |
+ " AND coalesce(rootpage,1)>0" |
); |
if( rc!=SQLITE_OK ) goto end_of_vacuum; |
rc = execExecSql(db, pzErrMsg, |
@@ -230,7 +255,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ |
"|| ' SELECT * FROM main.' || quote(name) || ';'" |
"FROM main.sqlite_master " |
"WHERE type = 'table' AND name!='sqlite_sequence' " |
- " AND rootpage>0" |
+ " AND coalesce(rootpage,1)>0" |
); |
if( rc!=SQLITE_OK ) goto end_of_vacuum; |
@@ -263,13 +288,11 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ |
); |
if( rc ) 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. |
+ /* At this point, there is a write transaction open on both the |
+ ** vacuum database and the main database. Assuming no error occurs, |
+ ** both transactions are closed by this block - the main database |
+ ** transaction by sqlite3BtreeCopyFile() and the other by an explicit |
+ ** call to sqlite3BtreeCommit(). |
*/ |
{ |
u32 meta; |
@@ -286,6 +309,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ |
BTREE_DEFAULT_CACHE_SIZE, 0, /* Preserve the default page cache size */ |
BTREE_TEXT_ENCODING, 0, /* Preserve the text encoding */ |
BTREE_USER_VERSION, 0, /* Preserve the user version */ |
+ BTREE_APPLICATION_ID, 0, /* Preserve the application id */ |
}; |
assert( 1==sqlite3BtreeIsInTrans(pTemp) ); |
@@ -337,7 +361,7 @@ end_of_vacuum: |
/* This both clears the schemas and reduces the size of the db->aDb[] |
** array. */ |
- sqlite3ResetInternalSchema(db, -1); |
+ sqlite3ResetAllSchemasOfConnection(db); |
return rc; |
} |