| 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 adc802e60b4330608c511829857dd14c74c597ce..25b1258510fc5435e172047d60b225043da826c1 100644
|
| --- a/third_party/sqlite/src/src/vacuum.c
|
| +++ b/third_party/sqlite/src/src/vacuum.c
|
| @@ -18,57 +18,52 @@
|
| #include "vdbeInt.h"
|
|
|
| #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){
|
| sqlite3_stmt *pStmt;
|
| - VVA_ONLY( int rc; )
|
| - if( !zSql ){
|
| - return SQLITE_NOMEM;
|
| - }
|
| - 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;
|
| 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;
|
| -
|
| - while( SQLITE_ROW==sqlite3_step(pStmt) ){
|
| - rc = execSql(db, pzErrMsg, (char*)sqlite3_column_text(pStmt, 0));
|
| - if( rc!=SQLITE_OK ){
|
| - vacuumFinalize(db, pStmt, pzErrMsg);
|
| - return rc;
|
| + while( SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){
|
| + const char *zSubSql = (const char*)sqlite3_column_text(pStmt,0);
|
| + assert( sqlite3_strnicmp(zSql,"SELECT",6)==0 );
|
| + if( zSubSql ){
|
| + assert( zSubSql[0]!='S' );
|
| + rc = execSql(db, pzErrMsg, zSubSql);
|
| + if( rc!=SQLITE_OK ) break;
|
| }
|
| }
|
| -
|
| - return vacuumFinalize(db, pStmt, pzErrMsg);
|
| + assert( rc!=SQLITE_ROW );
|
| + 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
|
| ** following reboot.
|
| */
|
| -void sqlite3Vacuum(Parse *pParse){
|
| +void sqlite3Vacuum(Parse *pParse, Token *pNm){
|
| Vdbe *v = sqlite3GetVdbe(pParse);
|
| - if( v ){
|
| - sqlite3VdbeAddOp2(v, OP_Vacuum, 0, 0);
|
| - sqlite3VdbeUsesBtree(v, 0);
|
| + int iDb = pNm ? sqlite3TwoPartName(pParse, pNm, pNm, &pNm) : 0;
|
| + if( v && (iDb>=2 || iDb==0) ){
|
| + sqlite3VdbeAddOp1(v, OP_Vacuum, iDb);
|
| + sqlite3VdbeUsesBtree(v, iDb);
|
| }
|
| return;
|
| }
|
| @@ -113,19 +109,19 @@ void sqlite3Vacuum(Parse *pParse){
|
| /*
|
| ** 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 */
|
| Btree *pMain; /* The database being vacuumed */
|
| Btree *pTemp; /* The temporary database we vacuum into */
|
| - char *zSql = 0; /* SQL statements */
|
| int saved_flags; /* Saved value of the db->flags */
|
| int saved_nChange; /* Saved value of db->nChange */
|
| int saved_nTotalChange; /* Saved value of db->nTotalChange */
|
| - void (*saved_xTrace)(void*,const char*); /* Saved db->xTrace */
|
| + u8 saved_mTrace; /* Saved trace settings */
|
| Db *pDb = 0; /* Database to detach at end of vacuum */
|
| int isMemDb; /* True if vacuuming a :memory: database */
|
| int nRes; /* Bytes of reserved space at the end of each page */
|
| int nDb; /* Number of attached databases */
|
| + const char *zDbMain; /* Schema name of database to vacuum */
|
|
|
| if( !db->autoCommit ){
|
| sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction");
|
| @@ -142,12 +138,14 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|
| saved_flags = db->flags;
|
| saved_nChange = db->nChange;
|
| saved_nTotalChange = db->nTotalChange;
|
| - saved_xTrace = db->xTrace;
|
| - db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_PreferBuiltin;
|
| - db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder);
|
| - db->xTrace = 0;
|
| -
|
| - pMain = db->aDb[0].pBt;
|
| + saved_mTrace = db->mTrace;
|
| + db->flags |= (SQLITE_WriteSchema | SQLITE_IgnoreChecks
|
| + | SQLITE_PreferBuiltin | SQLITE_Vacuum);
|
| + db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder | SQLITE_CountRows);
|
| + db->mTrace = 0;
|
| +
|
| + zDbMain = db->aDb[iDb].zDbSName;
|
| + pMain = db->aDb[iDb].pBt;
|
| isMemDb = sqlite3PagerIsMemdb(sqlite3BtreePager(pMain));
|
|
|
| /* 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.
|
| */
|
| nDb = db->nDb;
|
| - if( sqlite3TempInMemory(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->zName,"vacuum_db")==0 );
|
| - }
|
| + rc = execSql(db, pzErrMsg, "ATTACH''AS vacuum_db");
|
| 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
|
| ** locked (as there was more than one active statement when the transaction
|
| @@ -197,14 +189,15 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|
| }
|
| #endif
|
|
|
| - rc = execSql(db, pzErrMsg, "PRAGMA vacuum_db.synchronous=OFF");
|
| - if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
| + sqlite3BtreeSetCacheSize(pTemp, db->aDb[iDb].pSchema->cache_size);
|
| + sqlite3BtreeSetSpillSize(pTemp, sqlite3BtreeSetSpillSize(pMain,0));
|
| + sqlite3BtreeSetPagerFlags(pTemp, PAGER_SYNCHRONOUS_OFF|PAGER_CACHESPILL);
|
|
|
| /* 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;");
|
| + 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;
|
| @@ -219,7 +212,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|
| || (!isMemDb && sqlite3BtreeSetPageSize(pTemp, db->nextPagesize, nRes, 0))
|
| || NEVER(db->mallocFailed)
|
| ){
|
| - rc = SQLITE_NOMEM;
|
| + rc = SQLITE_NOMEM_BKPT;
|
| goto end_of_vacuum;
|
| }
|
|
|
| @@ -231,64 +224,48 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|
| /* 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 coalesce(rootpage,1)>0"
|
| + db->init.iDb = nDb; /* force new CREATE statements into vacuum_db */
|
| + rc = execSqlF(db, pzErrMsg,
|
| + "SELECT sql FROM \"%w\".sqlite_master"
|
| + " WHERE type='table'AND name<>'sqlite_sequence'"
|
| + " AND coalesce(rootpage,1)>0",
|
| + zDbMain
|
| );
|
| if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
| - rc = execExecSql(db, pzErrMsg,
|
| - "SELECT 'CREATE INDEX vacuum_db.' || substr(sql,14)"
|
| - " FROM sqlite_master WHERE sql LIKE 'CREATE INDEX %' ");
|
| - if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
| - rc = execExecSql(db, pzErrMsg,
|
| - "SELECT 'CREATE UNIQUE INDEX vacuum_db.' || substr(sql,21) "
|
| - " FROM sqlite_master WHERE sql LIKE 'CREATE UNIQUE INDEX %'");
|
| + rc = execSqlF(db, pzErrMsg,
|
| + "SELECT sql FROM \"%w\".sqlite_master"
|
| + " WHERE type='index' AND length(sql)>10",
|
| + zDbMain
|
| + );
|
| if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
| + db->init.iDb = 0;
|
|
|
| /* 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.
|
| */
|
| - assert( (db->flags & SQLITE_Vacuum)==0 );
|
| - db->flags |= SQLITE_Vacuum;
|
| - rc = execExecSql(db, pzErrMsg,
|
| - "SELECT 'INSERT INTO vacuum_db.' || quote(name) "
|
| - "|| ' SELECT * FROM main.' || quote(name) || ';'"
|
| - "FROM main.sqlite_master "
|
| - "WHERE type = 'table' AND name!='sqlite_sequence' "
|
| - " AND coalesce(rootpage,1)>0"
|
| + rc = execSqlF(db, pzErrMsg,
|
| + "SELECT'INSERT INTO vacuum_db.'||quote(name)"
|
| + "||' SELECT*FROM\"%w\".'||quote(name)"
|
| + "FROM vacuum_db.sqlite_master "
|
| + "WHERE type='table'AND coalesce(rootpage,1)>0",
|
| + zDbMain
|
| );
|
| assert( (db->flags & SQLITE_Vacuum)!=0 );
|
| db->flags &= ~SQLITE_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
|
| ** 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.
|
| */
|
| - rc = execSql(db, pzErrMsg,
|
| - "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)"
|
| + rc = execSqlF(db, pzErrMsg,
|
| + "INSERT INTO vacuum_db.sqlite_master"
|
| + " SELECT*FROM \"%w\".sqlite_master"
|
| + " WHERE type IN('view','trigger')"
|
| + " OR(type='table'AND rootpage=0)",
|
| + zDbMain
|
| );
|
| if( rc ) goto end_of_vacuum;
|
|
|
| @@ -342,10 +319,11 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|
|
|
| end_of_vacuum:
|
| /* Restore the original value of db->flags */
|
| + db->init.iDb = 0;
|
| db->flags = saved_flags;
|
| db->nChange = saved_nChange;
|
| db->nTotalChange = saved_nTotalChange;
|
| - db->xTrace = saved_xTrace;
|
| + db->mTrace = saved_mTrace;
|
| sqlite3BtreeSetPageSize(pMain, -1, -1, 1);
|
|
|
| /* Currently there is an SQL level transaction open on the vacuum
|
|
|