Annotation of embedaddon/sqlite3/src/vacuum.c, revision 1.1
1.1 ! misho 1: /*
! 2: ** 2003 April 6
! 3: **
! 4: ** The author disclaims copyright to this source code. In place of
! 5: ** a legal notice, here is a blessing:
! 6: **
! 7: ** May you do good and not evil.
! 8: ** May you find forgiveness for yourself and forgive others.
! 9: ** May you share freely, never taking more than you give.
! 10: **
! 11: *************************************************************************
! 12: ** This file contains code used to implement the VACUUM command.
! 13: **
! 14: ** Most of the code in this file may be omitted by defining the
! 15: ** SQLITE_OMIT_VACUUM macro.
! 16: */
! 17: #include "sqliteInt.h"
! 18: #include "vdbeInt.h"
! 19:
! 20: #if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH)
! 21: /*
! 22: ** Finalize a prepared statement. If there was an error, store the
! 23: ** text of the error message in *pzErrMsg. Return the result code.
! 24: */
! 25: static int vacuumFinalize(sqlite3 *db, sqlite3_stmt *pStmt, char **pzErrMsg){
! 26: int rc;
! 27: rc = sqlite3VdbeFinalize((Vdbe*)pStmt);
! 28: if( rc ){
! 29: sqlite3SetString(pzErrMsg, db, sqlite3_errmsg(db));
! 30: }
! 31: return rc;
! 32: }
! 33:
! 34: /*
! 35: ** Execute zSql on database db. Return an error code.
! 36: */
! 37: static int execSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
! 38: sqlite3_stmt *pStmt;
! 39: VVA_ONLY( int rc; )
! 40: if( !zSql ){
! 41: return SQLITE_NOMEM;
! 42: }
! 43: if( SQLITE_OK!=sqlite3_prepare(db, zSql, -1, &pStmt, 0) ){
! 44: sqlite3SetString(pzErrMsg, db, sqlite3_errmsg(db));
! 45: return sqlite3_errcode(db);
! 46: }
! 47: VVA_ONLY( rc = ) sqlite3_step(pStmt);
! 48: assert( rc!=SQLITE_ROW || (db->flags&SQLITE_CountRows) );
! 49: return vacuumFinalize(db, pStmt, pzErrMsg);
! 50: }
! 51:
! 52: /*
! 53: ** Execute zSql on database db. The statement returns exactly
! 54: ** one column. Execute this as SQL on the same database.
! 55: */
! 56: static int execExecSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
! 57: sqlite3_stmt *pStmt;
! 58: int rc;
! 59:
! 60: rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
! 61: if( rc!=SQLITE_OK ) return rc;
! 62:
! 63: while( SQLITE_ROW==sqlite3_step(pStmt) ){
! 64: rc = execSql(db, pzErrMsg, (char*)sqlite3_column_text(pStmt, 0));
! 65: if( rc!=SQLITE_OK ){
! 66: vacuumFinalize(db, pStmt, pzErrMsg);
! 67: return rc;
! 68: }
! 69: }
! 70:
! 71: return vacuumFinalize(db, pStmt, pzErrMsg);
! 72: }
! 73:
! 74: /*
! 75: ** The non-standard VACUUM command is used to clean up the database,
! 76: ** collapse free space, etc. It is modelled after the VACUUM command
! 77: ** in PostgreSQL.
! 78: **
! 79: ** In version 1.0.x of SQLite, the VACUUM command would call
! 80: ** gdbm_reorganize() on all the database tables. But beginning
! 81: ** with 2.0.0, SQLite no longer uses GDBM so this command has
! 82: ** become a no-op.
! 83: */
! 84: void sqlite3Vacuum(Parse *pParse){
! 85: Vdbe *v = sqlite3GetVdbe(pParse);
! 86: if( v ){
! 87: sqlite3VdbeAddOp2(v, OP_Vacuum, 0, 0);
! 88: }
! 89: return;
! 90: }
! 91:
! 92: /*
! 93: ** This routine implements the OP_Vacuum opcode of the VDBE.
! 94: */
! 95: int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
! 96: int rc = SQLITE_OK; /* Return code from service routines */
! 97: Btree *pMain; /* The database being vacuumed */
! 98: Btree *pTemp; /* The temporary database we vacuum into */
! 99: char *zSql = 0; /* SQL statements */
! 100: int saved_flags; /* Saved value of the db->flags */
! 101: int saved_nChange; /* Saved value of db->nChange */
! 102: int saved_nTotalChange; /* Saved value of db->nTotalChange */
! 103: void (*saved_xTrace)(void*,const char*); /* Saved db->xTrace */
! 104: Db *pDb = 0; /* Database to detach at end of vacuum */
! 105: int isMemDb; /* True if vacuuming a :memory: database */
! 106: int nRes; /* Bytes of reserved space at the end of each page */
! 107: int nDb; /* Number of attached databases */
! 108:
! 109: if( !db->autoCommit ){
! 110: sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction");
! 111: return SQLITE_ERROR;
! 112: }
! 113: if( db->activeVdbeCnt>1 ){
! 114: sqlite3SetString(pzErrMsg, db,"cannot VACUUM - SQL statements in progress");
! 115: return SQLITE_ERROR;
! 116: }
! 117:
! 118: /* Save the current value of the database flags so that it can be
! 119: ** restored before returning. Then set the writable-schema flag, and
! 120: ** disable CHECK and foreign key constraints. */
! 121: saved_flags = db->flags;
! 122: saved_nChange = db->nChange;
! 123: saved_nTotalChange = db->nTotalChange;
! 124: saved_xTrace = db->xTrace;
! 125: db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_PreferBuiltin;
! 126: db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder);
! 127: db->xTrace = 0;
! 128:
! 129: pMain = db->aDb[0].pBt;
! 130: isMemDb = sqlite3PagerIsMemdb(sqlite3BtreePager(pMain));
! 131:
! 132: /* Attach the temporary database as 'vacuum_db'. The synchronous pragma
! 133: ** can be set to 'off' for this file, as it is not recovered if a crash
! 134: ** occurs anyway. The integrity of the database is maintained by a
! 135: ** (possibly synchronous) transaction opened on the main database before
! 136: ** sqlite3BtreeCopyFile() is called.
! 137: **
! 138: ** An optimisation would be to use a non-journaled pager.
! 139: ** (Later:) I tried setting "PRAGMA vacuum_db.journal_mode=OFF" but
! 140: ** that actually made the VACUUM run slower. Very little journalling
! 141: ** actually occurs when doing a vacuum since the vacuum_db is initially
! 142: ** empty. Only the journal header is written. Apparently it takes more
! 143: ** time to parse and run the PRAGMA to turn journalling off than it does
! 144: ** to write the journal header file.
! 145: */
! 146: nDb = db->nDb;
! 147: if( sqlite3TempInMemory(db) ){
! 148: zSql = "ATTACH ':memory:' AS vacuum_db;";
! 149: }else{
! 150: zSql = "ATTACH '' AS vacuum_db;";
! 151: }
! 152: rc = execSql(db, pzErrMsg, zSql);
! 153: if( db->nDb>nDb ){
! 154: pDb = &db->aDb[db->nDb-1];
! 155: assert( strcmp(pDb->zName,"vacuum_db")==0 );
! 156: }
! 157: if( rc!=SQLITE_OK ) goto end_of_vacuum;
! 158: pTemp = db->aDb[db->nDb-1].pBt;
! 159:
! 160: /* The call to execSql() to attach the temp database has left the file
! 161: ** locked (as there was more than one active statement when the transaction
! 162: ** to read the schema was concluded. Unlock it here so that this doesn't
! 163: ** cause problems for the call to BtreeSetPageSize() below. */
! 164: sqlite3BtreeCommit(pTemp);
! 165:
! 166: nRes = sqlite3BtreeGetReserve(pMain);
! 167:
! 168: /* A VACUUM cannot change the pagesize of an encrypted database. */
! 169: #ifdef SQLITE_HAS_CODEC
! 170: if( db->nextPagesize ){
! 171: extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*);
! 172: int nKey;
! 173: char *zKey;
! 174: sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey);
! 175: if( nKey ) db->nextPagesize = 0;
! 176: }
! 177: #endif
! 178:
! 179: /* Do not attempt to change the page size for a WAL database */
! 180: if( sqlite3PagerGetJournalMode(sqlite3BtreePager(pMain))
! 181: ==PAGER_JOURNALMODE_WAL ){
! 182: db->nextPagesize = 0;
! 183: }
! 184:
! 185: if( sqlite3BtreeSetPageSize(pTemp, sqlite3BtreeGetPageSize(pMain), nRes, 0)
! 186: || (!isMemDb && sqlite3BtreeSetPageSize(pTemp, db->nextPagesize, nRes, 0))
! 187: || NEVER(db->mallocFailed)
! 188: ){
! 189: rc = SQLITE_NOMEM;
! 190: goto end_of_vacuum;
! 191: }
! 192: rc = execSql(db, pzErrMsg, "PRAGMA vacuum_db.synchronous=OFF");
! 193: if( rc!=SQLITE_OK ){
! 194: goto end_of_vacuum;
! 195: }
! 196:
! 197: #ifndef SQLITE_OMIT_AUTOVACUUM
! 198: sqlite3BtreeSetAutoVacuum(pTemp, db->nextAutovac>=0 ? db->nextAutovac :
! 199: sqlite3BtreeGetAutoVacuum(pMain));
! 200: #endif
! 201:
! 202: /* Begin a transaction */
! 203: rc = execSql(db, pzErrMsg, "BEGIN EXCLUSIVE;");
! 204: if( rc!=SQLITE_OK ) goto end_of_vacuum;
! 205:
! 206: /* Query the schema of the main database. Create a mirror schema
! 207: ** in the temporary database.
! 208: */
! 209: rc = execExecSql(db, pzErrMsg,
! 210: "SELECT 'CREATE TABLE vacuum_db.' || substr(sql,14) "
! 211: " FROM sqlite_master WHERE type='table' AND name!='sqlite_sequence'"
! 212: " AND rootpage>0"
! 213: );
! 214: if( rc!=SQLITE_OK ) goto end_of_vacuum;
! 215: rc = execExecSql(db, pzErrMsg,
! 216: "SELECT 'CREATE INDEX vacuum_db.' || substr(sql,14)"
! 217: " FROM sqlite_master WHERE sql LIKE 'CREATE INDEX %' ");
! 218: if( rc!=SQLITE_OK ) goto end_of_vacuum;
! 219: rc = execExecSql(db, pzErrMsg,
! 220: "SELECT 'CREATE UNIQUE INDEX vacuum_db.' || substr(sql,21) "
! 221: " FROM sqlite_master WHERE sql LIKE 'CREATE UNIQUE INDEX %'");
! 222: if( rc!=SQLITE_OK ) goto end_of_vacuum;
! 223:
! 224: /* Loop through the tables in the main database. For each, do
! 225: ** an "INSERT INTO vacuum_db.xxx SELECT * FROM main.xxx;" to copy
! 226: ** the contents to the temporary database.
! 227: */
! 228: rc = execExecSql(db, pzErrMsg,
! 229: "SELECT 'INSERT INTO vacuum_db.' || quote(name) "
! 230: "|| ' SELECT * FROM main.' || quote(name) || ';'"
! 231: "FROM main.sqlite_master "
! 232: "WHERE type = 'table' AND name!='sqlite_sequence' "
! 233: " AND rootpage>0"
! 234: );
! 235: if( rc!=SQLITE_OK ) goto end_of_vacuum;
! 236:
! 237: /* Copy over the sequence table
! 238: */
! 239: rc = execExecSql(db, pzErrMsg,
! 240: "SELECT 'DELETE FROM vacuum_db.' || quote(name) || ';' "
! 241: "FROM vacuum_db.sqlite_master WHERE name='sqlite_sequence' "
! 242: );
! 243: if( rc!=SQLITE_OK ) goto end_of_vacuum;
! 244: rc = execExecSql(db, pzErrMsg,
! 245: "SELECT 'INSERT INTO vacuum_db.' || quote(name) "
! 246: "|| ' SELECT * FROM main.' || quote(name) || ';' "
! 247: "FROM vacuum_db.sqlite_master WHERE name=='sqlite_sequence';"
! 248: );
! 249: if( rc!=SQLITE_OK ) goto end_of_vacuum;
! 250:
! 251:
! 252: /* Copy the triggers, views, and virtual tables from the main database
! 253: ** over to the temporary database. None of these objects has any
! 254: ** associated storage, so all we have to do is copy their entries
! 255: ** from the SQLITE_MASTER table.
! 256: */
! 257: rc = execSql(db, pzErrMsg,
! 258: "INSERT INTO vacuum_db.sqlite_master "
! 259: " SELECT type, name, tbl_name, rootpage, sql"
! 260: " FROM main.sqlite_master"
! 261: " WHERE type='view' OR type='trigger'"
! 262: " OR (type='table' AND rootpage=0)"
! 263: );
! 264: if( rc ) goto end_of_vacuum;
! 265:
! 266: /* At this point, there is a write transaction open on both the
! 267: ** vacuum database and the main database. Assuming no error occurs,
! 268: ** both transactions are closed by this block - the main database
! 269: ** transaction by sqlite3BtreeCopyFile() and the other by an explicit
! 270: ** call to sqlite3BtreeCommit().
! 271: */
! 272: {
! 273: u32 meta;
! 274: int i;
! 275:
! 276: /* This array determines which meta meta values are preserved in the
! 277: ** vacuum. Even entries are the meta value number and odd entries
! 278: ** are an increment to apply to the meta value after the vacuum.
! 279: ** The increment is used to increase the schema cookie so that other
! 280: ** connections to the same database will know to reread the schema.
! 281: */
! 282: static const unsigned char aCopy[] = {
! 283: BTREE_SCHEMA_VERSION, 1, /* Add one to the old schema cookie */
! 284: BTREE_DEFAULT_CACHE_SIZE, 0, /* Preserve the default page cache size */
! 285: BTREE_TEXT_ENCODING, 0, /* Preserve the text encoding */
! 286: BTREE_USER_VERSION, 0, /* Preserve the user version */
! 287: };
! 288:
! 289: assert( 1==sqlite3BtreeIsInTrans(pTemp) );
! 290: assert( 1==sqlite3BtreeIsInTrans(pMain) );
! 291:
! 292: /* Copy Btree meta values */
! 293: for(i=0; i<ArraySize(aCopy); i+=2){
! 294: /* GetMeta() and UpdateMeta() cannot fail in this context because
! 295: ** we already have page 1 loaded into cache and marked dirty. */
! 296: sqlite3BtreeGetMeta(pMain, aCopy[i], &meta);
! 297: rc = sqlite3BtreeUpdateMeta(pTemp, aCopy[i], meta+aCopy[i+1]);
! 298: if( NEVER(rc!=SQLITE_OK) ) goto end_of_vacuum;
! 299: }
! 300:
! 301: rc = sqlite3BtreeCopyFile(pMain, pTemp);
! 302: if( rc!=SQLITE_OK ) goto end_of_vacuum;
! 303: rc = sqlite3BtreeCommit(pTemp);
! 304: if( rc!=SQLITE_OK ) goto end_of_vacuum;
! 305: #ifndef SQLITE_OMIT_AUTOVACUUM
! 306: sqlite3BtreeSetAutoVacuum(pMain, sqlite3BtreeGetAutoVacuum(pTemp));
! 307: #endif
! 308: }
! 309:
! 310: assert( rc==SQLITE_OK );
! 311: rc = sqlite3BtreeSetPageSize(pMain, sqlite3BtreeGetPageSize(pTemp), nRes,1);
! 312:
! 313: end_of_vacuum:
! 314: /* Restore the original value of db->flags */
! 315: db->flags = saved_flags;
! 316: db->nChange = saved_nChange;
! 317: db->nTotalChange = saved_nTotalChange;
! 318: db->xTrace = saved_xTrace;
! 319: sqlite3BtreeSetPageSize(pMain, -1, -1, 1);
! 320:
! 321: /* Currently there is an SQL level transaction open on the vacuum
! 322: ** database. No locks are held on any other files (since the main file
! 323: ** was committed at the btree level). So it safe to end the transaction
! 324: ** by manually setting the autoCommit flag to true and detaching the
! 325: ** vacuum database. The vacuum_db journal file is deleted when the pager
! 326: ** is closed by the DETACH.
! 327: */
! 328: db->autoCommit = 1;
! 329:
! 330: if( pDb ){
! 331: sqlite3BtreeClose(pDb->pBt);
! 332: pDb->pBt = 0;
! 333: pDb->pSchema = 0;
! 334: }
! 335:
! 336: /* This both clears the schemas and reduces the size of the db->aDb[]
! 337: ** array. */
! 338: sqlite3ResetInternalSchema(db, -1);
! 339:
! 340: return rc;
! 341: }
! 342:
! 343: #endif /* SQLITE_OMIT_VACUUM && SQLITE_OMIT_ATTACH */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>