]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Rework recover extension code for readability.
authordan <Dan Kennedy>
Sat, 10 Sep 2022 20:01:49 +0000 (20:01 +0000)
committerdan <Dan Kennedy>
Sat, 10 Sep 2022 20:01:49 +0000 (20:01 +0000)
FossilOrigin-Name: 1a2540960e40e3c8c622448fd3862e249bd463c29ae4ce5e39942e942533f60a

ext/recover/sqlite3recover.c
manifest
manifest.uuid

index e6975b8771f4962ac6d2ba12ae5d8f71b1d20faf..9c5e10433b4986b1f6e3ba186e9a6aa3b69e7489 100644 (file)
 #include <assert.h>
 #include <string.h>
 
+/*
+** 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*<arg2>) of the blob.
+**
+**     SELECT read_i32(<blob>, <idx>)
 */
 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(<pgno>);
 */
 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(<input>, '\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; ii<nField; ii++){
     int eHidden = pTab->aCol[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);
index 3f230d94672f9b7f78ce4bdf554de3914b1a4353..d505905e12b86e44e73abffa474baad652995a0a 100644 (file)
--- 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.
index a1b607602a0a095dd2dbbfd7585e71d6f467ff5d..98f37f05634e7a58d9384123df3647aa47071354 100644 (file)
@@ -1 +1 @@
-599d1f8ec2f9e24924a6f9e66c85664360c7b95531b07a4efe1dd8c096b3fc99
\ No newline at end of file
+1a2540960e40e3c8c622448fd3862e249bd463c29ae4ce5e39942e942533f60a
\ No newline at end of file