/* ** 2022-08-27 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** */ #include "sqlite3recover.h" #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 unsigned char u8; 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 */ int bIPK; /* True for IPK column */ char *zCol; int eHidden; }; #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 */ /* ** Bitmap object used to track pages in the input database. Allocated ** and manipulated only by the following functions: ** ** 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 RecoverBitmap RecoverBitmap; struct RecoverBitmap { i64 nPg; /* Size of bitmap */ u32 aElem[1]; /* Array of 32-bit bitmasks */ }; /* ** State variables (part of the sqlite3_recover structure) used while ** recovering data for tables identified in the recovered schema (state ** RECOVER_STATE_WRITING). */ typedef struct RecoverStateW1 RecoverStateW1; struct RecoverStateW1 { sqlite3_stmt *pTbls; sqlite3_stmt *pSel; sqlite3_stmt *pInsert; int nInsert; RecoverTable *pTab; /* Table currently being written */ int nMax; /* Max column count in any schema table */ sqlite3_value **apVal; /* Array of nMax values */ int nVal; /* Number of valid entries in apVal[] */ int bHaveRowid; i64 iRowid; i64 iPrevPage; int iPrevCell; }; /* ** State variables (part of the sqlite3_recover structure) used while ** recovering data destined for the lost and found table (states ** RECOVER_STATE_LOSTANDFOUND[123]). */ typedef struct RecoverStateLAF RecoverStateLAF; struct RecoverStateLAF { RecoverBitmap *pUsed; i64 nPg; /* Size of db in pages */ sqlite3_stmt *pAllAndParent; sqlite3_stmt *pMapInsert; sqlite3_stmt *pMaxField; sqlite3_stmt *pUsedPages; sqlite3_stmt *pFindRoot; sqlite3_stmt *pInsert; /* INSERT INTO lost_and_found ... */ sqlite3_stmt *pAllPage; sqlite3_stmt *pPageData; sqlite3_value **apVal; int nMaxField; }; /* ** Main recover handle structure. */ struct sqlite3_recover { /* Copies of sqlite3_recover_init[_sql]() parameters */ sqlite3 *dbIn; /* Input database */ char *zDb; /* Name of input db ("main" etc.) */ char *zUri; /* URI for output database */ 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 */ int bSlowIndexes; /* SQLITE_RECOVER_SLOWINDEXES setting */ int pgsz; int detected_pgsz; int nReserve; u8 *pPage1Disk; u8 *pPage1Cache; /* Error code and error message */ int errCode; /* For sqlite3_recover_errcode() */ char *zErrMsg; /* For sqlite3_recover_errmsg() */ int eState; int bCloseTransaction; /* Variables used with eState==RECOVER_STATE_WRITING */ RecoverStateW1 w1; /* Variables used with states RECOVER_STATE_LOSTANDFOUND[123] */ RecoverStateLAF laf; /* Fields used within sqlite3_recover_run() */ sqlite3 *dbOut; /* Output database */ sqlite3_stmt *pGetPage; /* SELECT against input db sqlite_dbdata */ RecoverTable *pTblList; /* List of tables recovered from schema */ }; /* ** The various states in which an sqlite3_recover object may exist: ** ** RECOVER_STATE_INIT: ** The object is initially created in this state. sqlite3_recover_step() ** has yet to be called. This is the only state in which it is permitted ** to call sqlite3_recover_config(). ** ** RECOVER_STATE_WRITING: ** ** RECOVER_STATE_LOSTANDFOUND1: ** State to populate the bitmap of pages used by other tables or the ** database freelist. ** ** RECOVER_STATE_LOSTANDFOUND2: ** Populate the recovery.map table - used to figure out a "root" page ** for each lost page from in the database from which records are ** extracted. ** ** RECOVER_STATE_LOSTANDFOUND3: ** Populate the lost-and-found table itself. */ #define RECOVER_STATE_INIT 0 #define RECOVER_STATE_WRITING 1 #define RECOVER_STATE_LOSTANDFOUND1 2 #define RECOVER_STATE_LOSTANDFOUND2 3 #define RECOVER_STATE_LOSTANDFOUND3 4 #define RECOVER_STATE_SCHEMA2 5 #define RECOVER_STATE_DONE 6 /* ** Global variables used by this extension. */ typedef struct RecoverGlobal RecoverGlobal; struct RecoverGlobal { const sqlite3_io_methods *pMethods; sqlite3_recover *p; }; static RecoverGlobal recover_g; /* ** Use this static SQLite mutex to protect the globals during the ** first call to sqlite3_recover_step(). */ #define RECOVER_MUTEX_ID SQLITE_MUTEX_STATIC_APP2 /* ** Default value for SQLITE_RECOVER_ROWIDS (sqlite3_recover.bRecoverRowid). */ #define RECOVER_ROWID_DEFAULT 1 #if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0 # define recoverEnterMutex() # define recoverLeaveMutex() # define recoverAssertMutexHeld() #else static void recoverEnterMutex(void){ sqlite3_mutex_enter(sqlite3_mutex_alloc(RECOVER_MUTEX_ID)); } static void recoverLeaveMutex(void){ sqlite3_mutex_leave(sqlite3_mutex_alloc(RECOVER_MUTEX_ID)); } static void recoverAssertMutexHeld(void){ assert( sqlite3_mutex_held(sqlite3_mutex_alloc(RECOVER_MUTEX_ID)) ); } #endif /* ** Like strlen(). But handles NULL pointer arguments. */ static int recoverStrlen(const char *zStr){ int nRet = 0; if( zStr ){ while( zStr[nRet] ) nRet++; } 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 ); if( p->errCode==SQLITE_OK ){ pRet = sqlite3_malloc64(nByte); if( pRet ){ memset(pRet, 0, nByte); }else{ p->errCode = SQLITE_NOMEM; } } 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, const char *zFmt, ... ){ char *z = 0; va_list ap; va_start(ap, zFmt); if( zFmt ){ z = sqlite3_vmprintf(zFmt, ap); va_end(ap); } sqlite3_free(p->zErrMsg); p->zErrMsg = z; p->errCode = errCode; return errCode; } /* ** 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); RecoverBitmap *pRet = (RecoverBitmap*)recoverMalloc(p, nByte); if( pRet ){ pRet->nPg = 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); int iBit = (iPg % 32); pMap->aElem[iElem] |= (((u32)1) << iBit); } } /* ** 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 && iPg>0 ){ int iElem = (iPg / 32); int iBit = (iPg % 32); ret = (pMap->aElem[iElem] & (((u32)1) << iBit)) ? 1 : 0; } 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, const char *zSql ){ sqlite3_stmt *pStmt = 0; if( p->errCode==SQLITE_OK ){ if( sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0) ){ recoverDbError(p, db); } } return pStmt; } /* ** 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, sqlite3 *db, const char *zFmt, ... ){ sqlite3_stmt *pStmt = 0; if( p->errCode==SQLITE_OK ){ va_list ap; char *z; va_start(ap, zFmt); z = sqlite3_vmprintf(zFmt, ap); va_end(ap); if( z==0 ){ p->errCode = SQLITE_NOMEM; }else{ pStmt = recoverPrepare(p, db, z); sqlite3_free(z); } } 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 ){ recoverDbError(p, sqlite3_db_handle(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); if( rc!=SQLITE_OK && p->errCode==SQLITE_OK ){ recoverDbError(p, db); } } /* ** 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); if( rc ){ recoverDbError(p, db); } } return p->errCode; } /* ** Bind the value pVal to parameter iBind of statement pStmt. Leave an ** error in the recover handle passed as the first argument if an error ** (e.g. an OOM) occurs. */ static void recoverBindValue( sqlite3_recover *p, sqlite3_stmt *pStmt, int iBind, sqlite3_value *pVal ){ if( p->errCode==SQLITE_OK ){ int rc = sqlite3_bind_value(pStmt, iBind, pVal); if( rc ) recoverError(p, rc, 0); } } /* ** 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; va_start(ap, zFmt); z = sqlite3_vmprintf(zFmt, ap); va_end(ap); if( p->errCode==SQLITE_OK ){ if( z==0 ) p->errCode = SQLITE_NOMEM; }else{ sqlite3_free(z); z = 0; } return z; } /* ** 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; if( p->errCode==SQLITE_OK ){ sqlite3_stmt *pStmt = 0; pStmt = recoverPreparePrintf(p, p->dbIn, "PRAGMA %Q.page_count", p->zDb); if( pStmt ){ sqlite3_step(pStmt); nPg = sqlite3_column_int64(pStmt, 0); } recoverFinalize(p, pStmt); } return nPg; } /* ** 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, int argc, sqlite3_value **argv ){ const unsigned char *pBlob; int nBlob; int iInt; assert( argc==2 ); nBlob = sqlite3_value_bytes(argv[0]); pBlob = (const unsigned char*)sqlite3_value_blob(argv[0]); iInt = sqlite3_value_int(argv[1]) & 0xFFFF; if( (iInt+1)*4<=nBlob ){ const unsigned char *a = &pBlob[iInt*4]; i64 iVal = ((i64)a[0]<<24) + ((i64)a[1]<<16) + ((i64)a[2]<< 8) + ((i64)a[3]<< 0); sqlite3_result_int64(context, iVal); } } /* ** 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.laf.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, int nArg, sqlite3_value **apArg ){ sqlite3_recover *p = (sqlite3_recover*)sqlite3_user_data(pCtx); i64 pgno = sqlite3_value_int64(apArg[0]); assert( nArg==1 ); sqlite3_result_int(pCtx, recoverBitmapQuery(p->laf.pUsed, pgno)); } /* ** The implementation of a user-defined SQL function invoked by the ** 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 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 ** the identified page of the db being recovered. e.g. ** ** SELECT getpage(0); -- return number of pages in db ** SELECT getpage(4); -- return page 4 of db as a blob of data */ static void recoverGetPage( sqlite3_context *pCtx, int nArg, sqlite3_value **apArg ){ sqlite3_recover *p = (sqlite3_recover*)sqlite3_user_data(pCtx); i64 pgno = sqlite3_value_int64(apArg[0]); sqlite3_stmt *pStmt = 0; assert( nArg==1 ); if( pgno==0 ){ i64 nPg = recoverPageCount(p); sqlite3_result_int64(pCtx, nPg); return; }else{ if( p->pGetPage==0 ){ pStmt = p->pGetPage = recoverPreparePrintf( p, p->dbIn, "SELECT data FROM sqlite_dbpage(%Q) WHERE pgno=?", p->zDb ); }else if( p->errCode==SQLITE_OK ){ pStmt = p->pGetPage; } if( pStmt ){ sqlite3_bind_int64(pStmt, 1, pgno); if( SQLITE_ROW==sqlite3_step(pStmt) ){ assert( p->errCode==SQLITE_OK ); const u8 *aPg = sqlite3_column_blob(pStmt, 0); int nPg = sqlite3_column_bytes(pStmt, 0); if( pgno==1 && nPg==p->pgsz && 0==memcmp(p->pPage1Cache, aPg, nPg) ){ aPg = p->pPage1Disk; } sqlite3_result_blob(pCtx, aPg, nPg-p->nReserve, SQLITE_TRANSIENT); } recoverReset(p, pStmt); } } if( p->errCode ){ if( p->zErrMsg ) sqlite3_result_error(pCtx, p->zErrMsg, -1); sqlite3_result_error_code(pCtx, p->errCode); } } /* ** Find a string that is not found anywhere in z[]. Return a pointer ** to that string. ** ** Try to use zA and zB first. If both of those are already found in z[] ** then make up some string and store it in the buffer zBuf. */ static const char *recoverUnusedString( const char *z, /* Result must not appear anywhere in z */ const char *zA, const char *zB, /* Try these first */ char *zBuf /* Space to store a generated string */ ){ unsigned i = 0; if( strstr(z, zA)==0 ) return zA; if( strstr(z, zB)==0 ) return zB; do{ sqlite3_snprintf(20,zBuf,"(%s%u)", zA, i++); }while( strstr(z,zBuf)!=0 ); return zBuf; } /* ** 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. */ static void recoverEscapeCrnl( sqlite3_context *context, int argc, sqlite3_value **argv ){ const char *zText = (const char*)sqlite3_value_text(argv[0]); if( zText && zText[0]=='\'' ){ int nText = sqlite3_value_bytes(argv[0]); int i; char zBuf1[20]; char zBuf2[20]; const char *zNL = 0; const char *zCR = 0; int nCR = 0; int nNL = 0; for(i=0; zText[i]; i++){ if( zNL==0 && zText[i]=='\n' ){ zNL = recoverUnusedString(zText, "\\n", "\\012", zBuf1); nNL = (int)strlen(zNL); } if( zCR==0 && zText[i]=='\r' ){ zCR = recoverUnusedString(zText, "\\r", "\\015", zBuf2); nCR = (int)strlen(zCR); } } if( zNL || zCR ){ int iOut = 0; i64 nMax = (nNL > nCR) ? nNL : nCR; i64 nAlloc = nMax * nText + (nMax+64)*2; char *zOut = (char*)sqlite3_malloc64(nAlloc); if( zOut==0 ){ sqlite3_result_error_nomem(context); return; } if( zNL && zCR ){ memcpy(&zOut[iOut], "replace(replace(", 16); iOut += 16; }else{ memcpy(&zOut[iOut], "replace(", 8); iOut += 8; } for(i=0; zText[i]; i++){ if( zText[i]=='\n' ){ memcpy(&zOut[iOut], zNL, nNL); iOut += nNL; }else if( zText[i]=='\r' ){ memcpy(&zOut[iOut], zCR, nCR); iOut += nCR; }else{ zOut[iOut] = zText[i]; iOut++; } } if( zNL ){ memcpy(&zOut[iOut], ",'", 2); iOut += 2; memcpy(&zOut[iOut], zNL, nNL); iOut += nNL; memcpy(&zOut[iOut], "', char(10))", 12); iOut += 12; } if( zCR ){ memcpy(&zOut[iOut], ",'", 2); iOut += 2; memcpy(&zOut[iOut], zCR, nCR); iOut += nCR; memcpy(&zOut[iOut], "', char(13))", 12); iOut += 12; } sqlite3_result_text(context, zOut, iOut, SQLITE_TRANSIENT); sqlite3_free(zOut); return; } } sqlite3_result_value(context, argv[0]); } /* ** 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 (" " SELECT 1" " UNION" " SELECT child FROM sqlite_dbptr('getpage()'), pages WHERE pgno=p" ")" "INSERT INTO recovery.schema SELECT" " max(CASE WHEN field=0 THEN value ELSE NULL END)," " max(CASE WHEN field=1 THEN value ELSE NULL END)," " max(CASE WHEN field=2 THEN value ELSE NULL END)," " max(CASE WHEN field=3 THEN value ELSE NULL END)," " max(CASE WHEN field=4 THEN value ELSE NULL END)" "FROM sqlite_dbdata('getpage()') WHERE pgno IN (" " SELECT p FROM pages" ") GROUP BY pgno, cell" ); } /* ** 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); if( res ){ recoverError(p, SQLITE_ERROR, "callback returned an error - %d", res); } } } /* ** Transfer the following settings from the input database to the output ** database: ** ** + page-size, ** + auto-vacuum settings, ** + database encoding, ** + user-version (PRAGMA user_version), and ** + application-id (PRAGMA application_id), and */ static void recoverTransferSettings(sqlite3_recover *p){ const char *aPragma[] = { "encoding", "page_size", "auto_vacuum", "user_version", "application_id" }; int ii; /* Truncate the output database to 0 pages in size. This is done by ** opening a new, empty, temp db, then using the backup API to clobber ** any existing output db with a copy of it. */ if( p->errCode==SQLITE_OK ){ sqlite3 *db2 = 0; int rc = sqlite3_open("", &db2); if( rc!=SQLITE_OK ){ recoverDbError(p, db2); return; } for(ii=0; iidbIn, "PRAGMA %Q.%s", p->zDb, zPrag); if( p->errCode==SQLITE_OK && sqlite3_step(p1)==SQLITE_ROW ){ const char *zArg = (const char*)sqlite3_column_text(p1, 0); char *z2 = recoverMPrintf(p, "PRAGMA %s = %Q", zPrag, zArg); recoverSqlCallback(p, z2); recoverExec(p, db2, z2); sqlite3_free(z2); if( zArg==0 ){ recoverError(p, SQLITE_NOMEM, 0); } } recoverFinalize(p, p1); } recoverExec(p, db2, "CREATE TABLE t1(a); DROP TABLE t1;"); if( p->errCode==SQLITE_OK ){ sqlite3 *db = p->dbOut; sqlite3_backup *pBackup = sqlite3_backup_init(db, "main", db2, "main"); if( pBackup ){ sqlite3_backup_step(pBackup, -1); p->errCode = sqlite3_backup_finish(pBackup); }else{ recoverDbError(p, db); } } sqlite3_close(db2); } } /* ** 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; int nArg; void (*xFunc)(sqlite3_context*,int,sqlite3_value **); } aFunc[] = { { "getpage", 1, recoverGetPage }, { "page_is_used", 1, recoverPageIsUsed }, { "read_i32", 2, recoverReadI32 }, { "escape_crnl", 1, recoverEscapeCrnl }, }; const int flags = SQLITE_OPEN_URI|SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE; sqlite3 *db = 0; /* New database handle */ int ii; /* For iterating through aFunc[] */ assert( p->dbOut==0 ); if( sqlite3_open_v2(p->zUri, &db, flags, 0) ){ recoverDbError(p, db); } /* Register the sqlite_dbdata and sqlite_dbptr virtual table modules. ** These two are registered with the output database handle - this ** module depends on the input handle supporting the sqlite_dbpage ** virtual table only. */ if( p->errCode==SQLITE_OK ){ p->errCode = sqlite3_dbdata_init(db, 0, 0); } /* Register the custom user-functions with the output handle. */ for(ii=0; p->errCode==SQLITE_OK && iierrCode = sqlite3_create_function(db, aFunc[ii].zName, aFunc[ii].nArg, SQLITE_UTF8, (void*)p, aFunc[ii].xFunc, 0, 0 ); } p->dbOut = db; return p->errCode; } /* ** Attach the auxiliary database 'recovery' to the output database handle. ** This temporary database is used during the recovery process and then ** discarded. */ static void recoverOpenRecovery(sqlite3_recover *p){ char *zSql = recoverMPrintf(p, "ATTACH %Q AS recovery;", p->zStateDb); recoverExec(p, p->dbOut, zSql); recoverExec(p, p->dbOut, "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); } /* ** 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 ); if( pStmt ){ int iPk = -1; int iBind = 1; RecoverTable *pNew = 0; int nCol = 0; int nName = recoverStrlen(zName); int nByte = 0; while( sqlite3_step(pStmt)==SQLITE_ROW ){ nCol++; nByte += (sqlite3_column_bytes(pStmt, 1)+1); } nByte += sizeof(RecoverTable) + nCol*sizeof(RecoverColumn) + nName+1; recoverReset(p, pStmt); pNew = recoverMalloc(p, nByte); if( pNew ){ int i = 0; int iField = 0; char *csr = 0; pNew->aCol = (RecoverColumn*)&pNew[1]; pNew->zTab = csr = (char*)&pNew->aCol[nCol]; pNew->nCol = nCol; pNew->iRoot = iRoot; memcpy(csr, zName, nName); csr += nName+1; for(i=0; sqlite3_step(pStmt)==SQLITE_ROW; i++){ int iPKF = sqlite3_column_int(pStmt, 5); int n = sqlite3_column_bytes(pStmt, 1); const char *z = (const char*)sqlite3_column_text(pStmt, 1); const char *zType = (const char*)sqlite3_column_text(pStmt, 2); int eHidden = sqlite3_column_int(pStmt, 6); if( iPk==-1 && iPKF==1 && !sqlite3_stricmp("integer", zType) ) iPk = i; if( iPKF>1 ) iPk = -2; pNew->aCol[i].zCol = csr; pNew->aCol[i].eHidden = eHidden; if( eHidden==RECOVER_EHIDDEN_VIRTUAL ){ pNew->aCol[i].iField = -1; }else{ pNew->aCol[i].iField = iField++; } if( eHidden!=RECOVER_EHIDDEN_VIRTUAL && eHidden!=RECOVER_EHIDDEN_STORED ){ pNew->aCol[i].iBind = iBind++; } memcpy(csr, z, n); csr += (n+1); } pNew->pNext = p->pTblList; p->pTblList = pNew; pNew->bIntkey = 1; } recoverFinalize(p, pStmt); pStmt = recoverPreparePrintf(p, p->dbOut, "PRAGMA index_xinfo(%Q)", zName); while( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){ int iField = sqlite3_column_int(pStmt, 0); int iCol = sqlite3_column_int(pStmt, 1); assert( iFieldnCol && iColnCol ); pNew->aCol[iCol].iField = iField; pNew->bIntkey = 0; iPk = -2; } recoverFinalize(p, pStmt); if( p->errCode==SQLITE_OK ){ if( iPk>=0 ){ pNew->aCol[iPk].bIPK = 1; }else if( pNew->bIntkey ){ pNew->iRowidBind = iBind++; } } } } /* ** 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; sqlite3_stmt *pTblname = 0; pSelect = recoverPrepare(p, p->dbOut, "WITH dbschema(rootpage, name, sql, tbl, isVirtual, isIndex) AS (" " SELECT rootpage, name, sql, " " type='table', " " sql LIKE 'create virtual%'," " (type='index' AND (sql LIKE '%unique%' OR ?1))" " FROM recovery.schema" ")" "SELECT rootpage, tbl, isVirtual, name, sql" " FROM dbschema " " WHERE tbl OR isIndex" " ORDER BY tbl DESC, name=='sqlite_sequence' DESC" ); pTblname = recoverPrepare(p, p->dbOut, "SELECT name FROM sqlite_schema " "WHERE type='table' ORDER BY rowid DESC LIMIT 1" ); if( pSelect ){ sqlite3_bind_int(pSelect, 1, p->bSlowIndexes); while( sqlite3_step(pSelect)==SQLITE_ROW ){ i64 iRoot = sqlite3_column_int64(pSelect, 0); int bTable = sqlite3_column_int(pSelect, 1); int bVirtual = sqlite3_column_int(pSelect, 2); const char *zName = (const char*)sqlite3_column_text(pSelect, 3); const char *zSql = (const char*)sqlite3_column_text(pSelect, 4); char *zFree = 0; int rc = SQLITE_OK; if( bVirtual ){ zSql = (const char*)(zFree = recoverMPrintf(p, "INSERT INTO sqlite_schema VALUES('table', %Q, %Q, 0, %Q)", zName, zName, zSql )); } rc = sqlite3_exec(p->dbOut, zSql, 0, 0, 0); if( rc==SQLITE_OK ){ recoverSqlCallback(p, zSql); if( bTable && !bVirtual ){ if( SQLITE_ROW==sqlite3_step(pTblname) ){ const char *zTbl = (const char*)sqlite3_column_text(pTblname, 0); recoverAddTable(p, zTbl, iRoot); } recoverReset(p, pTblname); } }else if( rc!=SQLITE_ERROR ){ recoverDbError(p, p->dbOut); } sqlite3_free(zFree); } } recoverFinalize(p, pSelect); recoverFinalize(p, pTblname); 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; pSelect = recoverPrepare(p, p->dbOut, p->bSlowIndexes ? "SELECT rootpage, sql FROM recovery.schema " " WHERE type!='table' AND type!='index'" : "SELECT rootpage, sql FROM recovery.schema " " WHERE type!='table' AND (type!='index' OR sql NOT LIKE '%unique%')" ); if( pSelect ){ while( sqlite3_step(pSelect)==SQLITE_ROW ){ const char *zSql = (const char*)sqlite3_column_text(pSelect, 1); int rc = sqlite3_exec(p->dbOut, zSql, 0, 0, 0); if( rc==SQLITE_OK ){ recoverSqlCallback(p, zSql); }else if( rc!=SQLITE_ERROR ){ recoverDbError(p, p->dbOut); } } } recoverFinalize(p, pSelect); 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, int nField ){ sqlite3_stmt *pRet = 0; const char *zSep = ""; const char *zSqlSep = ""; char *zSql = 0; char *zFinal = 0; char *zBind = 0; int ii; int bSql = p->xSql ? 1 : 0; if( nField<=0 ) return 0; assert( nField<=pTab->nCol ); zSql = recoverMPrintf(p, "INSERT OR IGNORE INTO %Q(", pTab->zTab); if( pTab->iRowidBind ){ assert( pTab->bIntkey ); zSql = recoverMPrintf(p, "%z_rowid_", zSql); if( bSql ){ zBind = recoverMPrintf(p, "%zquote(?%d)", zBind, pTab->iRowidBind); }else{ zBind = recoverMPrintf(p, "%z?%d", zBind, pTab->iRowidBind); } zSqlSep = "||', '||"; zSep = ", "; } for(ii=0; iiaCol[ii].eHidden; if( eHidden!=RECOVER_EHIDDEN_VIRTUAL && eHidden!=RECOVER_EHIDDEN_STORED ){ assert( pTab->aCol[ii].iField>=0 && pTab->aCol[ii].iBind>=1 ); zSql = recoverMPrintf(p, "%z%s%Q", zSql, zSep, pTab->aCol[ii].zCol); if( bSql ){ zBind = recoverMPrintf(p, "%z%sescape_crnl(quote(?%d))", zBind, zSqlSep, pTab->aCol[ii].iBind ); zSqlSep = "||', '||"; }else{ zBind = recoverMPrintf(p, "%z%s?%d", zBind, zSep, pTab->aCol[ii].iBind); } zSep = ", "; } } if( bSql ){ zFinal = recoverMPrintf(p, "SELECT %Q || ') VALUES (' || %s || ')'", zSql, zBind ); }else{ zFinal = recoverMPrintf(p, "%s) VALUES (%s)", zSql, zBind); } pRet = recoverPrepare(p, p->dbOut, zFinal); sqlite3_free(zSql); sqlite3_free(zBind); sqlite3_free(zFinal); return pRet; } /* ** 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); return pRet; } /* ** This function attempts to create a lost and found table within the ** output db. If successful, it returns a pointer to a buffer containing ** the name of the new table. It is the responsibility of the caller to ** eventually free this buffer using sqlite3_free(). ** ** If an error occurs, NULL is returned and an error code and error ** message left in the recover handle. */ static char *recoverLostAndFoundCreate( sqlite3_recover *p, /* Recover object */ int nField /* Number of column fields in new table */ ){ char *zTbl = 0; sqlite3_stmt *pProbe = 0; int ii = 0; pProbe = recoverPrepare(p, p->dbOut, "SELECT 1 FROM sqlite_schema WHERE name=?" ); for(ii=-1; zTbl==0 && p->errCode==SQLITE_OK && ii<1000; ii++){ int bFail = 0; if( ii<0 ){ zTbl = recoverMPrintf(p, "%s", p->zLostAndFound); }else{ zTbl = recoverMPrintf(p, "%s_%d", p->zLostAndFound, ii); } if( p->errCode==SQLITE_OK ){ sqlite3_bind_text(pProbe, 1, zTbl, -1, SQLITE_STATIC); if( SQLITE_ROW==sqlite3_step(pProbe) ){ bFail = 1; } recoverReset(p, pProbe); } if( bFail ){ sqlite3_clear_bindings(pProbe); sqlite3_free(zTbl); zTbl = 0; } } recoverFinalize(p, pProbe); if( zTbl ){ const char *zSep = 0; char *zField = 0; char *zSql = 0; zSep = "rootpgno INTEGER, pgno INTEGER, nfield INTEGER, id INTEGER, "; for(ii=0; p->errCode==SQLITE_OK && iidbOut, zSql); recoverSqlCallback(p, zSql); sqlite3_free(zSql); }else if( p->errCode==SQLITE_OK ){ recoverError( p, SQLITE_ERROR, "failed to create %s output table", p->zLostAndFound ); } return zTbl; } /* ** Synthesize and prepare an INSERT statement to write to the lost_and_found ** table in the output database. The name of the table is zTab, and it has ** nField c* fields. */ static sqlite3_stmt *recoverLostAndFoundInsert( sqlite3_recover *p, const char *zTab, int nField ){ int nTotal = nField + 4; int ii; char *zBind = 0; sqlite3_stmt *pRet = 0; if( p->xSql==0 ){ for(ii=0; iidbOut, "INSERT INTO %s VALUES(%s)", zTab, zBind ); }else{ const char *zSep = ""; for(ii=0; iidbOut, "SELECT 'INSERT INTO %s VALUES(' || %s || ')'", zTab, zBind ); } sqlite3_free(zBind); return pRet; } /* ** Input database page iPg contains data that will be written to the ** lost-and-found table of the output database. This function attempts ** to identify the root page of the tree that page iPg belonged to. ** If successful, it sets output variable (*piRoot) to the page number ** of the root page and returns SQLITE_OK. Otherwise, if an error occurs, ** an SQLite error code is returned and the final value of *piRoot ** undefined. */ static int recoverLostAndFoundFindRoot( sqlite3_recover *p, i64 iPg, i64 *piRoot ){ RecoverStateLAF *pLaf = &p->laf; if( pLaf->pFindRoot==0 ){ pLaf->pFindRoot = recoverPrepare(p, p->dbOut, "WITH RECURSIVE p(pgno) AS (" " SELECT ?" " UNION" " SELECT parent FROM recovery.map AS m, p WHERE m.pgno=p.pgno" ") " "SELECT p.pgno FROM p, recovery.map m WHERE m.pgno=p.pgno " " AND m.parent IS NULL" ); } if( p->errCode==SQLITE_OK ){ sqlite3_bind_int64(pLaf->pFindRoot, 1, iPg); if( sqlite3_step(pLaf->pFindRoot)==SQLITE_ROW ){ *piRoot = sqlite3_column_int64(pLaf->pFindRoot, 0); }else{ *piRoot = iPg; } recoverReset(p, pLaf->pFindRoot); } return p->errCode; } /* ** Recover data from page iPage of the input database and write it to ** the lost-and-found table in the output database. */ static void recoverLostAndFoundOnePage(sqlite3_recover *p, i64 iPage){ RecoverStateLAF *pLaf = &p->laf; sqlite3_value **apVal = pLaf->apVal; sqlite3_stmt *pPageData = pLaf->pPageData; sqlite3_stmt *pInsert = pLaf->pInsert; int nVal = -1; int iPrevCell = 0; i64 iRoot = 0; int bHaveRowid = 0; i64 iRowid = 0; int ii = 0; if( recoverLostAndFoundFindRoot(p, iPage, &iRoot) ) return; sqlite3_bind_int64(pPageData, 1, iPage); while( p->errCode==SQLITE_OK && SQLITE_ROW==sqlite3_step(pPageData) ){ int iCell = sqlite3_column_int64(pPageData, 0); int iField = sqlite3_column_int64(pPageData, 1); if( iPrevCell!=iCell && nVal>=0 ){ /* Insert the new row */ sqlite3_bind_int64(pInsert, 1, iRoot); /* rootpgno */ sqlite3_bind_int64(pInsert, 2, iPage); /* pgno */ sqlite3_bind_int(pInsert, 3, nVal); /* nfield */ if( bHaveRowid ){ sqlite3_bind_int64(pInsert, 4, iRowid); /* id */ } for(ii=0; iinMaxField ){ sqlite3_value *pVal = sqlite3_column_value(pPageData, 2); apVal[iField] = sqlite3_value_dup(pVal); assert( iField==nVal || (nVal==-1 && iField==0) ); nVal = iField+1; if( apVal[iField]==0 ){ recoverError(p, SQLITE_NOMEM, 0); } } iPrevCell = iCell; } recoverReset(p, pPageData); for(ii=0; iilaf; if( p->errCode==SQLITE_OK ){ if( pLaf->pInsert==0 ){ return SQLITE_DONE; }else{ if( p->errCode==SQLITE_OK ){ int res = sqlite3_step(pLaf->pAllPage); if( res==SQLITE_ROW ){ i64 iPage = sqlite3_column_int64(pLaf->pAllPage, 0); if( recoverBitmapQuery(pLaf->pUsed, iPage)==0 ){ recoverLostAndFoundOnePage(p, iPage); } }else{ recoverReset(p, pLaf->pAllPage); return SQLITE_DONE; } } } } return SQLITE_OK; } /* ** Initialize resources required in RECOVER_STATE_LOSTANDFOUND3 ** state - during which the lost-and-found table of the output database ** is populated with recovered data that can not be assigned to any ** recovered schema object. */ static void recoverLostAndFound3Init(sqlite3_recover *p){ RecoverStateLAF *pLaf = &p->laf; if( pLaf->nMaxField>0 ){ char *zTab = 0; /* Name of lost_and_found table */ zTab = recoverLostAndFoundCreate(p, pLaf->nMaxField); pLaf->pInsert = recoverLostAndFoundInsert(p, zTab, pLaf->nMaxField); sqlite3_free(zTab); pLaf->pAllPage = recoverPreparePrintf(p, p->dbOut, "WITH RECURSIVE seq(ii) AS (" " SELECT 1 UNION ALL SELECT ii+1 FROM seq WHERE ii<%lld" ")" "SELECT ii FROM seq" , p->laf.nPg ); pLaf->pPageData = recoverPrepare(p, p->dbOut, "SELECT cell, field, value " "FROM sqlite_dbdata('getpage()') d WHERE d.pgno=? " "UNION ALL " "SELECT -1, -1, -1" ); pLaf->apVal = (sqlite3_value**)recoverMalloc(p, pLaf->nMaxField*sizeof(sqlite3_value*) ); } } /* ** Initialize resources required in RECOVER_STATE_WRITING state - during which ** tables recovered from the schema of the input database are populated with ** recovered data. */ static int recoverWriteDataInit(sqlite3_recover *p){ RecoverStateW1 *p1 = &p->w1; RecoverTable *pTbl = 0; int nByte = 0; /* Figure out the maximum number of columns for any table in the schema */ assert( p1->nMax==0 ); for(pTbl=p->pTblList; pTbl; pTbl=pTbl->pNext){ if( pTbl->nCol>p1->nMax ) p1->nMax = pTbl->nCol; } /* Allocate an array of (sqlite3_value*) in which to accumulate the values ** that will be written to the output database in a single row. */ nByte = sizeof(sqlite3_value*) * (p1->nMax+1); p1->apVal = (sqlite3_value**)recoverMalloc(p, nByte); if( p1->apVal==0 ) return p->errCode; /* Prepare the SELECT to loop through schema tables (pTbls) and the SELECT ** to loop through cells that appear to belong to a single table (pSel). */ p1->pTbls = recoverPrepare(p, p->dbOut, "SELECT rootpage FROM recovery.schema " " WHERE type='table' AND (sql NOT LIKE 'create virtual%')" " ORDER BY (tbl_name='sqlite_sequence') ASC" ); p1->pSel = recoverPrepare(p, p->dbOut, "WITH RECURSIVE pages(page) AS (" " SELECT ?1" " UNION" " SELECT child FROM sqlite_dbptr('getpage()'), pages " " WHERE pgno=page" ") " "SELECT page, cell, field, value " "FROM sqlite_dbdata('getpage()') d, pages p WHERE p.page=d.pgno " "UNION ALL " "SELECT 0, 0, 0, 0" ); return p->errCode; } /* ** Clean up resources allocated by recoverWriteDataInit() (stuff in ** sqlite3_recover.w1). */ static void recoverWriteDataCleanup(sqlite3_recover *p){ RecoverStateW1 *p1 = &p->w1; int ii; for(ii=0; iinVal; ii++){ sqlite3_value_free(p1->apVal[ii]); } sqlite3_free(p1->apVal); recoverFinalize(p, p1->pInsert); recoverFinalize(p, p1->pTbls); recoverFinalize(p, p1->pSel); memset(p1, 0, sizeof(*p1)); } /* ** Perform one step (sqlite3_recover_step()) of work for the connection ** passed as the only argument, which is guaranteed to be in ** RECOVER_STATE_WRITING state - during which tables recovered from the ** schema of the input database are populated with recovered data. */ static int recoverWriteDataStep(sqlite3_recover *p){ RecoverStateW1 *p1 = &p->w1; sqlite3_stmt *pSel = p1->pSel; sqlite3_value **apVal = p1->apVal; if( p->errCode==SQLITE_OK && p1->pTab==0 ){ if( sqlite3_step(p1->pTbls)==SQLITE_ROW ){ i64 iRoot = sqlite3_column_int64(p1->pTbls, 0); p1->pTab = recoverFindTable(p, iRoot); recoverFinalize(p, p1->pInsert); p1->pInsert = 0; /* If this table is unknown, return early. The caller will invoke this ** function again and it will move on to the next table. */ if( p1->pTab==0 ) return p->errCode; /* If this is the sqlite_sequence table, delete any rows added by ** earlier INSERT statements on tables with AUTOINCREMENT primary ** keys before recovering its contents. The p1->pTbls SELECT statement ** is rigged to deliver "sqlite_sequence" last of all, so we don't ** worry about it being modified after it is recovered. */ if( sqlite3_stricmp("sqlite_sequence", p1->pTab->zTab)==0 ){ recoverExec(p, p->dbOut, "DELETE FROM sqlite_sequence"); recoverSqlCallback(p, "DELETE FROM sqlite_sequence"); } /* Bind the root page of this table within the original database to ** SELECT statement p1->pSel. The SELECT statement will then iterate ** through cells that look like they belong to table pTab. */ sqlite3_bind_int64(pSel, 1, iRoot); p1->nVal = 0; p1->bHaveRowid = 0; p1->iPrevPage = -1; p1->iPrevCell = -1; }else{ return SQLITE_DONE; } } assert( p->errCode!=SQLITE_OK || p1->pTab ); if( p->errCode==SQLITE_OK && sqlite3_step(pSel)==SQLITE_ROW ){ RecoverTable *pTab = p1->pTab; i64 iPage = sqlite3_column_int64(pSel, 0); int iCell = sqlite3_column_int(pSel, 1); int iField = sqlite3_column_int(pSel, 2); sqlite3_value *pVal = sqlite3_column_value(pSel, 3); int bNewCell = (p1->iPrevPage!=iPage || p1->iPrevCell!=iCell); assert( bNewCell==0 || (iField==-1 || iField==0) ); assert( bNewCell || iField==p1->nVal || p1->nVal==pTab->nCol ); if( bNewCell ){ int ii = 0; if( p1->nVal>=0 ){ if( p1->pInsert==0 || p1->nVal!=p1->nInsert ){ recoverFinalize(p, p1->pInsert); p1->pInsert = recoverInsertStmt(p, pTab, p1->nVal); p1->nInsert = p1->nVal; } if( p1->nVal>0 ){ sqlite3_stmt *pInsert = p1->pInsert; for(ii=0; iinCol; ii++){ RecoverColumn *pCol = &pTab->aCol[ii]; int iBind = pCol->iBind; if( iBind>0 ){ if( pCol->bIPK ){ sqlite3_bind_int64(pInsert, iBind, p1->iRowid); }else if( pCol->iFieldnVal ){ recoverBindValue(p, pInsert, iBind, apVal[pCol->iField]); } } } if( p->bRecoverRowid && pTab->iRowidBind>0 && p1->bHaveRowid ){ sqlite3_bind_int64(pInsert, pTab->iRowidBind, p1->iRowid); } if( SQLITE_ROW==sqlite3_step(pInsert) ){ const char *z = (const char*)sqlite3_column_text(pInsert, 0); recoverSqlCallback(p, z); } recoverReset(p, pInsert); assert( p->errCode || pInsert ); if( pInsert ) sqlite3_clear_bindings(pInsert); } } for(ii=0; iinVal; ii++){ sqlite3_value_free(apVal[ii]); apVal[ii] = 0; } p1->nVal = -1; p1->bHaveRowid = 0; } if( iPage!=0 ){ if( iField<0 ){ p1->iRowid = sqlite3_column_int64(pSel, 3); assert( p1->nVal==-1 ); p1->nVal = 0; p1->bHaveRowid = 1; }else if( iFieldnCol ){ assert( apVal[iField]==0 ); apVal[iField] = sqlite3_value_dup( pVal ); if( apVal[iField]==0 ){ recoverError(p, SQLITE_NOMEM, 0); } p1->nVal = iField+1; } p1->iPrevCell = iCell; p1->iPrevPage = iPage; } }else{ recoverReset(p, pSel); p1->pTab = 0; } return p->errCode; } /* ** Initialize resources required by sqlite3_recover_step() in ** RECOVER_STATE_LOSTANDFOUND1 state - during which the set of pages not ** already allocated to a recovered schema element is determined. */ static void recoverLostAndFound1Init(sqlite3_recover *p){ RecoverStateLAF *pLaf = &p->laf; sqlite3_stmt *pStmt = 0; assert( p->laf.pUsed==0 ); pLaf->nPg = recoverPageCount(p); pLaf->pUsed = recoverBitmapAlloc(p, pLaf->nPg); /* Prepare a statement to iterate through all pages that are part of any tree ** in the recoverable part of the input database schema to the bitmap. And, ** if !p->bFreelistCorrupt, add all pages that appear to be part of the ** freelist. */ pStmt = recoverPrepare( p, p->dbOut, "WITH trunk(pgno) AS (" " SELECT read_i32(getpage(1), 8) AS x WHERE x>0" " UNION" " SELECT read_i32(getpage(trunk.pgno), 0) AS x FROM trunk WHERE x>0" ")," "trunkdata(pgno, data) AS (" " SELECT pgno, getpage(pgno) FROM trunk" ")," "freelist(data, n, freepgno) AS (" " SELECT data, min(16384, read_i32(data, 1)-1), pgno FROM trunkdata" " UNION ALL" " SELECT data, n-1, read_i32(data, 2+n) FROM freelist WHERE n>=0" ")," "" "roots(r) AS (" " SELECT 1 UNION ALL" " SELECT rootpage FROM recovery.schema WHERE rootpage>0" ")," "used(page) AS (" " SELECT r FROM roots" " UNION" " SELECT child FROM sqlite_dbptr('getpage()'), used " " WHERE pgno=page" ") " "SELECT page FROM used" " UNION ALL " "SELECT freepgno FROM freelist WHERE NOT ?" ); if( pStmt ) sqlite3_bind_int(pStmt, 1, p->bFreelistCorrupt); pLaf->pUsedPages = pStmt; } /* ** Perform one step (sqlite3_recover_step()) of work for the connection ** passed as the only argument, which is guaranteed to be in ** RECOVER_STATE_LOSTANDFOUND1 state - during which the set of pages not ** already allocated to a recovered schema element is determined. */ static int recoverLostAndFound1Step(sqlite3_recover *p){ RecoverStateLAF *pLaf = &p->laf; int rc = p->errCode; if( rc==SQLITE_OK ){ rc = sqlite3_step(pLaf->pUsedPages); if( rc==SQLITE_ROW ){ i64 iPg = sqlite3_column_int64(pLaf->pUsedPages, 0); recoverBitmapSet(pLaf->pUsed, iPg); rc = SQLITE_OK; }else{ recoverFinalize(p, pLaf->pUsedPages); pLaf->pUsedPages = 0; } } return rc; } /* ** Initialize resources required by RECOVER_STATE_LOSTANDFOUND2 ** state - during which the pages identified in RECOVER_STATE_LOSTANDFOUND1 ** are sorted into sets that likely belonged to the same database tree. */ static void recoverLostAndFound2Init(sqlite3_recover *p){ RecoverStateLAF *pLaf = &p->laf; assert( p->laf.pAllAndParent==0 ); assert( p->laf.pMapInsert==0 ); assert( p->laf.pMaxField==0 ); assert( p->laf.nMaxField==0 ); pLaf->pMapInsert = recoverPrepare(p, p->dbOut, "INSERT OR IGNORE INTO recovery.map(pgno, parent) VALUES(?, ?)" ); pLaf->pAllAndParent = recoverPreparePrintf(p, p->dbOut, "WITH RECURSIVE seq(ii) AS (" " SELECT 1 UNION ALL SELECT ii+1 FROM seq WHERE ii<%lld" ")" "SELECT pgno, child FROM sqlite_dbptr('getpage()') " " UNION ALL " "SELECT NULL, ii FROM seq", p->laf.nPg ); pLaf->pMaxField = recoverPreparePrintf(p, p->dbOut, "SELECT max(field)+1 FROM sqlite_dbdata('getpage') WHERE pgno = ?" ); } /* ** Perform one step (sqlite3_recover_step()) of work for the connection ** passed as the only argument, which is guaranteed to be in ** RECOVER_STATE_LOSTANDFOUND2 state - during which the pages identified ** in RECOVER_STATE_LOSTANDFOUND1 are sorted into sets that likely belonged ** to the same database tree. */ static int recoverLostAndFound2Step(sqlite3_recover *p){ RecoverStateLAF *pLaf = &p->laf; if( p->errCode==SQLITE_OK ){ int res = sqlite3_step(pLaf->pAllAndParent); if( res==SQLITE_ROW ){ i64 iChild = sqlite3_column_int(pLaf->pAllAndParent, 1); if( recoverBitmapQuery(pLaf->pUsed, iChild)==0 ){ sqlite3_bind_int64(pLaf->pMapInsert, 1, iChild); sqlite3_bind_value(pLaf->pMapInsert, 2, sqlite3_column_value(pLaf->pAllAndParent, 0) ); sqlite3_step(pLaf->pMapInsert); recoverReset(p, pLaf->pMapInsert); sqlite3_bind_int64(pLaf->pMaxField, 1, iChild); if( SQLITE_ROW==sqlite3_step(pLaf->pMaxField) ){ int nMax = sqlite3_column_int(pLaf->pMaxField, 0); if( nMax>pLaf->nMaxField ) pLaf->nMaxField = nMax; } recoverReset(p, pLaf->pMaxField); } }else{ recoverFinalize(p, pLaf->pAllAndParent); pLaf->pAllAndParent =0; return SQLITE_DONE; } } return p->errCode; } /* ** Free all resources allocated as part of sqlite3_recover_step() calls ** in one of the RECOVER_STATE_LOSTANDFOUND[123] states. */ static void recoverLostAndFoundCleanup(sqlite3_recover *p){ recoverBitmapFree(p->laf.pUsed); p->laf.pUsed = 0; sqlite3_finalize(p->laf.pUsedPages); sqlite3_finalize(p->laf.pAllAndParent); sqlite3_finalize(p->laf.pMapInsert); sqlite3_finalize(p->laf.pMaxField); sqlite3_finalize(p->laf.pFindRoot); sqlite3_finalize(p->laf.pInsert); sqlite3_finalize(p->laf.pAllPage); sqlite3_finalize(p->laf.pPageData); p->laf.pUsedPages = 0; p->laf.pAllAndParent = 0; p->laf.pMapInsert = 0; p->laf.pMaxField = 0; p->laf.pFindRoot = 0; p->laf.pInsert = 0; p->laf.pAllPage = 0; p->laf.pPageData = 0; sqlite3_free(p->laf.apVal); p->laf.apVal = 0; } /* ** Free all resources allocated as part of sqlite3_recover_step() calls. */ static void recoverFinalCleanup(sqlite3_recover *p){ RecoverTable *pTab = 0; RecoverTable *pNext = 0; recoverWriteDataCleanup(p); recoverLostAndFoundCleanup(p); for(pTab=p->pTblList; pTab; pTab=pNext){ pNext = pTab->pNext; sqlite3_free(pTab); } p->pTblList = 0; sqlite3_finalize(p->pGetPage); p->pGetPage = 0; { int res = sqlite3_close(p->dbOut); assert( res==SQLITE_OK ); } p->dbOut = 0; } /* ** Decode and return an unsigned 16-bit big-endian integer value from ** buffer a[]. */ static u32 recoverGetU16(const u8 *a){ return (((u32)a[0])<<8) + ((u32)a[1]); } /* ** Decode and return an unsigned 32-bit big-endian integer value from ** buffer a[]. */ static u32 recoverGetU32(const u8 *a){ return (((u32)a[0])<<24) + (((u32)a[1])<<16) + (((u32)a[2])<<8) + ((u32)a[3]); } /* ** Decode an SQLite varint from buffer a[]. Write the decoded value to (*pVal) ** and return the number of bytes consumed. */ static int recoverGetVarint(const u8 *a, i64 *pVal){ sqlite3_uint64 u = 0; int i; for(i=0; i<8; i++){ u = (u<<7) + (a[i]&0x7f); if( (a[i]&0x80)==0 ){ *pVal = (sqlite3_int64)u; return i+1; } } u = (u<<8) + (a[i]&0xff); *pVal = (sqlite3_int64)u; return 9; } /* ** The second argument points to a buffer n bytes in size. If this buffer ** or a prefix thereof appears to contain a well-formed SQLite b-tree page, ** return the page-size in bytes. Otherwise, if the buffer does not ** appear to contain a well-formed b-tree page, return 0. */ static int recoverIsValidPage(u8 *aTmp, const u8 *a, int n){ u8 *aUsed = aTmp; int nFrag = 0; int nActual = 0; int iFree = 0; int nCell = 0; /* Number of cells on page */ int iCellOff = 0; /* Offset of cell array in page */ int iContent = 0; int eType = 0; int ii = 0; eType = (int)a[0]; if( eType!=0x02 && eType!=0x05 && eType!=0x0A && eType!=0x0D ) return 0; iFree = (int)recoverGetU16(&a[1]); nCell = (int)recoverGetU16(&a[3]); iContent = (int)recoverGetU16(&a[5]); if( iContent==0 ) iContent = 65536; nFrag = (int)a[7]; if( iContent>n ) return 0; memset(aUsed, 0, n); memset(aUsed, 0xFF, iContent); /* Follow the free-list. This is the same format for all b-tree pages. */ if( iFree && iFree<=iContent ) return 0; while( iFree ){ int iNext = 0; int nByte = 0; if( iFree>(n-4) ) return 0; iNext = recoverGetU16(&a[iFree]); nByte = recoverGetU16(&a[iFree+2]); if( iFree+nByte>n ) return 0; if( iNext && iNextiContent ) return 0; for(ii=0; iin ){ return 0; } if( eType==0x05 || eType==0x02 ) nByte += 4; nByte += recoverGetVarint(&a[iOff+nByte], &nPayload); if( eType==0x0D ){ i64 dummy = 0; nByte += recoverGetVarint(&a[iOff+nByte], &dummy); } if( eType!=0x05 ){ int X = (eType==0x0D) ? n-35 : (((n-12)*64/255)-23); int M = ((n-12)*32/255)-23; int K = M+((nPayload-M)%(n-4)); if( nPayloadn ){ return 0; } for(iByte=iOff; iByte<(iOff+nByte); iByte++){ if( aUsed[iByte]!=0 ){ return 0; } aUsed[iByte] = 0xFF; } } nActual = 0; for(ii=0; iipMethods!=&recover_methods ); return pFd->pMethods->xClose(pFd); } /* ** Write value v to buffer a[] as a 16-bit big-endian unsigned integer. */ static void recoverPutU16(u8 *a, u32 v){ a[0] = (v>>8) & 0x00FF; a[1] = (v>>0) & 0x00FF; } /* ** Write value v to buffer a[] as a 32-bit big-endian unsigned integer. */ static void recoverPutU32(u8 *a, u32 v){ a[0] = (v>>24) & 0x00FF; a[1] = (v>>16) & 0x00FF; a[2] = (v>>8) & 0x00FF; a[3] = (v>>0) & 0x00FF; } /* ** Detect the page-size of the database opened by file-handle pFd by ** searching the first part of the file for a well-formed SQLite b-tree ** page. If parameter nReserve is non-zero, then as well as searching for ** a b-tree page with zero reserved bytes, this function searches for one ** with nReserve reserved bytes at the end of it. ** ** If successful, set variable p->detected_pgsz to the detected page-size ** in bytes and return SQLITE_OK. Or, if no error occurs but no valid page ** can be found, return SQLITE_OK but leave p->detected_pgsz set to 0. Or, ** if an error occurs (e.g. an IO or OOM error), then an SQLite error code ** is returned. The final value of p->detected_pgsz is undefined in this ** case. */ static int recoverVfsDetectPagesize( sqlite3_recover *p, /* Recover handle */ sqlite3_file *pFd, /* File-handle open on input database */ u32 nReserve, /* Possible nReserve value */ i64 nSz /* Size of database file in bytes */ ){ int rc = SQLITE_OK; const int nMin = 512; const int nMax = 65536; const int nMaxBlk = 4; u32 pgsz = 0; int iBlk = 0; u8 *aPg = 0; u8 *aTmp = 0; int nBlk = 0; aPg = (u8*)sqlite3_malloc(2*nMax); if( aPg==0 ) return SQLITE_NOMEM; aTmp = &aPg[nMax]; nBlk = (nSz+nMax-1)/nMax; if( nBlk>nMaxBlk ) nBlk = nMaxBlk; do { for(iBlk=0; rc==SQLITE_OK && iBlk=((iBlk+1)*nMax)) ? nMax : (nSz % nMax); memset(aPg, 0, nMax); rc = pFd->pMethods->xRead(pFd, aPg, nByte, iBlk*nMax); if( rc==SQLITE_OK ){ int pgsz2; for(pgsz2=(pgsz ? pgsz*2 : nMin); pgsz2<=nMax; pgsz2=pgsz2*2){ int iOff; for(iOff=0; iOffp->detected_pgsz ){ p->detected_pgsz = pgsz; p->nReserve = nReserve; } if( nReserve==0 ) break; nReserve = 0; }while( 1 ); p->detected_pgsz = pgsz; sqlite3_free(aPg); return rc; } /* ** The xRead() method of the wrapper VFS. This is used to intercept calls ** to read page 1 of the input database. */ static int recoverVfsRead(sqlite3_file *pFd, void *aBuf, int nByte, i64 iOff){ int rc = SQLITE_OK; if( pFd->pMethods==&recover_methods ){ pFd->pMethods = recover_g.pMethods; rc = pFd->pMethods->xRead(pFd, aBuf, nByte, iOff); if( nByte==16 ){ sqlite3_randomness(16, aBuf); }else if( rc==SQLITE_OK && iOff==0 && nByte>=108 ){ /* Ensure that the database has a valid header file. The only fields ** that really matter to recovery are: ** ** + Database page size (16-bits at offset 16) ** + Size of db in pages (32-bits at offset 28) ** + Database encoding (32-bits at offset 56) ** ** Also preserved are: ** ** + first freelist page (32-bits at offset 32) ** + size of freelist (32-bits at offset 36) ** ** We also try to preserve the auto-vacuum, incr-value, user-version ** and application-id fields - all 32 bit quantities at offsets ** 52, 60, 64 and 68. All other fields are set to known good values. ** ** Byte offset 105 should also contain the page-size as a 16-bit ** integer. */ const int aPreserve[] = {32, 36, 52, 60, 64, 68}; u8 aHdr[108] = { 0x53, 0x51, 0x4c, 0x69, 0x74, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20, 0x33, 0x00, 0xFF, 0xFF, 0x01, 0x01, 0x00, 0x40, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x10, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x5b, 0x30, 0x0D, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00 }; u8 *a = (u8*)aBuf; u32 pgsz = recoverGetU16(&a[16]); u32 nReserve = a[20]; u32 enc = recoverGetU32(&a[56]); u32 dbsz = 0; i64 dbFileSize = 0; int ii; sqlite3_recover *p = recover_g.p; if( pgsz==0x01 ) pgsz = 65536; rc = pFd->pMethods->xFileSize(pFd, &dbFileSize); if( rc==SQLITE_OK && p->detected_pgsz==0 ){ rc = recoverVfsDetectPagesize(p, pFd, nReserve, dbFileSize); } if( p->detected_pgsz ){ pgsz = p->detected_pgsz; nReserve = p->nReserve; } if( pgsz ){ dbsz = dbFileSize / pgsz; } if( enc!=SQLITE_UTF8 && enc!=SQLITE_UTF16BE && enc!=SQLITE_UTF16LE ){ enc = SQLITE_UTF8; } sqlite3_free(p->pPage1Cache); p->pPage1Cache = 0; p->pPage1Disk = 0; p->pgsz = nByte; p->pPage1Cache = (u8*)recoverMalloc(p, nByte*2); if( p->pPage1Cache ){ p->pPage1Disk = &p->pPage1Cache[nByte]; memcpy(p->pPage1Disk, aBuf, nByte); recoverPutU32(&aHdr[28], dbsz); recoverPutU32(&aHdr[56], enc); recoverPutU16(&aHdr[105], pgsz-nReserve); if( pgsz==65536 ) pgsz = 1; recoverPutU16(&aHdr[16], pgsz); aHdr[20] = nReserve; for(ii=0; iipPage1Cache, aBuf, nByte); }else{ rc = p->errCode; } } pFd->pMethods = &recover_methods; }else{ rc = pFd->pMethods->xRead(pFd, aBuf, nByte, iOff); } return rc; } /* ** Used to make sqlite3_io_methods wrapper methods less verbose. */ #define RECOVER_VFS_WRAPPER(code) \ int rc = SQLITE_OK; \ if( pFd->pMethods==&recover_methods ){ \ pFd->pMethods = recover_g.pMethods; \ rc = code; \ pFd->pMethods = &recover_methods; \ }else{ \ rc = code; \ } \ return rc; /* ** Methods of the wrapper VFS. All methods except for xRead() and xClose() ** simply uninstall the sqlite3_io_methods wrapper, invoke the equivalent ** method on the lower level VFS, then reinstall the wrapper before returning. ** Those that return an integer value use the RECOVER_VFS_WRAPPER macro. */ static int recoverVfsWrite( sqlite3_file *pFd, const void *aBuf, int nByte, i64 iOff ){ RECOVER_VFS_WRAPPER ( pFd->pMethods->xWrite(pFd, aBuf, nByte, iOff) ); } static int recoverVfsTruncate(sqlite3_file *pFd, sqlite3_int64 size){ RECOVER_VFS_WRAPPER ( pFd->pMethods->xTruncate(pFd, size) ); } static int recoverVfsSync(sqlite3_file *pFd, int flags){ RECOVER_VFS_WRAPPER ( pFd->pMethods->xSync(pFd, flags) ); } static int recoverVfsFileSize(sqlite3_file *pFd, sqlite3_int64 *pSize){ RECOVER_VFS_WRAPPER ( pFd->pMethods->xFileSize(pFd, pSize) ); } static int recoverVfsLock(sqlite3_file *pFd, int eLock){ RECOVER_VFS_WRAPPER ( pFd->pMethods->xLock(pFd, eLock) ); } static int recoverVfsUnlock(sqlite3_file *pFd, int eLock){ RECOVER_VFS_WRAPPER ( pFd->pMethods->xUnlock(pFd, eLock) ); } static int recoverVfsCheckReservedLock(sqlite3_file *pFd, int *pResOut){ RECOVER_VFS_WRAPPER ( pFd->pMethods->xCheckReservedLock(pFd, pResOut) ); } static int recoverVfsFileControl(sqlite3_file *pFd, int op, void *pArg){ RECOVER_VFS_WRAPPER ( (pFd->pMethods ? pFd->pMethods->xFileControl(pFd, op, pArg) : SQLITE_NOTFOUND) ); } static int recoverVfsSectorSize(sqlite3_file *pFd){ RECOVER_VFS_WRAPPER ( pFd->pMethods->xSectorSize(pFd) ); } static int recoverVfsDeviceCharacteristics(sqlite3_file *pFd){ RECOVER_VFS_WRAPPER ( pFd->pMethods->xDeviceCharacteristics(pFd) ); } static int recoverVfsShmMap( sqlite3_file *pFd, int iPg, int pgsz, int bExtend, void volatile **pp ){ RECOVER_VFS_WRAPPER ( pFd->pMethods->xShmMap(pFd, iPg, pgsz, bExtend, pp) ); } static int recoverVfsShmLock(sqlite3_file *pFd, int offset, int n, int flags){ RECOVER_VFS_WRAPPER ( pFd->pMethods->xShmLock(pFd, offset, n, flags) ); } static void recoverVfsShmBarrier(sqlite3_file *pFd){ if( pFd->pMethods==&recover_methods ){ pFd->pMethods = recover_g.pMethods; pFd->pMethods->xShmBarrier(pFd); pFd->pMethods = &recover_methods; }else{ pFd->pMethods->xShmBarrier(pFd); } } static int recoverVfsShmUnmap(sqlite3_file *pFd, int deleteFlag){ RECOVER_VFS_WRAPPER ( pFd->pMethods->xShmUnmap(pFd, deleteFlag) ); } /* ** Install the VFS wrapper around the file-descriptor open on the input ** database for recover handle p. Mutex RECOVER_MUTEX_ID must be held ** when this function is called. */ static void recoverInstallWrapper(sqlite3_recover *p){ sqlite3_file *pFd = 0; assert( recover_g.pMethods==0 ); recoverAssertMutexHeld(); sqlite3_file_control(p->dbIn, p->zDb, SQLITE_FCNTL_FILE_POINTER, (void*)&pFd); assert( pFd==0 || pFd->pMethods!=&recover_methods ); if( pFd ){ int iVersion = 1 + (pFd->pMethods->iVersion>1 && pFd->pMethods->xShmMap!=0); recover_g.pMethods = pFd->pMethods; recover_g.p = p; recover_methods.iVersion = iVersion; pFd->pMethods = &recover_methods; } } /* ** Uninstall the VFS wrapper that was installed around the file-descriptor open ** on the input database for recover handle p. Mutex RECOVER_MUTEX_ID must be ** held when this function is called. */ static void recoverUninstallWrapper(sqlite3_recover *p){ recoverAssertMutexHeld(); sqlite3_file *pFd = 0; sqlite3_file_control(p->dbIn, p->zDb,SQLITE_FCNTL_FILE_POINTER,(void*)&pFd); if( pFd ){ pFd->pMethods = recover_g.pMethods; recover_g.pMethods = 0; recover_g.p = 0; } } /* ** This function does the work of a single sqlite3_recover_step() call. It ** is guaranteed that the handle is not in an error state when this ** function is called. */ static void recoverStep(sqlite3_recover *p){ assert( p && p->errCode==SQLITE_OK ); switch( p->eState ){ case RECOVER_STATE_INIT: /* This is the very first call to sqlite3_recover_step() on this object. */ recoverSqlCallback(p, "BEGIN"); recoverSqlCallback(p, "PRAGMA writable_schema = on"); recoverEnterMutex(); recoverInstallWrapper(p); /* 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, "PRAGMA writable_schema = on"); recoverExec(p, p->dbIn, "BEGIN"); if( p->errCode==SQLITE_OK ) p->bCloseTransaction = 1; recoverExec(p, p->dbIn, "SELECT 1 FROM sqlite_schema"); recoverTransferSettings(p); recoverOpenRecovery(p); recoverCacheSchema(p); recoverUninstallWrapper(p); recoverLeaveMutex(); recoverExec(p, p->dbOut, "BEGIN"); recoverWriteSchema1(p); p->eState = RECOVER_STATE_WRITING; break; case RECOVER_STATE_WRITING: { if( p->w1.pTbls==0 ){ recoverWriteDataInit(p); } if( SQLITE_DONE==recoverWriteDataStep(p) ){ recoverWriteDataCleanup(p); if( p->zLostAndFound ){ p->eState = RECOVER_STATE_LOSTANDFOUND1; }else{ p->eState = RECOVER_STATE_SCHEMA2; } } break; } case RECOVER_STATE_LOSTANDFOUND1: { if( p->laf.pUsed==0 ){ recoverLostAndFound1Init(p); } if( SQLITE_DONE==recoverLostAndFound1Step(p) ){ p->eState = RECOVER_STATE_LOSTANDFOUND2; } break; } case RECOVER_STATE_LOSTANDFOUND2: { if( p->laf.pAllAndParent==0 ){ recoverLostAndFound2Init(p); } if( SQLITE_DONE==recoverLostAndFound2Step(p) ){ p->eState = RECOVER_STATE_LOSTANDFOUND3; } break; } case RECOVER_STATE_LOSTANDFOUND3: { if( p->laf.pInsert==0 ){ recoverLostAndFound3Init(p); } if( SQLITE_DONE==recoverLostAndFound3Step(p) ){ p->eState = RECOVER_STATE_SCHEMA2; } break; } case RECOVER_STATE_SCHEMA2: { int rc = SQLITE_OK; recoverWriteSchema2(p); p->eState = RECOVER_STATE_DONE; /* If no error has occurred, commit the write transaction on the output ** database. Regardless of whether or not an error has occurred, make ** an attempt to end the read transaction on the input database. */ 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"); p->eState = RECOVER_STATE_DONE; recoverFinalCleanup(p); break; }; case RECOVER_STATE_DONE: { /* no-op */ break; }; } } /* ** 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, /* 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; int nUri = 0; int nByte = 0; if( zDb==0 ){ zDb = "main"; } nDb = recoverStrlen(zDb); nUri = recoverStrlen(zUri); nByte = sizeof(sqlite3_recover) + nDb+1 + nUri+1; pRet = (sqlite3_recover*)sqlite3_malloc(nByte); if( pRet ){ memset(pRet, 0, nByte); pRet->dbIn = db; pRet->zDb = (char*)&pRet[1]; pRet->zUri = &pRet->zDb[nDb+1]; memcpy(pRet->zDb, zDb, nDb); if( nUri>0 ) memcpy(pRet->zUri, zUri, nUri); pRet->xSql = xSql; pRet->pSqlCtx = pSqlCtx; pRet->bRecoverRowid = RECOVER_ROWID_DEFAULT; } 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, const char *zUri ){ 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, int (*xSql)(void*, const char*), void *pSqlCtx ){ return recoverInit(db, zDb, 0, xSql, pSqlCtx); } /* ** Return the handle error message, if any. */ const char *sqlite3_recover_errmsg(sqlite3_recover *p){ return (p && p->errCode!=SQLITE_NOMEM) ? p->zErrMsg : "out of memory"; } /* ** 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; if( p==0 ){ rc = SQLITE_NOMEM; }else if( p->eState!=RECOVER_STATE_INIT ){ rc = SQLITE_MISUSE; }else{ switch( op ){ case 789: sqlite3_free(p->zStateDb); p->zStateDb = recoverMPrintf(p, "%s", (char*)pArg); break; case SQLITE_RECOVER_LOST_AND_FOUND: { const char *zArg = (const char*)pArg; sqlite3_free(p->zLostAndFound); if( zArg ){ p->zLostAndFound = recoverMPrintf(p, "%s", zArg); }else{ p->zLostAndFound = 0; } break; } case SQLITE_RECOVER_FREELIST_CORRUPT: p->bFreelistCorrupt = *(int*)pArg; break; case SQLITE_RECOVER_ROWIDS: p->bRecoverRowid = *(int*)pArg; break; case SQLITE_RECOVER_SLOWINDEXES: p->bSlowIndexes = *(int*)pArg; break; default: rc = SQLITE_NOTFOUND; break; } } return rc; } /* ** Do a unit of work towards the recovery job. Return SQLITE_OK if ** no error has occurred but database recovery is not finished, SQLITE_DONE ** if database recovery has been successfully completed, or an SQLite ** error code if an error has occurred. */ int sqlite3_recover_step(sqlite3_recover *p){ if( p==0 ) return SQLITE_NOMEM; if( p->errCode==SQLITE_OK ) recoverStep(p); if( p->eState==RECOVER_STATE_DONE && p->errCode==SQLITE_OK ){ return SQLITE_DONE; } return p->errCode; } /* ** Do the configured recovery operation. Return SQLITE_OK if successful, or ** else an SQLite error code. */ int sqlite3_recover_run(sqlite3_recover *p){ while( SQLITE_OK==sqlite3_recover_step(p) ); return sqlite3_recover_errcode(p); } /* ** 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; if( p==0 ){ rc = SQLITE_NOMEM; }else{ recoverFinalCleanup(p); if( p->bCloseTransaction && sqlite3_get_autocommit(p->dbIn)==0 ){ rc = sqlite3_exec(p->dbIn, "END", 0, 0, 0); if( p->errCode==SQLITE_OK ) p->errCode = rc; } rc = p->errCode; sqlite3_free(p->zErrMsg); sqlite3_free(p->zStateDb); sqlite3_free(p->zLostAndFound); sqlite3_free(p->pPage1Cache); sqlite3_free(p); } return rc; }