| Index: third_party/sqlite/sqlite-src-3080704/tool/fast_vacuum.c
|
| diff --git a/third_party/sqlite/sqlite-src-3080704/tool/fast_vacuum.c b/third_party/sqlite/sqlite-src-3080704/tool/fast_vacuum.c
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..6a50dcc68053673e72e9232daea6f47feabe4887
|
| --- /dev/null
|
| +++ b/third_party/sqlite/sqlite-src-3080704/tool/fast_vacuum.c
|
| @@ -0,0 +1,234 @@
|
| +/*
|
| +** 2013-10-01
|
| +**
|
| +** 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 program implements a high-speed version of the VACUUM command.
|
| +** It repacks an SQLite database to remove as much unused space as
|
| +** possible and to relocate content sequentially in the file.
|
| +**
|
| +** This program runs faster and uses less temporary disk space than the
|
| +** built-in VACUUM command. On the other hand, this program has a number
|
| +** of important restrictions relative to the built-in VACUUM command.
|
| +**
|
| +** (1) The caller must ensure that no other processes are accessing the
|
| +** database file while the vacuum is taking place. The usual SQLite
|
| +** file locking is insufficient for this. The caller must use
|
| +** external means to make sure only this one routine is reading and
|
| +** writing the database.
|
| +**
|
| +** (2) Database reconfiguration such as page size or auto_vacuum changes
|
| +** are not supported by this utility.
|
| +**
|
| +** (3) The database file might be renamed if a power loss or crash
|
| +** occurs at just the wrong moment. Recovery must be prepared to
|
| +** to deal with the possibly changed filename.
|
| +**
|
| +** This program is intended as a *Demonstration Only*. The intent of this
|
| +** program is to provide example code that application developers can use
|
| +** when creating similar functionality in their applications.
|
| +**
|
| +** To compile this program:
|
| +**
|
| +** cc fast_vacuum.c sqlite3.c
|
| +**
|
| +** Add whatever linker options are required. (Example: "-ldl -lpthread").
|
| +** Then to run the program:
|
| +**
|
| +** ./a.out file-to-vacuum
|
| +**
|
| +*/
|
| +#include "sqlite3.h"
|
| +#include <stdio.h>
|
| +#include <stdlib.h>
|
| +
|
| +/*
|
| +** Finalize a prepared statement. If an error has occurred, print the
|
| +** error message and exit.
|
| +*/
|
| +static void vacuumFinalize(sqlite3_stmt *pStmt){
|
| + sqlite3 *db = sqlite3_db_handle(pStmt);
|
| + int rc = sqlite3_finalize(pStmt);
|
| + if( rc ){
|
| + fprintf(stderr, "finalize error: %s\n", sqlite3_errmsg(db));
|
| + exit(1);
|
| + }
|
| +}
|
| +
|
| +/*
|
| +** Execute zSql on database db. The SQL text is printed to standard
|
| +** output. If an error occurs, print an error message and exit the
|
| +** process.
|
| +*/
|
| +static void execSql(sqlite3 *db, const char *zSql){
|
| + sqlite3_stmt *pStmt;
|
| + if( !zSql ){
|
| + fprintf(stderr, "out of memory!\n");
|
| + exit(1);
|
| + }
|
| + printf("%s;\n", zSql);
|
| + if( SQLITE_OK!=sqlite3_prepare(db, zSql, -1, &pStmt, 0) ){
|
| + fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db));
|
| + exit(1);
|
| + }
|
| + sqlite3_step(pStmt);
|
| + vacuumFinalize(pStmt);
|
| +}
|
| +
|
| +/*
|
| +** Execute zSql on database db. The zSql statement returns exactly
|
| +** one column. Execute this return value as SQL on the same database.
|
| +**
|
| +** The zSql statement is printed on standard output prior to being
|
| +** run. If any errors occur, an error is printed and the process
|
| +** exits.
|
| +*/
|
| +static void execExecSql(sqlite3 *db, const char *zSql){
|
| + sqlite3_stmt *pStmt;
|
| + int rc;
|
| +
|
| + printf("%s;\n", zSql);
|
| + rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
|
| + if( rc!=SQLITE_OK ){
|
| + fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db));
|
| + exit(1);
|
| + }
|
| + while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
| + execSql(db, (char*)sqlite3_column_text(pStmt, 0));
|
| + }
|
| + vacuumFinalize(pStmt);
|
| +}
|
| +
|
| +
|
| +int main(int argc, char **argv){
|
| + sqlite3 *db; /* Connection to the database file */
|
| + int rc; /* Return code from SQLite interface calls */
|
| + sqlite3_uint64 r; /* A random number */
|
| + const char *zDbToVacuum; /* Database to be vacuumed */
|
| + char *zBackupDb; /* Backup copy of the original database */
|
| + char *zTempDb; /* Temporary database */
|
| + char *zSql; /* An SQL statement */
|
| +
|
| + if( argc!=2 ){
|
| + fprintf(stderr, "Usage: %s DATABASE\n", argv[0]);
|
| + return 1;
|
| + }
|
| +
|
| + /* Identify the database file to be vacuumed and open it.
|
| + */
|
| + zDbToVacuum = argv[1];
|
| + printf("-- open database file \"%s\"\n", zDbToVacuum);
|
| + rc = sqlite3_open(zDbToVacuum, &db);
|
| + if( rc ){
|
| + fprintf(stderr, "%s: %s\n", zDbToVacuum, sqlite3_errstr(rc));
|
| + return 1;
|
| + }
|
| +
|
| + /* Create names for two other files. zTempDb will be a new database
|
| + ** into which we construct a vacuumed copy of zDbToVacuum. zBackupDb
|
| + ** will be a new name for zDbToVacuum after it is vacuumed.
|
| + */
|
| + sqlite3_randomness(sizeof(r), &r);
|
| + zTempDb = sqlite3_mprintf("%s-vacuum-%016llx", zDbToVacuum, r);
|
| + zBackupDb = sqlite3_mprintf("%s-backup-%016llx", zDbToVacuum, r);
|
| +
|
| + /* Attach the zTempDb database to the database connection.
|
| + */
|
| + zSql = sqlite3_mprintf("ATTACH '%q' AS vacuum_db;", zTempDb);
|
| + execSql(db, zSql);
|
| + sqlite3_free(zSql);
|
| +
|
| + /* TODO:
|
| + ** Set the page_size and auto_vacuum mode for zTempDb here, if desired.
|
| + */
|
| +
|
| + /* The vacuum will occur inside of a transaction. Set writable_schema
|
| + ** to ON so that we can directly update the sqlite_master table in the
|
| + ** zTempDb database.
|
| + */
|
| + execSql(db, "PRAGMA writable_schema=ON");
|
| + execSql(db, "BEGIN");
|
| +
|
| +
|
| + /* Query the schema of the main database. Create a mirror schema
|
| + ** in the temporary database.
|
| + */
|
| + execExecSql(db,
|
| + "SELECT 'CREATE TABLE vacuum_db.' || substr(sql,14) "
|
| + " FROM sqlite_master WHERE type='table' AND name!='sqlite_sequence'"
|
| + " AND rootpage>0"
|
| + );
|
| + execExecSql(db,
|
| + "SELECT 'CREATE INDEX vacuum_db.' || substr(sql,14)"
|
| + " FROM sqlite_master WHERE sql LIKE 'CREATE INDEX %'"
|
| + );
|
| + execExecSql(db,
|
| + "SELECT 'CREATE UNIQUE INDEX vacuum_db.' || substr(sql,21) "
|
| + " FROM sqlite_master WHERE sql LIKE 'CREATE UNIQUE INDEX %'"
|
| + );
|
| +
|
| + /* Loop through the tables in the main database. For each, do
|
| + ** an "INSERT INTO vacuum_db.xxx SELECT * FROM main.xxx;" to copy
|
| + ** the contents to the temporary database.
|
| + */
|
| + execExecSql(db,
|
| + "SELECT 'INSERT INTO vacuum_db.' || quote(name) "
|
| + "|| ' SELECT * FROM main.' || quote(name) "
|
| + "FROM main.sqlite_master "
|
| + "WHERE type = 'table' AND name!='sqlite_sequence' "
|
| + " AND rootpage>0"
|
| + );
|
| +
|
| + /* Copy over the sequence table
|
| + */
|
| + execExecSql(db,
|
| + "SELECT 'DELETE FROM vacuum_db.' || quote(name) "
|
| + "FROM vacuum_db.sqlite_master WHERE name='sqlite_sequence'"
|
| + );
|
| + execExecSql(db,
|
| + "SELECT 'INSERT INTO vacuum_db.' || quote(name) "
|
| + "|| ' SELECT * FROM main.' || quote(name) "
|
| + "FROM vacuum_db.sqlite_master WHERE name=='sqlite_sequence'"
|
| + );
|
| +
|
| + /* Copy the triggers, views, and virtual tables from the main database
|
| + ** over to the temporary database. None of these objects has any
|
| + ** associated storage, so all we have to do is copy their entries
|
| + ** from the SQLITE_MASTER table.
|
| + */
|
| + execSql(db,
|
| + "INSERT INTO vacuum_db.sqlite_master "
|
| + " SELECT type, name, tbl_name, rootpage, sql"
|
| + " FROM main.sqlite_master"
|
| + " WHERE type='view' OR type='trigger'"
|
| + " OR (type='table' AND rootpage=0)"
|
| + );
|
| +
|
| + /* Commit the transaction and close the database
|
| + */
|
| + execSql(db, "COMMIT");
|
| + printf("-- close database\n");
|
| + sqlite3_close(db);
|
| +
|
| +
|
| + /* At this point, zDbToVacuum is unchanged. zTempDb contains a
|
| + ** vacuumed copy of zDbToVacuum. Rearrange filenames so that
|
| + ** zTempDb becomes thenew zDbToVacuum.
|
| + */
|
| + printf("-- rename \"%s\" to \"%s\"\n", zDbToVacuum, zBackupDb);
|
| + rename(zDbToVacuum, zBackupDb);
|
| + printf("-- rename \"%s\" to \"%s\"\n", zTempDb, zDbToVacuum);
|
| + rename(zTempDb, zDbToVacuum);
|
| +
|
| + /* Release allocated memory */
|
| + sqlite3_free(zTempDb);
|
| + sqlite3_free(zBackupDb);
|
| + return 0;
|
| +}
|
|
|