Index: src/btree.c ================================================================== --- src/btree.c +++ src/btree.c @@ -3500,20 +3500,20 @@ if( p->inTrans==TRANS_WRITE || (p->inTrans==TRANS_READ && !wrflag) ){ goto trans_begun; } assert( pBt->inTransaction==TRANS_WRITE || IfNotOmitAV(pBt->bDoTruncate)==0 ); - if( (p->db->flags & SQLITE_ResetDatabase) - && sqlite3PagerIsreadonly(pPager)==0 - ){ - pBt->btsFlags &= ~BTS_READ_ONLY; - } - /* Write transactions are not possible on a read-only database */ if( (pBt->btsFlags & BTS_READ_ONLY)!=0 && wrflag ){ - rc = SQLITE_READONLY; - goto trans_begun; + if( (p->db->flags & SQLITE_ResetDatabase) + && sqlite3PagerIsreadonly(pPager)==0 + ){ + pBt->btsFlags &= ~BTS_READ_ONLY; + }else{ + rc = SQLITE_READONLY; + goto trans_begun; + } } #ifndef SQLITE_OMIT_SHARED_CACHE { sqlite3 *pBlock = 0; Index: src/pragma.c ================================================================== --- src/pragma.c +++ src/pragma.c @@ -2382,10 +2382,24 @@ db->nAnalysisLimit = (int)(N&0x7fffffff); } returnSingleInt(v, db->nAnalysisLimit); /* IMP: R-57594-65522 */ break; } + +#if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH) + /* + ** PRAGMA schema.reset_database + ** PRAGMA reset_database + ** + ** Remove all content from a database file. Bring the size of the + ** file back to zero. + */ + case PragTyp_RESET_DATABASE: { + sqlite3VdbeAddOp3(v, OP_Vacuum, iDb, 0, 1); + break; + } +#endif #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) /* ** Report the current state of file logs for all databases */ Index: src/pragma.h ================================================================== --- src/pragma.h +++ src/pragma.h @@ -36,23 +36,24 @@ #define PragTyp_MMAP_SIZE 28 #define PragTyp_MODULE_LIST 29 #define PragTyp_OPTIMIZE 30 #define PragTyp_PAGE_SIZE 31 #define PragTyp_PRAGMA_LIST 32 -#define PragTyp_SECURE_DELETE 33 -#define PragTyp_SHRINK_MEMORY 34 -#define PragTyp_SOFT_HEAP_LIMIT 35 -#define PragTyp_SYNCHRONOUS 36 -#define PragTyp_TABLE_INFO 37 -#define PragTyp_TABLE_LIST 38 -#define PragTyp_TEMP_STORE 39 -#define PragTyp_TEMP_STORE_DIRECTORY 40 -#define PragTyp_THREADS 41 -#define PragTyp_WAL_AUTOCHECKPOINT 42 -#define PragTyp_WAL_CHECKPOINT 43 -#define PragTyp_LOCK_STATUS 44 -#define PragTyp_STATS 45 +#define PragTyp_RESET_DATABASE 33 +#define PragTyp_SECURE_DELETE 34 +#define PragTyp_SHRINK_MEMORY 35 +#define PragTyp_SOFT_HEAP_LIMIT 36 +#define PragTyp_SYNCHRONOUS 37 +#define PragTyp_TABLE_INFO 38 +#define PragTyp_TABLE_LIST 39 +#define PragTyp_TEMP_STORE 40 +#define PragTyp_TEMP_STORE_DIRECTORY 41 +#define PragTyp_THREADS 42 +#define PragTyp_WAL_AUTOCHECKPOINT 43 +#define PragTyp_WAL_CHECKPOINT 44 +#define PragTyp_LOCK_STATUS 45 +#define PragTyp_STATS 46 /* Property flags associated with various pragma. */ #define PragFlg_NeedSchema 0x01 /* Force schema load before running */ #define PragFlg_NoColumns 0x02 /* OP_ResultRow called with zero columns */ #define PragFlg_NoColumns1 0x04 /* zero columns if RHS argument is present */ @@ -498,10 +499,19 @@ {/* zName: */ "recursive_triggers", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_RecTriggers }, +#endif +#if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH) + {/* zName: */ "reset_database", + /* ePragTyp: */ PragTyp_RESET_DATABASE, + /* ePragFlg: */ 0, + /* ColNames: */ 0, 0, + /* iArg: */ 0 }, +#endif +#if !defined(SQLITE_OMIT_FLAG_PRAGMAS) {/* zName: */ "reverse_unordered_selects", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_ReverseOrder }, @@ -655,6 +665,6 @@ /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1, /* ColNames: */ 0, 0, /* iArg: */ SQLITE_WriteSchema|SQLITE_NoSchemaError }, #endif }; -/* Number of pragmas: 68 on by default, 78 total. */ +/* Number of pragmas: 69 on by default, 79 total. */ Index: src/prepare.c ================================================================== --- src/prepare.c +++ src/prepare.c @@ -284,13 +284,10 @@ ** the possible values of meta[4]. */ for(i=0; ipBt, i+1, (u32 *)&meta[i]); } - if( (db->flags & SQLITE_ResetDatabase)!=0 ){ - memset(meta, 0, sizeof(meta)); - } pDb->pSchema->schema_cookie = meta[BTREE_SCHEMA_VERSION-1]; /* If opening a non-empty database, check the text encoding. For the ** main database, set sqlite3.enc to the encoding of the main database. ** For an attached db, it is an error if the encoding is not the same Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -4737,11 +4737,11 @@ Table *sqlite3LocateTableItem(Parse*,u32 flags,SrcItem *); Index *sqlite3FindIndex(sqlite3*,const char*, const char*); void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*); void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*); void sqlite3Vacuum(Parse*,Token*,Expr*); -int sqlite3RunVacuum(char**, sqlite3*, int, sqlite3_value*); +int sqlite3RunVacuum(char**, sqlite3*, int, sqlite3_value*, int); char *sqlite3NameFromToken(sqlite3*, const Token*); int sqlite3ExprCompare(const Parse*,const Expr*,const Expr*, int); int sqlite3ExprCompareSkip(Expr*,Expr*,int); int sqlite3ExprListCompare(const ExprList*,const ExprList*, int); int sqlite3ExprImpliesExpr(const Parse*,const Expr*,const Expr*, int); Index: src/vacuum.c ================================================================== --- src/vacuum.c +++ src/vacuum.c @@ -142,11 +142,12 @@ */ SQLITE_NOINLINE int sqlite3RunVacuum( char **pzErrMsg, /* Write error message here */ sqlite3 *db, /* Database connection */ int iDb, /* Which attached DB to vacuum */ - sqlite3_value *pOut /* Write results here, if not NULL. VACUUM INTO */ + sqlite3_value *pOut, /* Write results here, if not NULL. VACUUM INTO */ + int bReset /* Reset the database if true */ ){ int rc = SQLITE_OK; /* Return code from service routines */ Btree *pMain; /* The database being vacuumed */ Btree *pTemp; /* The temporary database we vacuum into */ u32 saved_mDbFlags; /* Saved value of db->mDbFlags */ @@ -160,10 +161,12 @@ 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 */ const char *zOut; /* Name of output file */ + assert( bReset==0 || pOut==0 ); + if( (db->flags & SQLITE_ResetDatabase)!=0 && pOut==0 ) bReset = 1; if( !db->autoCommit ){ sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction"); return SQLITE_ERROR; /* IMP: R-12218-18073 */ } if( db->nVdbeActive>1 ){ @@ -267,57 +270,62 @@ #ifndef SQLITE_OMIT_AUTOVACUUM sqlite3BtreeSetAutoVacuum(pTemp, db->nextAutovac>=0 ? db->nextAutovac : sqlite3BtreeGetAutoVacuum(pMain)); #endif - /* Query the schema of the main database. Create a mirror schema - ** in the temporary database. - */ - db->init.iDb = nDb; /* force new CREATE statements into vacuum_db */ - rc = execSqlF(db, pzErrMsg, - "SELECT sql FROM \"%w\".sqlite_schema" - " WHERE type='table'AND name<>'sqlite_sequence'" - " AND coalesce(rootpage,1)>0", - zDbMain - ); - if( rc!=SQLITE_OK ) goto end_of_vacuum; - rc = execSqlF(db, pzErrMsg, - "SELECT sql FROM \"%w\".sqlite_schema" - " WHERE type='index'", - 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. - */ - rc = execSqlF(db, pzErrMsg, - "SELECT'INSERT INTO vacuum_db.'||quote(name)" - "||' SELECT*FROM\"%w\".'||quote(name)" - "FROM vacuum_db.sqlite_schema " - "WHERE type='table'AND coalesce(rootpage,1)>0", - zDbMain - ); - assert( (db->mDbFlags & DBFLAG_Vacuum)!=0 ); - db->mDbFlags &= ~DBFLAG_Vacuum; - 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 schema table. - */ - rc = execSqlF(db, pzErrMsg, - "INSERT INTO vacuum_db.sqlite_schema" - " SELECT*FROM \"%w\".sqlite_schema" - " WHERE type IN('view','trigger')" - " OR(type='table'AND rootpage=0)", - zDbMain - ); + if( !bReset ){ + /* Query the schema of the main database. Create a mirror schema + ** in the temporary database. + */ + db->init.iDb = nDb; /* force new CREATE statements into vacuum_db */ + rc = execSqlF(db, pzErrMsg, + "SELECT sql FROM \"%w\".sqlite_schema" + " WHERE type='table'AND name<>'sqlite_sequence'" + " AND coalesce(rootpage,1)>0", + zDbMain + ); + if( rc!=SQLITE_OK ) goto end_of_vacuum; + rc = execSqlF(db, pzErrMsg, + "SELECT sql FROM \"%w\".sqlite_schema" + " WHERE type='index'", + 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. + */ + rc = execSqlF(db, pzErrMsg, + "SELECT'INSERT INTO vacuum_db.'||quote(name)" + "||' SELECT*FROM\"%w\".'||quote(name)" + "FROM vacuum_db.sqlite_schema " + "WHERE type='table'AND coalesce(rootpage,1)>0", + zDbMain + ); + assert( (db->mDbFlags & DBFLAG_Vacuum)!=0 ); + db->mDbFlags &= ~DBFLAG_Vacuum; + 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 schema table. + */ + rc = execSqlF(db, pzErrMsg, + "INSERT INTO vacuum_db.sqlite_schema" + " SELECT*FROM \"%w\".sqlite_schema" + " WHERE type IN('view','trigger')" + " OR(type='table'AND rootpage=0)", + zDbMain + ); + }else{ + rc = sqlite3BtreeBeginTrans(pTemp, 2, 0); + } if( rc ) goto end_of_vacuum; + /* 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 Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -7634,23 +7634,27 @@ break; }; #endif /* SQLITE_OMIT_PRAGMA */ #if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH) -/* Opcode: Vacuum P1 P2 * * * +/* Opcode: Vacuum P1 P2 P3 * * ** ** Vacuum the entire database P1. P1 is 0 for "main", and 2 or more ** for an attached database. The "temp" database may not be vacuumed. ** ** If P2 is not zero, then it is a register holding a string which is ** the file into which the result of vacuum should be written. When ** P2 is zero, the vacuum overwrites the original database. +** +** If P3 is not zero, then delete all database content as part of the +** vacuum. It is not allowed for both P2 and P3 to be non-zero at the +** same time. */ case OP_Vacuum: { assert( p->readOnly==0 ); rc = sqlite3RunVacuum(&p->zErrMsg, db, pOp->p1, - pOp->p2 ? &aMem[pOp->p2] : 0); + pOp->p2 ? &aMem[pOp->p2] : 0, pOp->p3); if( rc ) goto abort_due_to_error; break; } #endif Index: tool/mkpragmatab.tcl ================================================================== --- tool/mkpragmatab.tcl +++ tool/mkpragmatab.tcl @@ -396,10 +396,13 @@ NAME: legacy_alter_table TYPE: FLAG ARG: SQLITE_LegacyAlter IF: !defined(SQLITE_OMIT_FLAG_PRAGMAS) + + NAME: reset_database + IF: !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH) } # Open the output file # set destfile "[file dir [file dir [file normal $argv0]]]/src/pragma.h"