From bc2e7fc228f2a8c41bb1702aff1a9f450f8bfcda Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 9 Sep 2022 20:44:56 +0000 Subject: [PATCH] Further fixes to comments in sqlite3recover.h. Also rework some data structures in sqlite3recover.c. FossilOrigin-Name: 599d1f8ec2f9e24924a6f9e66c85664360c7b95531b07a4efe1dd8c096b3fc99 --- ext/recover/sqlite3recover.c | 140 ++++++++++++++++++++++++++--------- ext/recover/sqlite3recover.h | 82 +++++++++++++++----- ext/recover/test_recover.c | 2 +- manifest | 18 ++--- manifest.uuid | 2 +- test/savepoint.test | 2 + 6 files changed, 181 insertions(+), 65 deletions(-) diff --git a/ext/recover/sqlite3recover.c b/ext/recover/sqlite3recover.c index 7d05ec2b3e..e6975b8771 100644 --- a/ext/recover/sqlite3recover.c +++ b/ext/recover/sqlite3recover.c @@ -20,7 +20,76 @@ typedef unsigned int u32; typedef sqlite3_int64 i64; +typedef struct RecoverTable RecoverTable; typedef struct RecoverColumn RecoverColumn; + +/* +** When recovering rows of data that can be associated with table +** definitions recovered from the sqlite_schema table, each table is +** represented by an instance of the following object. +** +** iRoot: +** The root page in the original database. Not necessarily (and usually +** not) the same in the recovered database. +** +** zTab: +** Name of the table. +** +** nCol/aCol[]: +** aCol[] is an array of nCol columns. In the order in which they appear +** in the table. +** +** bIntkey: +** Set to true for intkey tables, false for WITHOUT ROWID. +** +** iRowidBind: +** Each column in the aCol[] array has associated with it the index of +** the bind parameter its values will be bound to in the INSERT statement +** used to construct the output database. If the table does has a rowid +** but not an INTEGER PRIMARY KEY column, then iRowidBind contains the +** index of the bind paramater to which the rowid value should be bound. +** Otherwise, it contains -1. If the table does contain an INTEGER PRIMARY +** KEY column, then the rowid value should be bound to the index associated +** with the column. +** +** pNext: +** All RecoverTable objects used by the recovery operation are allocated +** and populated as part of creating the recovered database schema in +** the output database, before any non-schema data are recovered. They +** are then stored in a singly-linked list linked by this variable beginning +** at sqlite3_recover.pTblList. +*/ +struct RecoverTable { + u32 iRoot; /* Root page in original database */ + char *zTab; /* Name of table */ + int nCol; /* Number of columns in table */ + RecoverColumn *aCol; /* Array of columns */ + int bIntkey; /* True for intkey, false for without rowid */ + int iRowidBind; /* If >0, bind rowid to INSERT here */ + RecoverTable *pNext; +}; + +/* +** Each database column is represented by an instance of the following object +** stored in the RecoverTable.aCol[] array of the associated table. +** +** iField: +** The index of the associated field within database records. Or -1 if +** there is no associated field (e.g. for virtual generated columns). +** +** iBind: +** The bind index of the INSERT statement to bind this columns values +** to. Or 0 if there is no such index (iff (iField<0)). +** +** bIPK: +** True if this is the INTEGER PRIMARY KEY column. +** +** zCol: +** Name of column. +** +** eHidden: +** A RECOVER_EHIDDEN_* constant value (see below for interpretation of each). +*/ struct RecoverColumn { int iField; /* Field in record on disk */ int iBind; /* Binding to use in INSERT */ @@ -29,59 +98,62 @@ struct RecoverColumn { int eHidden; }; -#define RECOVER_EHIDDEN_NONE 0 -#define RECOVER_EHIDDEN_HIDDEN 1 -#define RECOVER_EHIDDEN_VIRTUAL 2 -#define RECOVER_EHIDDEN_STORED 3 +#define RECOVER_EHIDDEN_NONE 0 /* Normal database column */ +#define RECOVER_EHIDDEN_HIDDEN 1 /* Column is __HIDDEN__ */ +#define RECOVER_EHIDDEN_VIRTUAL 2 /* Virtual generated column */ +#define RECOVER_EHIDDEN_STORED 3 /* Stored generated column */ /* -** When running the ".recover" command, each output table, and the special -** orphaned row table if it is required, is represented by an instance -** of the following struct. +** Bitmap object used to track pages in the input database. Allocated +** and manipulated only by the following functions: ** -** aCol[]: -** Array of nCol columns. In the order in which they appear in the table. +** recoverBitmapAlloc() +** recoverBitmapFree() +** recoverBitmapSet() +** recoverBitmapQuery() +** +** nPg: +** Largest page number that may be stored in the bitmap. The range +** of valid keys is 1 to nPg, inclusive. +** +** aElem[]: +** Array large enough to contain a bit for each key. For key value +** iKey, the associated bit is the bit (iKey%32) of aElem[iKey/32]. +** In other words, the following is true if bit iKey is set, or +** false if it is clear: +** +** (aElem[iKey/32] & (1 << (iKey%32))) ? 1 : 0 */ -typedef struct RecoverTable RecoverTable; -struct RecoverTable { - u32 iRoot; /* Root page in original database */ - char *zTab; /* Name of table */ - int nCol; /* Number of columns in table */ - RecoverColumn *aCol; /* Array of columns */ - int bIntkey; /* True for intkey, false for without rowid */ - int iRowidBind; /* If >0, bind rowid to INSERT here */ - - RecoverTable *pNext; -}; - typedef struct RecoverBitmap RecoverBitmap; struct RecoverBitmap { i64 nPg; /* Size of bitmap */ u32 aElem[0]; /* Array of 32-bit bitmasks */ }; - +/* +** +*/ struct sqlite3_recover { - sqlite3 *dbIn; - sqlite3 *dbOut; + sqlite3 *dbIn; /* Input database */ + sqlite3 *dbOut; /* Output database */ - sqlite3_stmt *pGetPage; + sqlite3_stmt *pGetPage; /* SELECT against input db sqlite_dbdata */ - char *zDb; - char *zUri; - RecoverTable *pTblList; + char *zDb; /* Name of input db ("main" etc.) */ + char *zUri; /* URI for output database */ + RecoverTable *pTblList; /* List of tables recovered from schem */ RecoverBitmap *pUsed; /* Used by recoverLostAndFound() */ int errCode; /* For sqlite3_recover_errcode() */ char *zErrMsg; /* For sqlite3_recover_errmsg() */ - char *zStateDb; + char *zStateDb; /* State database to use (or NULL) */ char *zLostAndFound; /* Name of lost-and-found table (or NULL) */ - int bFreelistCorrupt; - int bRecoverRowid; + int bFreelistCorrupt; /* SQLITE_RECOVER_FREELIST_CORRUPT setting */ + int bRecoverRowid; /* SQLITE_RECOVER_ROWIDS setting */ - void *pSqlCtx; - int (*xSql)(void*,const char*); + void *pSqlCtx; /* SQL callback context */ + int (*xSql)(void*,const char*); /* Pointer to SQL callback function */ }; /* @@ -1340,7 +1412,7 @@ int sqlite3_recover_config(sqlite3_recover *p, int op, void *pArg){ int rc = SQLITE_OK; switch( op ){ - case SQLITE_RECOVER_TESTDB: + case 789: sqlite3_free(p->zStateDb); p->zStateDb = recoverMPrintf(p, "%s", (char*)pArg); break; diff --git a/ext/recover/sqlite3recover.h b/ext/recover/sqlite3recover.h index ffe016b4f3..30771d8804 100644 --- a/ext/recover/sqlite3recover.h +++ b/ext/recover/sqlite3recover.h @@ -37,7 +37,6 @@ ** using sqlite3_recover_finish(). */ - #ifndef _SQLITE_RECOVER_H #define _SQLITE_RECOVER_H @@ -95,13 +94,18 @@ sqlite3_recover *sqlite3_recover_init_sql( ); /* +** Configure an sqlite3_recover object that has just been created using +** sqlite3_recover_init() or sqlite3_recover_init_sql(). The second +** argument passed to this function must be one of the SQLITE_RECOVER_* +** symbols defined below. Valid values for the third argument depend +** on the specific SQLITE_RECOVER_* symbol in use. ** +** SQLITE_OK is returned if the configuration operation was successful, +** or an SQLite error code otherwise. */ int sqlite3_recover_config(sqlite3_recover*, int op, void *pArg); /* -** SQLITE_RECOVER_TESTDB: -** ** SQLITE_RECOVER_LOST_AND_FOUND: ** The pArg argument points to a string buffer containing the name ** of a "lost-and-found" table in the output database, or NULL. If @@ -118,41 +122,79 @@ int sqlite3_recover_config(sqlite3_recover*, int op, void *pArg); ** corrupt and an attempt is made to recover records from pages that ** appear to be linked into the freelist. Otherwise, pages on the freelist ** are ignored. Setting this option can recover more data from the -** database, but often ends up "recovering" deleted records. +** database, but often ends up "recovering" deleted records. The default +** value is 0 (clear). ** ** SQLITE_RECOVER_ROWIDS: -** -** SQLITE_RECOVER_SQLHOOK: +** The pArg value must actually be a pointer to a value of type +** int containing value 0 or 1 cast as a (void*). If this option is set +** (argument is 1), then an attempt is made to recover rowid values +** that are not also INTEGER PRIMARY KEY values. If this option is +** clear, then new rowids are assigned to all recovered rows. The +** default value is 1 (set). */ -#define SQLITE_RECOVER_TESTDB 789 -#define SQLITE_RECOVER_LOST_AND_FOUND 790 -#define SQLITE_RECOVER_FREELIST_CORRUPT 791 -#define SQLITE_RECOVER_ROWIDS 792 +#define SQLITE_RECOVER_LOST_AND_FOUND 1 +#define SQLITE_RECOVER_FREELIST_CORRUPT 2 +#define SQLITE_RECOVER_ROWIDS 3 /* -** Run the recovery. Return an SQLite error code if an error occurs, or -** SQLITE_OK otherwise. +** Run the recovery operation. This function does not return until the +** recovery operation is completed - either the new database has been +** created and populated (sqlite3_recover_init()) or all SQL statements have +** been passed to the callback (sqlite3_recover_init_sql()) - or an error +** occurs. If the recovery is completed without error, SQLITE_OK +** is returned. It is not considered an error if data cannot be recovered +** due to database corruption. +** +** If an error (for example an out-of-memory or IO error) occurs, then +** an SQLite error code is returned. The final state of the output database +** or the results of running any SQL statements already passed to the +** callback in this case are undefined. An English language error +** message corresponding to the error may be available via the +** sqlite3_recover_errmsg() API. +** +** This function may only be called once on an sqlite3_recover handle. +** If it is called more than once, the second and subsequent calls +** return SQLITE_MISUSE. The error code and error message returned +** by sqlite3_recover_errcode() and sqlite3_recover_errmsg() are not +** updated in this case. */ int sqlite3_recover_run(sqlite3_recover*); /* -** Return a pointer to a buffer containing the English language error -** message stored in the sqlite3_recover handle. If no error message -** is available (including in the case where no error has occurred), -** NULL is returned. +** If this is called on an sqlite3_recover handle before +** sqlite3_recover_run() has been called, or if the call to +** sqlite3_recover_run() returned SQLITE_OK, then this API always returns +** a NULL pointer. +** +** Otherwise, an attempt is made to return a pointer to a buffer containing +** an English language error message related to the error that occurred +** within the sqlite3_recover_run() call. If no error message is available, +** or if an out-of-memory error occurs while attempting to allocate a buffer +** for one, NULL may still be returned. +** +** The buffer remains valid until the sqlite3_recover handle is destroyed +** using sqlite3_recover_finish(). */ const char *sqlite3_recover_errmsg(sqlite3_recover*); /* -** Return the recover handle error code. SQLITE_OK is returned if no error -** has occurred. +** If this function is called on an sqlite3_recover handle before +** sqlite3_recover_run() has been called, it always returns SQLITE_OK. +** Otherwise, it returns a copy of the value returned by the first +** sqlite3_recover_run() call made on the handle. */ int sqlite3_recover_errcode(sqlite3_recover*); /* ** Clean up a recovery object created by a call to sqlite3_recover_init(). -** This function returns SQLITE_OK if no error occurred, or else a copy -** of the recover handle error code. +** The results of using a recovery object with any API after it has been +** passed to this function are undefined. +** +** If this function is called on an sqlite3_recover handle before +** sqlite3_recover_run() has been called, it always returns SQLITE_OK. +** Otherwise, it returns a copy of the value returned by the first +** sqlite3_recover_run() call made on the handle. */ int sqlite3_recover_finish(sqlite3_recover*); diff --git a/ext/recover/test_recover.c b/ext/recover/test_recover.c index e7fef42fd7..a31ddbb94a 100644 --- a/ext/recover/test_recover.c +++ b/ext/recover/test_recover.c @@ -117,7 +117,7 @@ static int testRecoverCmd( switch( iOp ){ case 0: res = sqlite3_recover_config(pTest->p, - SQLITE_RECOVER_TESTDB, (void*)Tcl_GetString(objv[3]) + 789, (void*)Tcl_GetString(objv[3]) /* MAGIC NUMBER! */ ); break; case 1: diff --git a/manifest b/manifest index 3da6b77b47..3f230d9467 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Handle\srecovery\sof\svirtual\stables\sby\srecovering\seach\sshadow\stable\sindividually,\sthen\swriting\sthe\sCREATE\sVIRTUAL\sTABLE\sstatement\sdirectly\sinto\sthe\ssqlite_schema\stable. -D 2022-09-09T16:25:19.064 +C Further\sfixes\sto\scomments\sin\ssqlite3recover.h.\sAlso\srework\ssome\sdata\sstructures\sin\ssqlite3recover.c. +D 2022-09-09T20:44:56.135 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -392,9 +392,9 @@ F ext/recover/recover_common.tcl 6679af7dffc858e345053a91c9b0a897595b4a13007acef F ext/recover/recoverclobber.test e6537ebf99f57bfff6cca59550b5f4278319b57a89865abb98d755a8fd561d84 F ext/recover/recoverold.test f368a6ae2db12b6017257b332a19ab5df527f4061e43f12f5c85d8e2b236f074 F ext/recover/recoverrowid.test ec4436cd69e6cdacb48dd2963ff6dd9dbd5fe648376de5e7c0c2f4f6cbacb417 -F ext/recover/sqlite3recover.c 7ed9a573d081435a16982b972c6bb963d273a0a457ec69fa16b0918165991e9d -F ext/recover/sqlite3recover.h 73648878cea3492427b9a8facebdad0464ed15b161ed1cd70f349a02456e8879 -F ext/recover/test_recover.c b973526785e145e7d9d7920ccde72112025ee5204e22b332db9d86796be73f00 +F ext/recover/sqlite3recover.c e8d0eae7da7ba24e733f6247082c12d7a030ce794eb5c42d64993f55c12882f5 +F ext/recover/sqlite3recover.h 81108efb8c4618d3d9c6da4df785212b0e4501aa0d25edfc463405fe839a6640 +F ext/recover/test_recover.c ed8d0cc8703ab29cf562f793623b045de109b7937f254108ff4132f35abb37fb F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15 F ext/repair/checkfreelist.c e21f06995ff4efdc1622dcceaea4dcba2caa83ca2f31a1607b98a8509168a996 F ext/repair/checkindex.c 4383e4469c21e5b9ae321d0d63cec53e981af9d7a6564be6374f0eeb93dfc890 @@ -1398,7 +1398,7 @@ F test/rowvaluefault.test 963ae9cdaed30a85a29668dd514e639f3556cae903ee9f172ea972 F test/rowvaluevtab.test cd9747bb3f308086944c07968f547ad6b05022e698d80b9ffbdfe09ce0b8da6f F test/rtree.test 0c8d9dd458d6824e59683c19ab2ffa9ef946f798 F test/run-wordcount.sh 891e89c4c2d16e629cd45951d4ed899ad12afc09 -F test/savepoint.test 1f8a6b1aea9a0d05837adc463d4bf47bd9d0f1c842f1c2a9caccd639baf34bf9 +F test/savepoint.test 6e9804a17767f08432c7a5e738b9a8f4b891d243110b63d3a41d270d3d1378ec F test/savepoint2.test 9b8543940572a2f01a18298c3135ad0c9f4f67d7 F test/savepoint4.test c8f8159ade6d2acd9128be61e1230f1c1edc6cc0 F test/savepoint5.test 0735db177e0ebbaedc39812c8d065075d563c4fd @@ -2007,8 +2007,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 14164047c43e3ca43aa010c77ea00dfa85400e15645ee0f5b90a677898b6a836 -R e52e610730a5083b962d51491c2e90a5 +P 5f2d5ccd56c06c3468377126acfd4be39b79b05bb6fb09b674b2e185df143aa3 +R d4558eaeefd2a718275b323dc4307f9b U dan -Z 0661ae93944ad96dd9dbcc27159d9d3c +Z 6e64e7331e56e0c44a533586bb618920 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index c6ebbcda0e..a1b607602a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5f2d5ccd56c06c3468377126acfd4be39b79b05bb6fb09b674b2e185df143aa3 \ No newline at end of file +599d1f8ec2f9e24924a6f9e66c85664360c7b95531b07a4efe1dd8c096b3fc99 \ No newline at end of file diff --git a/test/savepoint.test b/test/savepoint.test index eed8a9e702..3952981dab 100644 --- a/test/savepoint.test +++ b/test/savepoint.test @@ -16,6 +16,8 @@ source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl +forcedelete test2.db + #---------------------------------------------------------------------- # The following tests - savepoint-1.* - test that the SAVEPOINT, RELEASE # and ROLLBACK TO comands are correctly parsed, and that the auto-commit