From: drh <> Date: Wed, 28 Sep 2022 17:10:23 +0000 (+0000) Subject: Prototype implementation of "PRAGMA reset_database". This pragma differs from X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=refs%2Fheads%2Freset-database;p=thirdparty%2Fsqlite.git Prototype implementation of "PRAGMA reset_database". This pragma differs from SQLITE_DBCONFIG_RESET_DATABASE in that the pragma only works if the database is reasonably well-formed, whereas the dbconfig works regardless. FossilOrigin-Name: cf0999d4f8fd120c4796240600cd42fcc8baa056efad432d0dae7da372023787 --- diff --git a/manifest b/manifest index 824eb56c81..9606d49572 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Provide\sthe\sSQLITE_MAX_ALLOCATION_SIZE\scompile-time\soption\sfor\slimiting\sthe\nmaximum\smemory\sallocation\ssize. -D 2022-09-27T16:35:06.221 +C Prototype\simplementation\sof\s"PRAGMA\sreset_database".\s\sThis\spragma\sdiffers\sfrom\nSQLITE_DBCONFIG_RESET_DATABASE\sin\sthat\sthe\spragma\sonly\sworks\sif\sthe\sdatabase\nis\sreasonably\swell-formed,\swhereas\sthe\sdbconfig\sworks\sregardless. +D 2022-09-28T17:10:23.906 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -528,7 +528,7 @@ F src/auth.c f4fa91b6a90bbc8e0d0f738aa284551739c9543a367071f55574681e0f24f8cf F src/backup.c a2891172438e385fdbe97c11c9745676bec54f518d4447090af97189fd8e52d7 F src/bitvec.c 7c849aac407230278445cb069bebc5f89bf2ddd87c5ed9459b070a9175707b3d F src/btmutex.c 6ffb0a22c19e2f9110be0964d0731d2ef1c67b5f7fabfbaeb7b9dabc4b7740ca -F src/btree.c aad3381b25b0aa56838b35f86fbae9fb10ab8670ba130b413e6e3652cb732d74 +F src/btree.c 55688477058bb5733128e5d2ff385c58ea82d198ba8a90aeef087946343d9445 F src/btree.h 74d64b8f28cfa4a894d14d4ed64fa432cd697b98b61708d4351482ae15913e22 F src/btreeInt.h 8ce1332edd89dfd2461d561ac10a0ab5601c8e06200cb5230596c3caaf54482e F src/build.c 898884afd67d953808cb687babc15b66a10213f99fe2ce7db98960e959881f98 @@ -581,9 +581,9 @@ F src/parse.y 8e67d820030d2655b9942ffe61c1e7e6b96cea2f2f72183533299393907d0564 F src/pcache.c f4268f7f73c6a3db12ce22fd25bc68dc42315d19599414ab1207d7cf32f79197 F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586 F src/pcache1.c dee95e3cd2b61e6512dc814c5ab76d5eb36f0bfc9441dbb4260fccc0d12bbddc -F src/pragma.c 9bf7d8a2a9ad3bc36df3ec0d61817a44c38a1da527d59c26c203047f906e334a -F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7 -F src/prepare.c 971d5819a4bda88038c2283d71fc0a2974ffc3dd480f9bd941341017abacfd1b +F src/pragma.c 7a66d429ac95147809b496b0129636a4ed2913355da5868be990090861c97f27 +F src/pragma.h 05e527d0aef2e9fcf4854cd7af1a852c3d3f316936000e4968298ac1dd570a34 +F src/prepare.c 1730581972370ba6f7ce3ce713492d6bb21ae801cb566817540db1b3b46e8b82 F src/printf.c e99ee9741e79ae3873458146f59644276657340385ade4e76a5f5d1c25793764 F src/random.c 546d6feb15ec69c1aafe9bb351a277cbb498fd5410e646add673acb805714960 F src/resolve.c efea4e5fbecfd6d0a9071b0be0d952620991673391b6ffaaf4c277b0bb674633 @@ -593,7 +593,7 @@ F src/shell.c.in e7e7c2c69ae86c5ee9e8ad66227203d46ff6dce8700a1b1dababff01c71d33d F src/sqlite.h.in b9b7fd73239d94db20332bb6e504688001e5564b655e1318a4427a1caef4b99e F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h a988810c9b21c0dc36dc7a62735012339dc76fc7ab448fb0792721d30eacb69d -F src/sqliteInt.h 94e7fc2a5f0fa5d1f0af84513fd2d1c70a9f6e772556b9dfef16feee63291eae +F src/sqliteInt.h 7bba148584fdbc11f32dbdffaeb97f7957646aedec9ae27b3d9e6d03d9facd58 F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657 F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749 F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1 @@ -659,8 +659,8 @@ F src/update.c c52a7991bece0453d22c77c08469512ee2f1391c12503fd347d1c939220c5877 F src/upsert.c 8789047a8f0a601ea42fa0256d1ba3190c13746b6ba940fe2d25643a7e991937 F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0 F src/util.c 0be191521ff6d2805995f4910f0b6231b42843678b2efdc1abecaf39929a673f -F src/vacuum.c bb346170b0b54c6683bba4a5983aea40485597fdf605c87ec8bc2e199fe88cd8 -F src/vdbe.c 0d1e3c658d98a7bb7201532ea7a3e4d59bf9165421c780d5f84c361e372f1179 +F src/vacuum.c 7d641f22e047bc033d577cef75146f445a88ac23785e5131e74f053c6215f69a +F src/vdbe.c 57004f80fc2a7d45473a6c83cc18dc9a362f5d218f10dff740f1f6e3246b2b50 F src/vdbe.h 64619af62603dc3c4f5ff6ff6d2c8f389abd667a29ce6007ed44bd22b3211cd0 F src/vdbeInt.h 17b7461ffcf9ee760d1341731715a419f6b8c763089a7ece25c2e8098d702b3f F src/vdbeapi.c fc3183daf72808b4311b228989120fdbc2dc44972fb0d77d5c453460cc0e5b2c @@ -1932,7 +1932,7 @@ F tool/mkmsvcmin.tcl 6ecab9fe22c2c8de4d82d4c46797bda3d2deac8e763885f5a38d0c44a89 F tool/mkopcodec.tcl 33d20791e191df43209b77d37f0ff0904620b28465cca6990cf8d60da61a07ef F tool/mkopcodeh.tcl bcb2bd5affb545fd219ef0304c7978e2a356407ab723f45ec8569235892c1c3f F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa -F tool/mkpragmatab.tcl bd07bd59d45d0f3448e123d6937e9811195f9908a51e09d774609883055bfd3d +F tool/mkpragmatab.tcl cb827237c3184697c8335ff427085a31c1ac1ce18f5cdf893c9dfad78c98f3b1 F tool/mkshellc.tcl df5d249617f9cc94d5c48eb0401673eb3f31f383ecbc54e8a13ca3dd97e89450 F tool/mksourceid.c 36aa8020014aed0836fd13c51d6dc9219b0df1761d6b5f58ff5b616211b079b9 F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97 @@ -2000,8 +2000,11 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 5517bc50988b5339c2fd071b29de1b5ca03037b0b635c3b112cf7108fab54d5f -R d334d68f4ee6840cbb06511749063d29 +P 584de6a996c78b8e41bdfcd05a8e2a3844664c6b4efedb5883c8b8af388462b5 +R df3dd3e3ab8a07ab9eb6697e699f0af2 +T *branch * reset-database +T *sym-reset-database * +T -sym-trunk * U drh -Z 739e270ad16b7bf5c87cbcd809fe00c5 +Z 8ce23413b5db3390219d6d34f63e62a0 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a06b3b3074..8be2aa71ca 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -584de6a996c78b8e41bdfcd05a8e2a3844664c6b4efedb5883c8b8af388462b5 \ No newline at end of file +cf0999d4f8fd120c4796240600cd42fcc8baa056efad432d0dae7da372023787 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index a08ed95d14..4aef986374 100644 --- a/src/btree.c +++ b/src/btree.c @@ -3502,16 +3502,16 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){ } 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 diff --git a/src/pragma.c b/src/pragma.c index 9d46a10dcd..c40bbc55cf 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -2385,6 +2385,20 @@ void sqlite3Pragma( 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 diff --git a/src/pragma.h b/src/pragma.h index 7270db1db4..9087f3553b 100644 --- a/src/pragma.h +++ b/src/pragma.h @@ -38,19 +38,20 @@ #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 */ @@ -500,6 +501,15 @@ static const PragmaName aPragmaName[] = { /* 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, @@ -657,4 +667,4 @@ static const PragmaName aPragmaName[] = { /* iArg: */ SQLITE_WriteSchema|SQLITE_NoSchemaError }, #endif }; -/* Number of pragmas: 68 on by default, 78 total. */ +/* Number of pragmas: 69 on by default, 79 total. */ diff --git a/src/prepare.c b/src/prepare.c index f66c366b99..da85335dbd 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -286,9 +286,6 @@ int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFlags){ 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 diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 148ba75949..b01a21291c 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -4739,7 +4739,7 @@ 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); diff --git a/src/vacuum.c b/src/vacuum.c index 9899b63cf1..1a9b916236 100644 --- a/src/vacuum.c +++ b/src/vacuum.c @@ -144,7 +144,8 @@ 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 */ @@ -162,6 +163,8 @@ SQLITE_NOINLINE int sqlite3RunVacuum( 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 */ @@ -269,54 +272,59 @@ SQLITE_NOINLINE int sqlite3RunVacuum( 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 diff --git a/src/vdbe.c b/src/vdbe.c index ebf52e67af..88698dab00 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -7636,7 +7636,7 @@ case OP_JournalMode: { /* out2 */ #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. @@ -7644,11 +7644,15 @@ case OP_JournalMode: { /* out2 */ ** 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; } diff --git a/tool/mkpragmatab.tcl b/tool/mkpragmatab.tcl index 5c36df0497..ac1042d9a5 100644 --- a/tool/mkpragmatab.tcl +++ b/tool/mkpragmatab.tcl @@ -398,6 +398,9 @@ set pragma_def { 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