From: dan Date: Sat, 10 Sep 2022 20:01:49 +0000 (+0000) Subject: Rework recover extension code for readability. X-Git-Tag: version-3.40.0~91^2~30 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=65660916dc906dc2323b704caf937e1c0cfc6c9b;p=thirdparty%2Fsqlite.git Rework recover extension code for readability. FossilOrigin-Name: 1a2540960e40e3c8c622448fd3862e249bd463c29ae4ce5e39942e942533f60a --- diff --git a/ext/recover/sqlite3recover.c b/ext/recover/sqlite3recover.c index e6975b8771..9c5e10433b 100644 --- a/ext/recover/sqlite3recover.c +++ b/ext/recover/sqlite3recover.c @@ -17,6 +17,16 @@ #include #include +/* +** Declaration for public API function in file dbdata.c. This may be called +** with NULL as the final two arguments to register the sqlite_dbptr and +** sqlite_dbdata virtual tables with a database handle. +*/ +#ifdef _WIN32 +__declspec(dllexport) +#endif +int sqlite3_dbdata_init(sqlite3*, char**, const sqlite3_api_routines*); + typedef unsigned int u32; typedef sqlite3_int64 i64; @@ -131,29 +141,32 @@ struct RecoverBitmap { }; /* -** +** Main recover handle structure. */ struct sqlite3_recover { + /* Copies of sqlite3_recover_init[_sql]() parameters */ sqlite3 *dbIn; /* Input database */ - sqlite3 *dbOut; /* Output database */ - - sqlite3_stmt *pGetPage; /* SELECT against input db sqlite_dbdata */ - 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() */ + void *pSqlCtx; /* SQL callback context */ + int (*xSql)(void*,const char*); /* Pointer to SQL callback function */ + /* Values configured by sqlite3_recover_config() */ char *zStateDb; /* State database to use (or NULL) */ char *zLostAndFound; /* Name of lost-and-found table (or NULL) */ int bFreelistCorrupt; /* SQLITE_RECOVER_FREELIST_CORRUPT setting */ int bRecoverRowid; /* SQLITE_RECOVER_ROWIDS setting */ - void *pSqlCtx; /* SQL callback context */ - int (*xSql)(void*,const char*); /* Pointer to SQL callback function */ + /* Error code and error message */ + int errCode; /* For sqlite3_recover_errcode() */ + char *zErrMsg; /* For sqlite3_recover_errmsg() */ + + /* Fields used within sqlite3_recover_run() */ + int bRun; /* True once _recover_run() has been called */ + sqlite3 *dbOut; /* Output database */ + sqlite3_stmt *pGetPage; /* SELECT against input db sqlite_dbdata */ + RecoverTable *pTblList; /* List of tables recovered from schem */ + RecoverBitmap *pUsed; /* Used by recoverLostAndFound() */ }; /* @@ -172,6 +185,15 @@ static int recoverStrlen(const char *zStr){ return nRet; } +/* +** This function is a no-op if the recover handle passed as the first +** argument already contains an error (if p->errCode!=SQLITE_OK). +** +** Otherwise, an attempt is made to allocate, zero and return a buffer nByte +** bytes in size. If successful, a pointer to the new buffer is returned. Or, +** if an OOM error occurs, NULL is returned and the handle error code +** (p->errCode) set to SQLITE_NOMEM. +*/ static void *recoverMalloc(sqlite3_recover *p, i64 nByte){ void *pRet = 0; assert( nByte>0 ); @@ -186,6 +208,19 @@ static void *recoverMalloc(sqlite3_recover *p, i64 nByte){ return pRet; } +/* +** Set the error code and error message for the recover handle passed as +** the first argument. The error code is set to the value of parameter +** errCode. +** +** Parameter zFmt must be a printf() style formatting string. The handle +** error message is set to the result of using any trailing arguments for +** parameter substitutions in the formatting string. +** +** For example: +** +** recoverError(p, SQLITE_ERROR, "no such table: %s", zTablename); +*/ static int recoverError( sqlite3_recover *p, int errCode, @@ -204,6 +239,14 @@ static int recoverError( } +/* +** This function is a no-op if p->errCode is initially other than SQLITE_OK. +** In this case it returns NULL. +** +** Otherwise, an attempt is made to allocate and return a bitmap object +** large enough to store a bit for all page numbers between 1 and nPg, +** inclusive. The bitmap is initially zeroed. +*/ static RecoverBitmap *recoverBitmapAlloc(sqlite3_recover *p, i64 nPg){ int nElem = (nPg+1+31) / 32; int nByte = sizeof(RecoverBitmap) + nElem*sizeof(u32); @@ -215,10 +258,16 @@ static RecoverBitmap *recoverBitmapAlloc(sqlite3_recover *p, i64 nPg){ return pRet; } +/* +** Free a bitmap object allocated by recoverBitmapAlloc(). +*/ static void recoverBitmapFree(RecoverBitmap *pMap){ sqlite3_free(pMap); } +/* +** Set the bit associated with page iPg in bitvec pMap. +*/ static void recoverBitmapSet(RecoverBitmap *pMap, i64 iPg){ if( iPg<=pMap->nPg ){ int iElem = (iPg / 32); @@ -227,6 +276,10 @@ static void recoverBitmapSet(RecoverBitmap *pMap, i64 iPg){ } } +/* +** Query bitmap object pMap for the state of the bit associated with page +** iPg. Return 1 if it is set, or 0 otherwise. +*/ static int recoverBitmapQuery(RecoverBitmap *pMap, i64 iPg){ int ret = 1; if( iPg<=pMap->nPg ){ @@ -237,11 +290,24 @@ static int recoverBitmapQuery(RecoverBitmap *pMap, i64 iPg){ return ret; } - +/* +** Set the recover handle error to the error code and message returned by +** calling sqlite3_errcode() and sqlite3_errmsg(), respectively, on database +** handle db. +*/ static int recoverDbError(sqlite3_recover *p, sqlite3 *db){ return recoverError(p, sqlite3_errcode(db), "%s", sqlite3_errmsg(db)); } +/* +** This function is a no-op if recover handle p already contains an error +** (if p->errCode!=SQLITE_OK). +** +** Otherwise, it attempts to prepare the SQL statement in zSql against +** database handle db. If successful, the statement handle is returned. +** Or, if an error occurs, NULL is returned and an error left in the +** recover handle. +*/ static sqlite3_stmt *recoverPrepare( sqlite3_recover *p, sqlite3 *db, @@ -257,7 +323,15 @@ static sqlite3_stmt *recoverPrepare( } /* -** Create a prepared statement using printf-style arguments for the SQL. +** This function is a no-op if recover handle p already contains an error +** (if p->errCode!=SQLITE_OK). +** +** Otherwise, argument zFmt is used as a printf() style format string, +** along with any trailing arguments, to create an SQL statement. This +** SQL statement is prepared against database handle db and, if successful, +** the statment handle returned. Or, if an error occurs - either during +** the printf() formatting or when preparing the resulting SQL - an +** error code and message are left in the recover handle. */ static sqlite3_stmt *recoverPreparePrintf( sqlite3_recover *p, @@ -281,7 +355,15 @@ static sqlite3_stmt *recoverPreparePrintf( return pStmt; } - +/* +** Reset SQLite statement handle pStmt. If the call to sqlite3_reset() +** indicates that an error occurred, and there is not already an error +** in the recover handle passed as the first argument, set the error +** code and error message appropriately. +** +** This function returns a copy of the statement handle pointer passed +** as the second argument. +*/ static sqlite3_stmt *recoverReset(sqlite3_recover *p, sqlite3_stmt *pStmt){ int rc = sqlite3_reset(pStmt); if( rc!=SQLITE_OK && p->errCode==SQLITE_OK ){ @@ -290,6 +372,12 @@ static sqlite3_stmt *recoverReset(sqlite3_recover *p, sqlite3_stmt *pStmt){ return pStmt; } +/* +** Finalize SQLite statement handle pStmt. If the call to sqlite3_reset() +** indicates that an error occurred, and there is not already an error +** in the recover handle passed as the first argument, set the error +** code and error message appropriately. +*/ static void recoverFinalize(sqlite3_recover *p, sqlite3_stmt *pStmt){ sqlite3 *db = sqlite3_db_handle(pStmt); int rc = sqlite3_finalize(pStmt); @@ -298,6 +386,15 @@ static void recoverFinalize(sqlite3_recover *p, sqlite3_stmt *pStmt){ } } +/* +** This function is a no-op if recover handle p already contains an error +** (if p->errCode!=SQLITE_OK). A copy of p->errCode is returned in this +** case. +** +** Otherwise, execute SQL script zSql. If successful, return SQLITE_OK. +** Or, if an error occurs, leave an error code and message in the recover +** handle and return a copy of the error code. +*/ static int recoverExec(sqlite3_recover *p, sqlite3 *db, const char *zSql){ if( p->errCode==SQLITE_OK ){ int rc = sqlite3_exec(db, zSql, 0, 0, 0); @@ -308,6 +405,20 @@ static int recoverExec(sqlite3_recover *p, sqlite3 *db, const char *zSql){ return p->errCode; } +/* +** This function is a no-op if recover handle p already contains an error +** (if p->errCode!=SQLITE_OK). NULL is returned in this case. +** +** Otherwise, an attempt is made to interpret zFmt as a printf() style +** formatting string and the result of using the trailing arguments for +** parameter substitution with it written into a buffer obtained from +** sqlite3_malloc(). If successful, a pointer to the buffer is returned. +** It is the responsibility of the caller to eventually free the buffer +** using sqlite3_free(). +** +** Or, if an error occurs, an error code and message is left in the recover +** handle and NULL returned. +*/ static char *recoverMPrintf(sqlite3_recover *p, const char *zFmt, ...){ va_list ap; char *z; @@ -324,9 +435,13 @@ static char *recoverMPrintf(sqlite3_recover *p, const char *zFmt, ...){ } /* -** Execute "PRAGMA page_count" against the input database. If successful, -** return the integer result. Or, if an error occurs, leave an error code -** and error message in the sqlite3_recover handle. +** This function is a no-op if recover handle p already contains an error +** (if p->errCode!=SQLITE_OK). Zero is returned in this case. +** +** Otherwise, execute "PRAGMA page_count" against the input database. If +** successful, return the integer result. Or, if an error occurs, leave an +** error code and error message in the sqlite3_recover handle and return +** zero. */ static i64 recoverPageCount(sqlite3_recover *p){ i64 nPg = 0; @@ -342,10 +457,12 @@ static i64 recoverPageCount(sqlite3_recover *p){ } /* -** Scalar function "read_i32". The first argument to this function -** must be a blob. The second a non-negative integer. This function -** reads and returns a 32-bit big-endian integer from byte +** Implementation of SQL scalar function "read_i32". The first argument to +** this function must be a blob. The second a non-negative integer. This +** function reads and returns a 32-bit big-endian integer from byte ** offset (4*) of the blob. +** +** SELECT read_i32(, ) */ static void recoverReadI32( sqlite3_context *context, @@ -372,7 +489,16 @@ static void recoverReadI32( } /* -** SELECT page_is_used(pgno); +** Implementation of SQL scalar function "page_is_used". This function +** is used as part of the procedure for locating orphan rows for the +** lost-and-found table, and it depends on those routines having populated +** the sqlite3_recover.pUsed variable. +** +** The only argument to this function is a page-number. It returns true +** if the page has already been used somehow during data recovery, or false +** otherwise. +** +** SELECT page_is_used(); */ static void recoverPageIsUsed( sqlite3_context *pCtx, @@ -394,7 +520,7 @@ static void recoverPageIsUsed( ** sqlite_dbdata and sqlite_dbptr virtual table modules to access pages ** of the database being recovered. ** -** This function always takes a single integer argument. If the arguement +** This function always takes a single integer argument. If the argument ** is zero, then the value returned is the number of pages in the db being ** recovered. If the argument is greater than zero, it is a page number. ** The value returned in this case is an SQL blob containing the data for @@ -462,18 +588,17 @@ static const char *recoverUnusedString( return zBuf; } - /* -** Scalar function "escape_crnl". The argument passed to this function is the -** output of built-in function quote(). If the first character of the input is -** "'", indicating that the value passed to quote() was a text value, then this -** function searches the input for "\n" and "\r" characters and adds a wrapper -** similar to the following: +** Implementation of scalar SQL function "escape_crnl". The argument passed to +** this function is the output of built-in function quote(). If the first +** character of the input is "'", indicating that the value passed to quote() +** was a text value, then this function searches the input for "\n" and "\r" +** characters and adds a wrapper similar to the following: ** ** replace(replace(, '\n', char(10), '\r', char(13)); ** -** Or, if the first character of the input is not "'", then a copy -** of the input is returned. +** Or, if the first character of the input is not "'", then a copy of the input +** is returned. */ static void recoverEscapeCrnl( sqlite3_context *context, @@ -552,12 +677,20 @@ static void recoverEscapeCrnl( sqlite3_result_value(context, argv[0]); } - -#ifdef _WIN32 -__declspec(dllexport) -#endif -int sqlite3_dbdata_init(sqlite3*, char**, const sqlite3_api_routines*); - +/* +** This function is a no-op if recover handle p already contains an error +** (if p->errCode!=SQLITE_OK). A copy of the error code is returned in +** this case. +** +** Otherwise, an attempt is made to open the output database, attach +** and create the schema of the temporary database used to store +** intermediate data, and to register all required user functions and +** virtual table modules with the output handle. +** +** If no error occurs, SQLITE_OK is returned. Otherwise, an error code +** and error message are left in the recover handle and a copy of the +** error code returned. +*/ static int recoverOpenOutput(sqlite3_recover *p){ struct Func { const char *zName; @@ -576,17 +709,19 @@ static int recoverOpenOutput(sqlite3_recover *p){ assert( p->dbOut==0 ); - if( sqlite3_open_v2(p->zUri, &db, flags, 0) ){ - recoverDbError(p, db); - }else{ - char *zSql = recoverMPrintf(p, "ATTACH %Q AS recovery;", p->zStateDb); - recoverExec(p, db, zSql); - recoverExec(p, db, - "PRAGMA writable_schema = 1;" - "CREATE TABLE recovery.map(pgno INTEGER PRIMARY KEY, parent INT);" - "CREATE TABLE recovery.schema(type, name, tbl_name, rootpage, sql);" - ); - sqlite3_free(zSql); + if( p->errCode==SQLITE_OK ){ + if( sqlite3_open_v2(p->zUri, &db, flags, 0) ){ + recoverDbError(p, db); + }else{ + char *zSql = recoverMPrintf(p, "ATTACH %Q AS recovery;", p->zStateDb); + recoverExec(p, db, zSql); + recoverExec(p, db, + "PRAGMA writable_schema = 1;" + "CREATE TABLE recovery.map(pgno INTEGER PRIMARY KEY, parent INT);" + "CREATE TABLE recovery.schema(type, name, tbl_name, rootpage, sql);" + ); + sqlite3_free(zSql); + } } /* Register the sqlite_dbdata and sqlite_dbptr virtual table modules. @@ -624,6 +759,19 @@ static int recoverOpenOutput(sqlite3_recover *p){ return p->errCode; } +/* +** This function is a no-op if recover handle p already contains an error +** (if p->errCode!=SQLITE_OK). A copy of the error code is returned in +** this case. +** +** Otherwise, attempt to populate temporary table "recovery.schema" with the +** parts of the database schema that can be extracted from the input database. +** +** If no error occurs, SQLITE_OK is returned. Otherwise, an error code +** and error message are left in the recover handle and a copy of the +** error code returned. It is not considered an error if part of all of +** the database schema cannot be recovered due to corruption. +*/ static int recoverCacheSchema(sqlite3_recover *p){ return recoverExec(p, p->dbOut, "WITH RECURSIVE pages(p) AS (" @@ -643,7 +791,24 @@ static int recoverCacheSchema(sqlite3_recover *p){ ); } -static void recoverAddTable(sqlite3_recover *p, const char *zName, i64 iRoot){ +/* +** This function is a no-op if recover handle p already contains an error +** (if p->errCode!=SQLITE_OK). +** +** Otherwise, argument zName must be the name of a table that has just been +** created in the output database. This function queries the output db +** for the schema of said table, and creates a RecoverTable object to +** store the schema in memory. The new RecoverTable object is linked into +** the list at sqlite3_recover.pTblList. +** +** Parameter iRoot must be the root page of table zName in the INPUT +** database. +*/ +static void recoverAddTable( + sqlite3_recover *p, + const char *zName, /* Name of table created in output db */ + i64 iRoot /* Root page of same table in INPUT db */ +){ sqlite3_stmt *pStmt = recoverPreparePrintf(p, p->dbOut, "PRAGMA table_xinfo(%Q)", zName ); @@ -727,6 +892,15 @@ static void recoverAddTable(sqlite3_recover *p, const char *zName, i64 iRoot){ } } +/* +** If this recover handle is not in SQL callback mode (i.e. was not created +** using sqlite3_recover_init_sql()) of if an error has already occurred, +** this function is a no-op. Otherwise, issue a callback with SQL statement +** zSql as the parameter. +** +** If the callback returns non-zero, set the recover handle error code to +** the value returned (so that the caller will abandon processing). +*/ static void recoverSqlCallback(sqlite3_recover *p, const char *zSql){ if( p->errCode==SQLITE_OK && p->xSql ){ int res = p->xSql(p->pSqlCtx, zSql); @@ -737,7 +911,23 @@ static void recoverSqlCallback(sqlite3_recover *p, const char *zSql){ } /* +** This function is called after recoverCacheSchema() has cached those parts +** of the input database schema that could be recovered in temporary table +** "recovery.schema". This function creates in the output database copies +** of all parts of that schema that must be created before the tables can +** be populated. Specifically, this means: +** +** * all tables that are not VIRTUAL, and +** * UNIQUE indexes. ** +** If the recovery handle uses SQL callbacks, then callbacks containing +** the associated "CREATE TABLE" and "CREATE INDEX" statements are made. +** +** Additionally, records are added to the sqlite_schema table of the +** output database for any VIRTUAL tables. The CREATE VIRTUAL TABLE +** records are written directly to sqlite_schema, not actually executed. +** If the handle is in SQL callback mode, then callbacks are invoked +** with equivalent SQL statements. */ static int recoverWriteSchema1(sqlite3_recover *p){ sqlite3_stmt *pSelect = 0; @@ -800,6 +990,19 @@ static int recoverWriteSchema1(sqlite3_recover *p){ return p->errCode; } +/* +** This function is called after the output database has been populated. It +** adds all recovered schema elements that were not created in the output +** database by recoverWriteSchema1() - everything except for tables and +** UNIQUE indexes. Specifically: +** +** * views, +** * triggers, +** * non-UNIQUE indexes. +** +** If the recover handle is in SQL callback mode, then equivalent callbacks +** are issued to create the schema elements. +*/ static int recoverWriteSchema2(sqlite3_recover *p){ sqlite3_stmt *pSelect = 0; @@ -825,6 +1028,41 @@ static int recoverWriteSchema2(sqlite3_recover *p){ return p->errCode; } +/* +** This function is a no-op if recover handle p already contains an error +** (if p->errCode!=SQLITE_OK). In this case it returns NULL. +** +** Otherwise, if the recover handle is configured to create an output +** database (was created by sqlite3_recover_init()), then this function +** prepares and returns an SQL statement to INSERT a new record into table +** pTab, assuming the first nField fields of a record extracted from disk +** are valid. +** +** For example, if table pTab is: +** +** CREATE TABLE name(a, b GENERATED ALWAYS AS (a+1) STORED, c, d, e); +** +** And nField is 4, then the SQL statement prepared and returned is: +** +** INSERT INTO (a, c, d) VALUES (?1, ?2, ?3); +** +** In this case even though 4 values were extracted from the input db, +** only 3 are written to the output, as the generated STORED column +** cannot be written. +** +** If the recover handle is in SQL callback mode, then the SQL statement +** prepared is such that evaluating it returns a single row containing +** a single text value - itself an SQL statement similar to the above, +** except with SQL literals in place of the variables. For example: +** +** SELECT 'INSERT INTO (a, c, d) VALUES (' +** || quote(?1) || ', ' +** || quote(?2) || ', ' +** || quote(?3) || ')'; +** +** In either case, it is the responsibility of the caller to eventually +** free the statement handle using sqlite3_finalize(). +*/ static sqlite3_stmt *recoverInsertStmt( sqlite3_recover *p, RecoverTable *pTab, @@ -855,7 +1093,6 @@ static sqlite3_stmt *recoverInsertStmt( zSep = ", "; } - for(ii=0; iiaCol[ii].eHidden; if( eHidden!=RECOVER_EHIDDEN_VIRTUAL @@ -893,6 +1130,11 @@ static sqlite3_stmt *recoverInsertStmt( } +/* +** Search the list of RecoverTable objects at p->pTblList for one that +** has root page iRoot in the input database. If such an object is found, +** return a pointer to it. Otherwise, return NULL. +*/ static RecoverTable *recoverFindTable(sqlite3_recover *p, u32 iRoot){ RecoverTable *pRet = 0; for(pRet=p->pTblList; pRet && pRet->iRoot!=iRoot; pRet=pRet->pNext); @@ -1007,6 +1249,9 @@ static sqlite3_stmt *recoverLostAndFoundInsert( return pRet; } +/* +** Helper function for recoverLostAndFound(). +*/ static void recoverLostAndFoundPopulate( sqlite3_recover *p, sqlite3_stmt *pInsert, @@ -1096,6 +1341,12 @@ static void recoverLostAndFoundPopulate( sqlite3_free(apVal); } +/* +** This function searches for orphaned rows in the input database. If +** any are found, it creates the lost-and-found table in the output +** db and writes all orphaned rows to it. Or, if the recover handle is +** in SQL callback mode, issues equivalent callbacks. +*/ static int recoverLostAndFound(sqlite3_recover *p){ i64 nPg = 0; RecoverBitmap *pMap = 0; @@ -1198,12 +1449,18 @@ static int recoverLostAndFound(sqlite3_recover *p){ recoverFinalize(p, pInsert); sqlite3_free(zTab); } - - recoverBitmapFree(pMap); - p->pUsed = 0; } } +/* +** For each table in the recovered schema, this function extracts as much +** data as possible from the output database and writes it to the input +** database. Or, if the recover handle is in SQL callback mode, issues +** equivalent callbacks. +** +** It does not recover "orphaned" data into the lost-and-found table. +** See recoverLostAndFound() for that. +*/ static int recoverWriteData(sqlite3_recover *p){ RecoverTable *pTbl; int nMax = 0; @@ -1349,12 +1606,79 @@ static int recoverWriteData(sqlite3_recover *p){ return p->errCode; } +/* +** This function does the work of sqlite3_recover_run(). It is assumed that +** no error has occurred when this is called. If an error occurs during +** the recovery operation, an error code and error message are left in +** the recovery handle. +*/ +static void recoverRun(sqlite3_recover *p){ + RecoverTable *pTab = 0; + RecoverTable *pNext = 0; + int rc = SQLITE_OK; + + assert( p->errCode==SQLITE_OK ); + assert( p->bRun==0 ); + p->bRun = 1; + + recoverSqlCallback(p, "BEGIN"); + recoverSqlCallback(p, "PRAGMA writable_schema = on"); + + /* Open the output database. And register required virtual tables and + ** user functions with the new handle. */ + recoverOpenOutput(p); + + /* Open transactions on both the input and output databases. */ + recoverExec(p, p->dbIn, "BEGIN"); + recoverExec(p, p->dbOut, "BEGIN"); + + recoverCacheSchema(p); + recoverWriteSchema1(p); + recoverWriteData(p); + if( p->zLostAndFound ) recoverLostAndFound(p); + recoverWriteSchema2(p); + + /* If no error has occurred, commit the write transaction on the output + ** database. Then end the read transaction on the input database, regardless + ** of whether or not prior errors have occurred. */ + recoverExec(p, p->dbOut, "COMMIT"); + rc = sqlite3_exec(p->dbIn, "END", 0, 0, 0); + if( p->errCode==SQLITE_OK ) p->errCode = rc; + + recoverSqlCallback(p, "PRAGMA writable_schema = off"); + recoverSqlCallback(p, "COMMIT"); + + /* Clean up various resources allocated by this function. */ + for(pTab=p->pTblList; pTab; pTab=pNext){ + pNext = pTab->pNext; + sqlite3_free(pTab); + } + p->pTblList = 0; + sqlite3_finalize(p->pGetPage); + p->pGetPage = 0; + recoverBitmapFree(p->pUsed); + p->pUsed = 0; + sqlite3_close(p->dbOut); +} + + +/* +** This is a worker function that does the heavy lifting for both init +** functions: +** +** sqlite3_recover_init() +** sqlite3_recover_init_sql() +** +** All this function does is allocate space for the recover handle and +** take copies of the input parameters. All the real work is done within +** sqlite3_recover_run(). +*/ sqlite3_recover *recoverInit( sqlite3* db, const char *zDb, - const char *zUri, - int (*xSql)(void*, const char*), - void *pSqlCtx + const char *zUri, /* Output URI for _recover_init() */ + int (*xSql)(void*, const char*),/* SQL callback for _recover_init_sql() */ + void *pSqlCtx /* Context arg for _recover_init_sql() */ ){ sqlite3_recover *pRet = 0; int nDb = 0; @@ -1384,6 +1708,10 @@ sqlite3_recover *recoverInit( return pRet; } +/* +** Initialize a recovery handle that creates a new database containing +** the recovered data. +*/ sqlite3_recover *sqlite3_recover_init( sqlite3* db, const char *zDb, @@ -1392,6 +1720,10 @@ sqlite3_recover *sqlite3_recover_init( return recoverInit(db, zDb, zUri, 0, 0); } +/* +** Initialize a recovery handle that returns recovered data in the +** form of SQL statements via a callback. +*/ sqlite3_recover *sqlite3_recover_init_sql( sqlite3* db, const char *zDb, @@ -1401,13 +1733,23 @@ sqlite3_recover *sqlite3_recover_init_sql( return recoverInit(db, zDb, "", xSql, pSqlCtx); } +/* +** Return the handle error message, if any. +*/ const char *sqlite3_recover_errmsg(sqlite3_recover *p){ return p ? p->zErrMsg : "not an error"; } + +/* +** Return the handle error code. +*/ int sqlite3_recover_errcode(sqlite3_recover *p){ return p ? p->errCode : SQLITE_NOMEM; } +/* +** Configure the handle. +*/ int sqlite3_recover_config(sqlite3_recover *p, int op, void *pArg){ int rc = SQLITE_OK; @@ -1443,57 +1785,27 @@ int sqlite3_recover_config(sqlite3_recover *p, int op, void *pArg){ return rc; } -static void recoverStep(sqlite3_recover *p){ - RecoverTable *pTab = 0; - RecoverTable *pNext = 0; - int rc = SQLITE_OK; - assert( p->errCode==SQLITE_OK ); - - recoverSqlCallback(p, "BEGIN"); - recoverSqlCallback(p, "PRAGMA writable_schema = on"); - - /* Open the output database. And register required virtual tables and - ** user functions with the new handle. */ - recoverOpenOutput(p); - - /* Open transactions on both the input and output databases. */ - recoverExec(p, p->dbIn, "BEGIN"); - recoverExec(p, p->dbOut, "BEGIN"); - - recoverCacheSchema(p); - recoverWriteSchema1(p); - recoverWriteData(p); - if( p->zLostAndFound ) recoverLostAndFound(p); - recoverWriteSchema2(p); - - /* If no error has occurred, commit the write transaction on the output - ** database. Then end the read transaction on the input database, regardless - ** of whether or not prior errors have occurred. */ - recoverExec(p, p->dbOut, "COMMIT"); - rc = sqlite3_exec(p->dbIn, "END", 0, 0, 0); - if( p->errCode==SQLITE_OK ) p->errCode = rc; - - recoverSqlCallback(p, "PRAGMA writable_schema = off"); - recoverSqlCallback(p, "COMMIT"); - - for(pTab=p->pTblList; pTab; pTab=pNext){ - pNext = pTab->pNext; - sqlite3_free(pTab); - } - p->pTblList = 0; - - sqlite3_finalize(p->pGetPage); - sqlite3_close(p->dbOut); - p->pGetPage = 0; -} - +/* +** Do the configured recovery operation. Return SQLITE_OK if successful, or +** else an SQLite error code. +*/ int sqlite3_recover_run(sqlite3_recover *p){ - if( p && p->errCode==SQLITE_OK ){ - recoverStep(p); + if( p ){ + if( p->bRun ) return SQLITE_MISUSE; /* Has already run */ + if( p->errCode==SQLITE_OK ) recoverRun(p); } return p ? p->errCode : SQLITE_NOMEM; } +/* +** Free all resources associated with the recover handle passed as the only +** argument. The results of using a handle with any sqlite3_recover_** +** API function after it has been passed to this function are undefined. +** +** A copy of the value returned by the first call made to sqlite3_recover_run() +** on this handle is returned, or SQLITE_OK if sqlite3_recover_run() has +** not been called on this handle. +*/ int sqlite3_recover_finish(sqlite3_recover *p){ int rc = p->errCode; sqlite3_free(p->zErrMsg); diff --git a/manifest b/manifest index 3f230d9467..d505905e12 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Further\sfixes\sto\scomments\sin\ssqlite3recover.h.\sAlso\srework\ssome\sdata\sstructures\sin\ssqlite3recover.c. -D 2022-09-09T20:44:56.135 +C Rework\srecover\sextension\scode\sfor\sreadability. +D 2022-09-10T20:01:49.308 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -392,7 +392,7 @@ 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 e8d0eae7da7ba24e733f6247082c12d7a030ce794eb5c42d64993f55c12882f5 +F ext/recover/sqlite3recover.c 4ed53fd33639ede83505f4397b8e1e46b6c0c5a5188c42f28f8372487141170a F ext/recover/sqlite3recover.h 81108efb8c4618d3d9c6da4df785212b0e4501aa0d25edfc463405fe839a6640 F ext/recover/test_recover.c ed8d0cc8703ab29cf562f793623b045de109b7937f254108ff4132f35abb37fb F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15 @@ -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 5f2d5ccd56c06c3468377126acfd4be39b79b05bb6fb09b674b2e185df143aa3 -R d4558eaeefd2a718275b323dc4307f9b +P 599d1f8ec2f9e24924a6f9e66c85664360c7b95531b07a4efe1dd8c096b3fc99 +R cd56b05532a91c08b9e673fffba829f5 U dan -Z 6e64e7331e56e0c44a533586bb618920 +Z 624cfda4b7c658530a41a87671068e55 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a1b607602a..98f37f0563 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -599d1f8ec2f9e24924a6f9e66c85664360c7b95531b07a4efe1dd8c096b3fc99 \ No newline at end of file +1a2540960e40e3c8c622448fd3862e249bd463c29ae4ce5e39942e942533f60a \ No newline at end of file