From d6f2f7b1d7d288bc0cb0ff998052bb8efac40f7f Mon Sep 17 00:00:00 2001
From: MartinChoo <214582617@qq.com>
Date: Wed, 23 Jul 2025 17:41:00 +0800
Subject: [PATCH 04/12] Support meta recovery
src/sqlite3.c | 1156 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 1151 insertions(+), 5 deletions(-)
@@ -786,6 +786,7 @@ SQLITE_API int sqlite3_exec(
#define SQLITE_WARNING 28 /* Warnings from sqlite3_log() */
#define SQLITE_ROW 100 /* sqlite3_step() has another row ready */
#define SQLITE_DONE 101 /* sqlite3_step() has finished executing */
+#define SQLITE_META_RECOVERED 66 /* meta page recovered*/
/* end-of-error-codes */
/*
@@ -57472,6 +57473,26 @@ struct PagerSavepoint {
#endif
};
+#if !defined(SQLITE_OS_UNIX) && defined(SQLITE_META_DWR)
+#undef SQLITE_META_DWR
+#endif
+
+#ifdef SQLITE_META_DWR
+static int PragmaMetaDoubleWrie(sqlite3 *db, int iDb, Parse *parse, const char *zLeft, const char *zRight);
+typedef struct MetaDwrHdr MetaDwrHdr;
+static int MetaDwrWriteHeader(Pager *pPager, MetaDwrHdr *hdr);
+static int MetaDwrUpdateMetaPages(Btree *pBt);
+static void MetaDwrPagerRelease(Pager *pPager);
+static int MetaDwrOpenFile(Pager *pPager, u8 openCreate);
+static void MetaDwrCheckVacuum(BtShared *pBt);
+static int MetaDwrRecoverAndBeginTran(Btree *pBt, int wrflag, int *pSchemaVersion);
+static int MetaDwrOpenAndCheck(Btree *pBt);
+static void MetaDwrDisable(Btree *pBt);
+#define META_HEADER_CHANGED 1
+#define META_SCHEMA_CHANGED 2
+#define META_IN_RECOVERY 1
+#define META_RECOVER_SUCCESS 2
+#endif
/*
** Bits of the Pager.doNotSpill flag. See further description below.
*/
@@ -57737,6 +57758,13 @@ struct Pager {
Wal *pWal; /* Write-ahead log used by "journal_mode=wal" */
char *zWal; /* File name for write-ahead log */
#endif
+#ifdef SQLITE_META_DWR
+ u8 metaChanged;
+ sqlite3_file *metaFd;
+ MetaDwrHdr *metaHdr;
+ void *metaMapPage;
+ int (*xGetMethod)(Pager*,Pgno,DbPage**,int); /* Routine to fetch a patch */
+#endif
};
/*
@@ -58096,7 +58124,11 @@ static void setGetterMethod(Pager *pPager){
pPager->xGet = getPageMMap;
#endif /* SQLITE_MAX_MMAP_SIZE>0 */
}else{
+#ifdef SQLITE_META_DWR
+ pPager->xGet = pPager->xGetMethod ? pPager->xGetMethod : getPageNormal;
+#else
pPager->xGet = getPageNormal;
+#endif
}
}
@@ -59170,7 +59202,14 @@ static int pager_end_transaction(Pager *pPager, int hasSuper, int bCommit){
}
sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize);
}
-
+#ifdef SQLITE_META_DWR
+ if (bCommit && pPager->metaChanged != 0) {
+ sqlite3BeginBenignMalloc();
+ (void)MetaDwrWriteHeader(pPager, pPager->metaHdr);
+ sqlite3EndBenignMalloc();
+ pPager->metaChanged = 0;
+ }
+#endif
if( pagerUseWal(pPager) ){
/* Drop the WAL write-lock, if any. Also, if the connection was in
** locking_mode=exclusive mode but is no longer, drop the EXCLUSIVE
@@ -61269,6 +61308,9 @@ SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager, sqlite3 *db){
sqlite3WalClose(pPager->pWal, db, pPager->walSyncFlags, pPager->pageSize,a);
pPager->pWal = 0;
}
+#endif
+#ifdef SQLITE_META_DWR
+ MetaDwrPagerRelease(pPager);
#endif
pager_reset(pPager);
if( MEMDB ){
@@ -62112,7 +62154,11 @@ act_like_temp_file:
rc = sqlite3PcacheOpen(szPageDflt, nExtra, !memDb,
!memDb?pagerStress:0, (void *)pPager, pPager->pPCache);
}
-
+#ifdef SQLITE_META_DWR
+ if( rc==SQLITE_OK && !memDb && !readOnly){
+ (void)MetaDwrOpenFile(pPager, 0);
+ }
+#endif
/* If an error occurred above, free the Pager structure and close the file.
*/
if( rc!=SQLITE_OK ){
@@ -70005,6 +70051,10 @@ struct BtShared {
#endif
u8 *pTmpSpace; /* Temp space sufficient to hold a single cell */
int nPreformatSize; /* Size of last cell written by TransferRow() */
+#ifdef SQLITE_META_DWR
+ u32 maxMetaPage;
+ u32 metaRecoverStatus;
+#endif
};
/*
@@ -74298,7 +74348,12 @@ trans_begun:
rc = sqlite3PagerOpenSavepoint(pPager, p->db->nSavepoint);
}
}
-
+#ifdef SQLITE_META_DWR
+ if (rc == SQLITE_NOTADB || rc == SQLITE_CORRUPT) {
+ int rc1 = MetaDwrRecoverAndBeginTran(p, wrflag, pSchemaVersion);
+ rc = (rc1 == SQLITE_OK) ? SQLITE_OK : rc;
+ }
+#endif
btreeIntegrity(p);
sqlite3BtreeLeave(p);
return rc;
@@ -74687,6 +74742,9 @@ SQLITE_PRIVATE int sqlite3BtreeIncrVacuum(Btree *p){
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
put4byte(&pBt->pPage1->aData[28], pBt->nPage);
+#ifdef SQLITE_META_DWR
+ MetaDwrCheckVacuum(pBt);
+#endif
}
}else{
rc = SQLITE_DONE;
@@ -74771,6 +74829,9 @@ static int autoVacuumCommit(Btree *p){
put4byte(&pBt->pPage1->aData[28], nFin);
pBt->bDoTruncate = 1;
pBt->nPage = nFin;
+#ifdef SQLITE_META_DWR
+ MetaDwrCheckVacuum(pBt);
+#endif
}
if( rc!=SQLITE_OK ){
sqlite3PagerRollback(pPager);
@@ -74827,6 +74888,9 @@ SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zSuperJrnl){
if( pBt->bDoTruncate ){
sqlite3PagerTruncateImage(pBt->pPager, pBt->nPage);
}
+#endif
+#ifdef SQLITE_META_DWR
+ (void)MetaDwrUpdateMetaPages(p);
#endif
rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zSuperJrnl, 0);
sqlite3BtreeLeave(p);
@@ -80898,6 +80962,11 @@ SQLITE_PRIVATE int sqlite3BtreeUpdateMeta(Btree *p, int idx, u32 iMeta){
assert( iMeta==0 || iMeta==1 );
pBt->incrVacuum = (u8)iMeta;
}
+#endif
+#ifdef SQLITE_META_DWR
+ if (idx == 1 && pBt->pPager->metaFd) {
+ pBt->pPager->metaChanged = META_SCHEMA_CHANGED;
+ }
#endif
}
sqlite3BtreeLeave(p);
@@ -82544,7 +82613,12 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
sqlite3PagerTruncateImage(pDestPager, nDestTruncate);
rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 0);
}
-
+ #ifdef SQLITE_META_DWR
+ if (rc == SQLITE_OK && p->pDest->pBt->pPager->metaFd) {
+ p->pDest->pBt->pPager->metaChanged = META_SCHEMA_CHANGED;
+ (void)MetaDwrUpdateMetaPages(p->pDest);
+ }
+ #endif
/* Finish committing the transaction to the destination database. */
if( SQLITE_OK==rc
&& SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest, 0))
@@ -138957,7 +139031,12 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
#endif
/* END CODEC */
-
+#ifdef SQLITE_META_DWR
+ if(PragmaMetaDoubleWrie(db, iDb, pParse, zLeft, zRight)) {
+ /* PragmaMetaDoubleWrie executes internal */
+ goto pragma_out;
+ }
+#endif
/* Locate the pragma in the lookup table */
pPragma = pragmaLocate(zLeft);
if( pPragma==0 ){
@@ -180709,6 +180788,10 @@ SQLITE_PRIVATE const char *sqlite3ErrStr(int rc){
zErr = "no more rows available";
break;
}
+ case SQLITE_META_RECOVERED: {
+ zErr = "warning meta recover message";
+ break;
+ }
default: {
rc &= 0xff;
if( ALWAYS(rc>=0) && rc<ArraySize(aMsg) && aMsg[rc]!=0 ){
@@ -254918,7 +255001,1070 @@ export_finish:
}
/************** End file hw_codec.c *****************************************/
#endif
+#ifdef SQLITE_META_DWR
+#define META_DWR_MAX_PAGES 500
+#define META_DWR_MAGIC 0x234A86D9
+#define META_DWR_VERSION 0x00000001
+#define META_DWR_INVALID_ZONE 0x55
+#define META_DWR_HEADER_PAGE_SIZE 4096
+#define META_DWR_HEADER_DEFAULT_PAGE_CNT 8
+typedef struct ScanPages {
+ u32 pageCnt;
+ u32 pageBufSize;
+ u32 maxPageNo;
+ Pgno *pages;
+} ScanPages;
+
+typedef struct MetaDwrHdr {
+ u32 magic;
+ u32 version;
+ u32 dbSize;
+ u32 mxFrameInWal;
+ u32 freeListPageNo;
+ u32 freeListPageCnt;
+ u32 schemaCookie;
+ u32 pageSz;
+ u32 pageCnt;
+ u64 dbFileInode;
+ u32 reserved[12];
+ u32 checkSum;
+ u8 *zones;
+ Pgno *pages;
+ u32 pageBufSize;
+ u8 hdrValid;
+ u8 checkFileId;
+ u16 needSync;
+ i64 lastSyncTime;
+} MetaDwrHdr;
+
+#define META_VERIFIED_HDR_LEN (offsetof(MetaDwrHdr, zones))
+#define META_ZONES_LENGTH (META_DWR_MAX_PAGES * sizeof(u8))
+#define META_PAGE_NO_OFFSET (META_VERIFIED_HDR_LEN + META_ZONES_LENGTH)
+#define META_FILE_UPDATE_TIMES_PER_SYNC 100 // sync once for every 100 update
+#define META_FILE_SYNC_TIMEOUT_MS 30000 // 30 seconds
+
+static int MetaDwrHeaderSimpleCheck(Pager *pPager, MetaDwrHdr *hdr) {
+#if SQLITE_OS_UNIX
+ if (hdr->checkFileId) {
+ unixFile *fd = (unixFile *)pPager->fd;
+ if (fd == NULL || fd->pInode == NULL || pPager->pVfs == NULL) {
+ return SQLITE_INTERNAL;
+ }
+ if (fd->pInode->fileId.ino != hdr->dbFileInode) {
+ sqlite3_log(SQLITE_IOERR_DATA, "Ino mismatch file %llu dwr file %llu",
+ fd->pInode->fileId.ino, hdr->dbFileInode);
+ return SQLITE_IOERR_DATA;
+ }
+ }
+#endif
+ if (hdr->pageCnt > META_DWR_MAX_PAGES || hdr->version != META_DWR_VERSION ||
+ hdr->magic != META_DWR_MAGIC || hdr->checkSum != META_DWR_MAGIC) {
+ sqlite3_log(SQLITE_IOERR_DATA, "Meta dwr file check wrong pageCnt %u, version %u, magic %u, checkSum %u",
+ hdr->pageCnt, hdr->version, hdr->magic, hdr->checkSum);
+ return SQLITE_IOERR_DATA;
+ }
+ if (hdr->pageSz != pPager->pageSize) {
+ sqlite3_log(SQLITE_IOERR_DATA, "Meta dwr file check wrong pageSz %u-%u", hdr->pageSz, pPager->pageSize);
+ return SQLITE_IOERR_DATA;
+ }
+ return SQLITE_OK;
+}
+
+static int PragmaMetaDoubleWrie(sqlite3 *db, int iDb, Parse *parse, const char *zLeft, const char *zRight) {
+ Btree *pBt = db->aDb[iDb].pBt;
+ if (pBt == NULL || zLeft == NULL || sqlite3StrICmp(zLeft, "meta_double_write") != 0) {
+ return 0;
+ }
+ Pager *pPager = pBt->pBt->pPager;
+ if (pPager == NULL) {
+ sqlite3_log(SQLITE_WARNING_DUMP, "Invalid pager handle");
+ return 1;
+ }
+ if (zRight == NULL) {
+ Vdbe *v = sqlite3GetVdbe(parse);
+ sqlite3VdbeSetNumCols(v, 1);
+ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "meta_double_write", SQLITE_STATIC);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, pPager->metaFd ? "enabled" : "disabled", 0);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
+ } else if (strncmp(zRight, "enabled", 7) == 0) {
+ sqlite3_mutex_enter(db->mutex);
+ // only support enabled meta double write
+ int rc = MetaDwrOpenAndCheck(pBt);
+ if (rc != SQLITE_OK && rc != SQLITE_PERM) {
+ parse->nErr++;
+ parse->rc = rc;
+ }
+ sqlite3_mutex_leave(db->mutex);
+ } else if (strncmp(zRight, "disabled", 8) == 0) {
+ sqlite3_mutex_enter(db->mutex);
+ MetaDwrDisable(pBt);
+ sqlite3_mutex_leave(db->mutex);
+ }
+ return 1;
+}
+
+static int GetBtreePageNo(MemPage *pPage, void *args) {
+ ScanPages *pInfo = (ScanPages *)args;
+ // realloc buffer to store pages
+ u32 pageCnt = pInfo->pageCnt;
+ if (pageCnt == pInfo->pageBufSize) {
+ u32 memSz = sizeof(Pgno) * ROUND8(pageCnt + 1);
+ Pgno *pages = sqlite3Malloc(memSz);
+ if (pages == NULL) {
+ sqlite3_log(SQLITE_NOMEM, "GetPages alloc buffer go wrong %u", memSz);
+ return SQLITE_NOMEM;
+ }
+ if (pageCnt != 0) {
+ memcpy(pages, pInfo->pages, pageCnt * sizeof(Pgno));
+ }
+ sqlite3_free(pInfo->pages);
+ pInfo->pageBufSize = ROUND8(pageCnt + 1);
+ pInfo->pages = pages;
+ }
+ pInfo->pages[pageCnt] = pPage->pgno;
+ pInfo->pageCnt++;
+ if (pInfo->maxPageNo < pPage->pgno) {
+ pInfo->maxPageNo = pPage->pgno;
+ }
+ return 0;
+}
+
+typedef int (*ScanFn)(MemPage *pPage, void *args);
+static SQLITE_NOINLINE int ScanOverflowPages(
+ MemPage *pPage, /* The page that contains the Cell */
+ unsigned char *pCell, /* First byte of the Cell */
+ CellInfo *pInfo, /* Size information about the cell */
+ ScanFn fn, /* Scan pages function */
+ void *args) {
+ BtShared *pBt;
+ Pgno ovflPgno;
+ int rc;
+ int nOvfl;
+ u32 ovflPageSize;
+
+ if (pCell + pInfo->nSize > pPage->aDataEnd) {
+ /* Cell extends past end of page */
+ return SQLITE_CORRUPT_BKPT;
+ }
+ ovflPgno = get4byte(pCell + pInfo->nSize - 4);
+ pBt = pPage->pBt;
+ assert(pBt->usableSize > 4);
+ ovflPageSize = pBt->usableSize - 4;
+ nOvfl = (pInfo->nPayload - pInfo->nLocal + ovflPageSize - 1) / ovflPageSize;
+ assert(nOvfl > 0 ||
+ (CORRUPT_DB && (pInfo->nPayload + ovflPageSize) < ovflPageSize));
+ while (nOvfl > 0) {
+ nOvfl--;
+ Pgno iNext = 0;
+ MemPage *pOvfl = 0;
+ if (ovflPgno < 2 || ovflPgno > btreePagecount(pBt)) {
+ sqlite3_log(SQLITE_WARNING_DUMP, "Ignore for ovfl page not as expect, pgno %u ovflPgno %u novfl %d payload %u local %u",
+ pPage->pgno, ovflPgno, nOvfl, pInfo->nPayload, pInfo->nLocal);
+ return SQLITE_MISUSE;
+ }
+ rc = getOverflowPage(pBt, ovflPgno, &pOvfl, &iNext);
+ if (rc)
+ return rc;
+ if (pOvfl) {
+ rc = fn(pOvfl, args);
+ if (rc) {
+ return rc;
+ }
+ sqlite3PagerUnref(pOvfl->pDbPage);
+ }
+ ovflPgno = iNext;
+ }
+ return SQLITE_OK;
+}
+
+static int ScanBtreePage(
+ BtShared *pBt, /* The BTree that contains the table */
+ Pgno pgno, /* Page number to clear */
+ ScanFn fn, /* Scan pages function */
+ void *args) { /* Scan pages args */
+ MemPage *pPage;
+ int rc;
+ unsigned char *pCell;
+ int i;
+ int hdr;
+ CellInfo info;
+
+ assert(sqlite3_mutex_held(pBt->mutex));
+ if (pgno > btreePagecount(pBt)) {
+ return SQLITE_OK;
+ }
+ rc = getAndInitPage(pBt, pgno, &pPage, 0);
+ if (rc) {
+ return rc;
+ }
+ rc = fn(pPage, args);
+ if (rc) {
+ goto SCAN_PAGE_OUT;
+ }
+ hdr = pPage->hdrOffset;
+ for (i = pPage->nCell - 1; i >= 0; i--) {
+ pCell = findCell(pPage, i);
+ if (!pPage->leaf) {
+ rc = ScanBtreePage(pBt, get4byte(pCell), fn, args);
+ if (rc) {
+ goto SCAN_PAGE_OUT;
+ }
+ }
+ pPage->xParseCell(pPage, pCell, &info);
+ if (info.nLocal < info.nPayload) {
+ rc = ScanOverflowPages(pPage, pCell, &info, fn, args);
+ if (rc) {
+ goto SCAN_PAGE_OUT;
+ }
+ }
+ }
+ if (!pPage->leaf) {
+ rc = ScanBtreePage(pBt, get4byte(&pPage->aData[hdr + 8]), fn, args);
+ if (rc) {
+ goto SCAN_PAGE_OUT;
+ }
+ }
+SCAN_PAGE_OUT:
+ releasePage(pPage);
+ return rc;
+}
+
+static inline int ScanMetaPages(Btree *pBt, ScanPages *pages) {
+ return ScanBtreePage(pBt->pBt, 1, GetBtreePageNo, pages);
+}
+
+static int ReleaseMetaPages(ScanPages *pages) {
+ sqlite3_free(pages->pages);
+ pages->pages = NULL;
+ return SQLITE_OK;
+}
+
+static void InitMetaHeader(MetaDwrHdr *hdr) {
+ (void)memset(hdr, 0, META_VERIFIED_HDR_LEN);
+ hdr->magic = META_DWR_MAGIC;
+ hdr->version = META_DWR_VERSION;
+ hdr->checkSum = META_DWR_MAGIC;
+}
+
+static void MetaDwrReleaseHdr(MetaDwrHdr *hdr) {
+ if (!hdr) {
+ return;
+ }
+ sqlite3_free(hdr->zones);
+ sqlite3_free(hdr);
+}
+
+static int ExpandMetaPageBuf(MetaDwrHdr *hdr, u32 minimalPageCnt, u32 bufHasData) {
+ if (minimalPageCnt < hdr->pageBufSize && hdr->zones != NULL) {
+ return SQLITE_OK;
+ }
+ int pageBufSz = ROUND8(MAX(hdr->pageCnt, minimalPageCnt));
+ u8 *zones = (u8 *)sqlite3Malloc(pageBufSz * (sizeof(u8) + sizeof(Pgno)));
+ if (zones == NULL) {
+ return SQLITE_NOMEM_BKPT;
+ }
+ Pgno *pgnos = (Pgno *)(zones + pageBufSz);
+ if (hdr->zones != NULL) {
+ if (bufHasData && hdr->pageCnt > 0) {
+ (void)memcpy(zones, hdr->zones, hdr->pageCnt * sizeof(u8));
+ (void)memcpy(pgnos, hdr->pages, hdr->pageCnt * sizeof(Pgno));
+ }
+ sqlite3_free(hdr->zones);
+ }
+ hdr->pageBufSize = pageBufSz;
+ hdr->zones = zones;
+ hdr->pages = pgnos;
+ return SQLITE_OK;
+}
+
+static MetaDwrHdr *AllocInitMetaHeaderDwr(Pager *pPager) {
+ MetaDwrHdr *hdr = sqlite3MallocZero(sizeof(MetaDwrHdr));
+ if (hdr == NULL) {
+ return NULL;
+ }
+ InitMetaHeader(hdr);
+ int rc = ExpandMetaPageBuf(hdr, META_DWR_HEADER_DEFAULT_PAGE_CNT, 0);
+ if (rc != SQLITE_OK) {
+ MetaDwrReleaseHdr(hdr);
+ return NULL;
+ }
+ hdr->checkFileId = (pPager->pVfs != NULL && sqlite3_stricmp(pPager->pVfs->zName, "unix") == 0);
+ return hdr;
+}
+
+static void MetaDwrCloseFile(Pager *pPager) {
+ if (!pPager->metaFd) {
+ return;
+ }
+#if SQLITE_OS_UNIX
+ if (pPager->metaMapPage) {
+ osMunmap(pPager->metaMapPage, META_DWR_HEADER_PAGE_SIZE);
+ pPager->metaMapPage = NULL;
+ }
+#endif
+ if (pPager->metaHdr && pPager->metaHdr->needSync > 0) {
+ (void)sqlite3OsSync(pPager->metaFd, SQLITE_SYNC_NORMAL);
+ }
+ sqlite3OsClose(pPager->metaFd);
+}
+
+static void MetaDwrPagerRelease(Pager *pPager) {
+ MetaDwrCloseFile(pPager);
+ MetaDwrReleaseHdr(pPager->metaHdr);
+ pPager->metaHdr = NULL;
+ if (pPager->metaFd) {
+ sqlite3_free(pPager->metaFd);
+ pPager->metaFd = NULL;
+ }
+}
+
+static inline int ReadFromHdrPage(Pager *pPager, void *data, int amt, i64 offset) {
+ if (pPager->metaMapPage) {
+ (void)memcpy(data, (u8 *)pPager->metaMapPage + offset, amt);
+ return SQLITE_OK;
+ }
+ return sqlite3OsRead(pPager->metaFd, data, amt, offset);
+}
+
+static int MetaDwrReadHeader(Pager *pPager, MetaDwrHdr *hdr) {
+ i64 sz = 0;
+ int rc = sqlite3OsFileSize(pPager->metaFd, &sz);
+ if (rc != SQLITE_OK) {
+ sqlite3_log(rc, "Meta dwr file size go wrong");
+ return rc;
+ }
+ if (sz <= META_DWR_HEADER_PAGE_SIZE) {
+ rc = SQLITE_IOERR_DATA;
+ goto READ_META_OUT;
+ }
+ rc = ReadFromHdrPage(pPager, hdr, META_VERIFIED_HDR_LEN, 0);
+ if (rc != SQLITE_OK) {
+ sqlite3_log(rc, "Meta dwr file header read wrong");
+ goto READ_META_OUT;
+ }
+ rc = MetaDwrHeaderSimpleCheck(pPager, hdr);
+ if (rc != SQLITE_OK) {
+ goto READ_META_OUT;
+ }
+ // avoid realloc buffer if buf can't hold all pages
+ rc = ExpandMetaPageBuf(hdr, hdr->pageCnt, 0);
+ if (rc != SQLITE_OK) {
+ goto READ_META_OUT;
+ }
+ int zoneSize = hdr->pageCnt * sizeof(u8);
+ rc = ReadFromHdrPage(pPager, hdr->zones, zoneSize, META_VERIFIED_HDR_LEN);
+ if (rc != SQLITE_OK) {
+ goto READ_META_OUT;
+ }
+ rc = ReadFromHdrPage(pPager, hdr->pages, hdr->pageCnt * sizeof(Pgno), META_PAGE_NO_OFFSET);
+ if (rc != SQLITE_OK) {
+ goto READ_META_OUT;
+ }
+ for (u32 i = 0; i < hdr->pageCnt; i++) {
+ u8 zoneIdx = hdr->zones[i];
+ if (zoneIdx != 0 && zoneIdx != 1 && zoneIdx != META_DWR_INVALID_ZONE) {
+ sqlite3_log(SQLITE_IOERR_DATA, "Invalid zoneIdx %d", zoneIdx);
+ rc = SQLITE_IOERR_DATA;
+ break;
+ }
+ }
+READ_META_OUT:
+ if (rc == SQLITE_IOERR_DATA) {
+ InitMetaHeader(hdr);
+ rc = SQLITE_OK;
+ }
+ return rc;
+}
+
+static inline u64 CaculateMetaDwrWriteOffset(int pageSz, u32 idx, u8 zone) {
+ // 1 header page, idx correspond 2 zone pages
+ return META_DWR_HEADER_PAGE_SIZE + pageSz * (idx * 2 + zone);
+}
+
+static void MetaDwrUpdateHeaderDbInfo(BtShared *pBt) {
+ MetaDwrHdr *hdr = pBt->pPager->metaHdr;
+ // 28 offset: dbSize, freelist pageNo, freelist pages count, schema cookie
+ const u8 *dbHdrInfo = &pBt->pPage1->aData[28];
+ hdr->dbSize = sqlite3Get4byte(dbHdrInfo);
+#ifndef SQLITE_OMIT_WAL
+ if (pagerUseWal(pBt->pPager)) {
+ WalIndexHdr *pWalHdr = &pBt->pPager->pWal->hdr;
+ if (pWalHdr->isInit) {
+ hdr->mxFrameInWal = pWalHdr->mxFrame;
+ hdr->dbSize = pWalHdr->nPage;
+ }
+ } else {
+ hdr->mxFrameInWal = 0;
+ }
+#endif
+#if SQLITE_OS_UNIX
+ if (hdr->checkFileId) {
+ unixFile *fd = (unixFile *)pBt->pPager->fd;
+ if (fd == NULL || fd->pInode == NULL) {
+ sqlite3_log(SQLITE_WARNING_DUMP, "update meta header invalid fd");
+ hdr->hdrValid = 0;
+ return;
+ }
+ hdr->dbFileInode = fd->pInode->fileId.ino;
+ }
+#endif
+ hdr->freeListPageNo = sqlite3Get4byte(dbHdrInfo + 4);
+ hdr->freeListPageCnt = sqlite3Get4byte(dbHdrInfo + 8);
+ hdr->schemaCookie = sqlite3Get4byte(dbHdrInfo + 12);
+ hdr->hdrValid = 1;
+}
+
+static inline int WriteToHdrPage(Pager *pPager, const void *data, int amt, i64 offset) {
+ if (pPager->metaMapPage) {
+ (void)memcpy((u8 *)pPager->metaMapPage + offset, data, amt);
+ return SQLITE_OK;
+ }
+ return sqlite3OsWrite(pPager->metaFd, data, amt, offset);
+}
+
+static int MetaDwrWriteHeader(Pager *pPager, MetaDwrHdr *hdr) {
+ if (pPager->metaChanged == 0 || hdr == NULL || hdr->pageCnt == 0 || hdr->hdrValid == 0) {
+ return SQLITE_OK;
+ }
+ hdr->hdrValid = 0;
+ hdr->pageSz = pPager->pageSize;
+ hdr->dbSize = pPager->dbSize;
+ int rc = WriteToHdrPage(pPager, hdr, META_VERIFIED_HDR_LEN, 0);
+ if (rc != SQLITE_OK) {
+ sqlite3_log(rc, "update meta header write hdr %u wrong", META_VERIFIED_HDR_LEN);
+ return rc;
+ }
+ if (hdr->zones) {
+ int zoneSize = hdr->pageCnt * sizeof(u8);
+ rc = WriteToHdrPage(pPager, hdr->zones, zoneSize, META_VERIFIED_HDR_LEN);
+ if (rc != SQLITE_OK) {
+ sqlite3_log(rc, "update meta header write zonebuf %u wrong", zoneSize);
+ return rc;
+ }
+ }
+ if (hdr->pages) {
+ int pageBufSz = hdr->pageCnt * sizeof(Pgno);
+ rc = WriteToHdrPage(pPager, hdr->pages, pageBufSz, META_PAGE_NO_OFFSET);
+ if (rc != SQLITE_OK) {
+ sqlite3_log(rc, "update meta header write pagebuf %u wrong", pageBufSz);
+ }
+ }
+ if (rc == SQLITE_OK) {
+ u64 size = CaculateMetaDwrWriteOffset((int)pPager->pageSize, hdr->pageCnt, 0);
+ rc = sqlite3OsTruncate(pPager->metaFd, size);
+ if (rc != SQLITE_OK) {
+ sqlite3_log(rc, "update meta header truncate filesz %lu wrong", size);
+ }
+ i64 timeMs = 0;
+ sqlite3OsCurrentTimeInt64(pPager->pVfs, &timeMs);
+ if ((timeMs - hdr->lastSyncTime) > META_FILE_SYNC_TIMEOUT_MS ||
+ hdr->needSync >= META_FILE_UPDATE_TIMES_PER_SYNC) {
+ rc = sqlite3OsSync(pPager->metaFd, SQLITE_SYNC_NORMAL);
+ if (rc != SQLITE_OK) {
+ sqlite3_log(rc, "update meta header sync filesz %lu wrong", size);
+ }
+ hdr->lastSyncTime = timeMs;
+ hdr->needSync = 0;
+ } else {
+ hdr->needSync++;
+ }
+ }
+ return rc;
+}
+
+static int MetaDwrFindPageIdx(MetaDwrHdr *hdr, u32 pgno, u32 *idx) {
+ for (u32 i = 0; i < hdr->pageCnt && i < META_DWR_MAX_PAGES; i++) {
+ if (pgno == hdr->pages[i]) {
+ *idx = i;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int MetaDwrWriteOnePage(Btree *pBt, PgHdr *pPage, MetaDwrHdr *hdr, u8 curZones, u32 idx) {
+ int rc = SQLITE_OK;
+ u8 pageExpand = 0;
+ if (hdr->pageCnt <= idx) {
+ rc = ExpandMetaPageBuf(hdr, idx + 1, 1);
+ if (rc != SQLITE_OK) {
+ return rc;
+ }
+ pageExpand = 1;
+ }
+ Pager *pPager = pBt->pBt->pPager;
+ // asume zone 0 or 1
+ u8 zone = 1 - curZones;
+ int pageSz = sqlite3BtreeGetPageSize(pBt);
+ u64 ofs = CaculateMetaDwrWriteOffset(pageSz, idx, zone);
+ void *pData;
+#if defined(SQLITE_HAS_CODEC)
+ if ((pData = sqlite3PagerCodec(pPage)) == 0)
+ return SQLITE_NOMEM;
+#else
+ pData = pPage->pData;
+#endif
+ rc = sqlite3OsWrite(pPager->metaFd, pData, pageSz, ofs);
+ if (rc != SQLITE_OK) {
+ return rc;
+ }
+ hdr->zones[idx] = zone;
+ hdr->pages[idx] = pPage->pgno;
+ if (pageExpand) {
+ hdr->pageCnt++;
+ }
+ return SQLITE_OK;
+}
+
+static int MetaDwrRestoreAllPages(Btree *pBt, const ScanPages *metaPages, MetaDwrHdr *hdr) {
+ u32 i = 0;
+ PgHdr *p = NULL;
+ int rc = SQLITE_OK;
+ for (i = 0; i < metaPages->pageCnt && i < META_DWR_MAX_PAGES; i++) {
+ Pgno pgno = metaPages->pages[i];
+ if (pgno > btreePagecount(pBt->pBt)) {
+ sqlite3_log(SQLITE_WARNING_DUMP, "pageno %d overlimit", pgno);
+ return SQLITE_CORRUPT_BKPT;
+ }
+ rc = sqlite3PagerGet(pBt->pBt->pPager, pgno, &p, 0);
+ if (rc) {
+ return rc;
+ }
+ rc = MetaDwrWriteOnePage(pBt, p, hdr, 1, i);
+ sqlite3PagerUnref(p);
+ if (rc) {
+ return rc;
+ }
+ }
+ hdr->pageCnt = metaPages->pageCnt;
+ MetaDwrUpdateHeaderDbInfo(pBt->pBt);
+ return rc;
+}
+
+static inline const char *GetMetaFilePath(Pager *pPager)
+{
+ return pPager->metaFd == NULL ? NULL : ((const char *)pPager->metaFd + ROUND8(pPager->pVfs->szOsFile));
+}
+
+static int MetaDwrCheckPerm(sqlite3_vfs *pVfs, u8 openCreate, char *metaPath) {
+ int exists = 0;
+ int rc = sqlite3OsAccess(pVfs, metaPath, SQLITE_ACCESS_EXISTS, &exists);
+ if (rc != SQLITE_OK) {
+ return rc;
+ }
+ if (!exists && !openCreate) {
+ return SQLITE_PERM;
+ }
+#ifdef OS_FEATURE
+ // check if the path have enough permission
+ rc = osAccess(metaPath, W_OK|R_OK);
+ if (rc == 0 || errno == ENOENT) {
+ return SQLITE_OK;
+ }
+ rc = SQLITE_PERM;
+ if (openCreate) {
+ sqlite3_log(SQLITE_WARNING_DUMP, "Meta double write disabled, sysno %d", errno);
+ }
+#endif
+ return rc;
+}
+
+static int MetaDwrOpenFile(Pager *pPager, u8 openCreate) {
+ if (pPager->metaFd || pPager->zFilename == NULL) {
+ return SQLITE_OK;
+ }
+ sqlite3BeginBenignMalloc();
+ sqlite3_vfs *pVfs = pPager->pVfs;
+ int size = strlen(pPager->zFilename) + sizeof("-dwr");
+ int szOsFile = ROUND8(pVfs->szOsFile);
+ sqlite3_file *metaFd = (sqlite3_file *)sqlite3MallocZero(szOsFile + size);
+ char *metaPath = (char *)metaFd + szOsFile;
+ if (metaFd == NULL) {
+ sqlite3EndBenignMalloc();
+ sqlite3_log(SQLITE_NOMEM_BKPT, "sqlite alloc memsize %d go wrong", szOsFile + size);
+ return SQLITE_NOMEM_BKPT;
+ }
+ sqlite3_snprintf(size, metaPath, "%s-dwr", pPager->zFilename);
+ int rc = MetaDwrCheckPerm(pVfs, openCreate, metaPath);
+ if (rc != SQLITE_OK) {
+ goto INIT_META_OUT;
+ }
+ u32 flags = (SQLITE_OPEN_READWRITE | SQLITE_OPEN_SUPER_JOURNAL);
+ if (openCreate) {
+ flags |= SQLITE_OPEN_CREATE;
+ }
+ rc = sqlite3OsOpen(pVfs, metaPath, metaFd, (int)flags, 0);
+ if (rc != SQLITE_OK) {
+ goto INIT_META_OUT;
+ }
+#if SQLITE_OS_UNIX
+ if (pPager->metaMapPage == NULL) {
+ sqlite3_int64 sz = META_DWR_HEADER_PAGE_SIZE;
+ sqlite3OsFileControlHint(metaFd, SQLITE_FCNTL_CHUNK_SIZE, &sz);
+ sqlite3OsFileControlHint(metaFd, SQLITE_FCNTL_SIZE_HINT, &sz);
+ void *page = osMmap(0, META_DWR_HEADER_PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
+ ((unixFile *)metaFd)->h, 0);
+ if (page != MAP_FAILED) {
+ pPager->metaMapPage = page;
+ }
+ }
+#endif
+ pPager->metaFd = metaFd;
+INIT_META_OUT:
+ sqlite3EndBenignMalloc();
+ if (rc != SQLITE_OK && metaFd != NULL) {
+ sqlite3_free(metaFd);
+ }
+ return rc;
+}
+
+void MetaDwrCheckVacuum(BtShared *pBt) {
+ if (!pBt || !pBt->pPager->metaFd) {
+ return;
+ }
+ if (pBt->nPage < pBt->maxMetaPage) {
+ pBt->pPager->metaChanged = META_SCHEMA_CHANGED;
+ }
+}
+
+static inline u8 LocalMetaHdrValid(Pager *pPager) {
+ return pPager->metaMapPage != NULL && memcmp(pPager->metaMapPage, pPager->metaHdr,
+ META_VERIFIED_HDR_LEN) == 0;
+}
+
+static int MetaDwrLoadHdr(Pager *pPager) {
+ if (!pPager->metaHdr) {
+ pPager->metaHdr = AllocInitMetaHeaderDwr(pPager);
+ if (pPager->metaHdr == NULL) {
+ return SQLITE_NOMEM_BKPT;
+ }
+ }
+ if (LocalMetaHdrValid(pPager)) {
+ return SQLITE_OK;
+ }
+ return MetaDwrReadHeader(pPager, pPager->metaHdr);
+}
+
+static int MetaDwrLoadAndCheckMetaFile(BtShared *pBt, u8 reportErr) {
+ int rc = MetaDwrLoadHdr(pBt->pPager);
+ if (rc != SQLITE_OK) {
+ return rc;
+ }
+ MetaDwrHdr *hdr = pBt->pPager->metaHdr;
+ if (hdr->pageCnt == 0) {
+ return reportErr ? SQLITE_IOERR_DATA : SQLITE_OK;
+ }
+ // 28 offset: dbSize, freelist pageNo, freelist pages count, schema cookie
+ u8 *dbHdrInfo = &pBt->pPage1->aData[28];
+ if (hdr->dbSize != pBt->pPager->dbSize || hdr->dbSize != sqlite3Get4byte(dbHdrInfo) ||
+ hdr->freeListPageNo != sqlite3Get4byte(dbHdrInfo + 4) ||
+ hdr->freeListPageCnt != sqlite3Get4byte(dbHdrInfo + 8) ||
+ hdr->schemaCookie != sqlite3Get4byte(dbHdrInfo + 12)) {
+ sqlite3_log(SQLITE_IOERR_DATA, "Meta dwr file expect %u-%u-%u-%u-%u but gotton %u-%u-%u-%u-%u",
+ pBt->pPager->dbSize, hdr->dbSize, sqlite3Get4byte(dbHdrInfo), sqlite3Get4byte(dbHdrInfo + 4),
+ sqlite3Get4byte(dbHdrInfo + 8), sqlite3Get4byte(dbHdrInfo + 12), hdr->dbSize, hdr->freeListPageNo,
+ hdr->freeListPageCnt, hdr->schemaCookie);
+ // reinit
+ InitMetaHeader(hdr);
+ if (reportErr) {
+ return SQLITE_IOERR_DATA;
+ }
+ }
+ return SQLITE_OK;
+}
+
+static int MetaDwrReadOnePage(Pager *pPager, MetaDwrHdr *hdr, int idx, u8 *pData) {
+ u64 ofs = CaculateMetaDwrWriteOffset(pPager->pageSize, idx, hdr->zones[idx]);
+ int rc = sqlite3OsRead(pPager->metaFd, pData, pPager->pageSize, ofs);
+ CODEC1(pPager, pData, hdr->pages[idx], 3, rc = SQLITE_NOMEM_BKPT);
+ return rc;
+}
+
+static int MetaDwrRecoverHeadPage(
+ Pager *pPager, /* The pager open on the database file */
+ Pgno pgno, /* Page number to fetch */
+ DbPage **pDbPage,
+ int flag) {
+ if (pPager->metaFd == NULL) {
+ return pgno == 1 ? SQLITE_NOTADB : SQLITE_CORRUPT;
+ }
+ sqlite3_pcache_page *pCachePage = sqlite3PcacheFetch(pPager->pPCache, pgno, 3);
+ if (pCachePage == NULL) {
+ sqlite3_log(SQLITE_NOMEM_BKPT, "Get meta page wrong %d", pgno);
+ return SQLITE_NOMEM_BKPT;
+ }
+ DbPage *pPage = sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pCachePage);
+ pPage->pPager = pPager;
+ assert(pCachePage != 0);
+ int rc = MetaDwrLoadHdr(pPager);
+ if (rc != SQLITE_OK) {
+ goto RELEASE_OUT;
+ }
+ MetaDwrHdr *hdr = pPager->metaHdr;
+ u8 walChecked = 0;
+#ifndef SQLITE_OMIT_WAL
+ if (pagerUseWal(pPager)) {
+ WalIndexHdr *pWalHdr = &pPager->pWal->hdr;
+ if (pWalHdr->isInit && pWalHdr->mxFrame != 0) {
+ if (hdr->mxFrameInWal != pWalHdr->mxFrame || hdr->dbSize != pWalHdr->nPage) {
+ rc = SQLITE_NOTADB;
+ sqlite3_log(SQLITE_WARNING_DUMP, "Meta dwr wal hdr expect %u-%u but gotten %u-%u",
+ hdr->mxFrameInWal, hdr->dbSize, pWalHdr->mxFrame, pWalHdr->nPage);
+ goto RELEASE_OUT;
+ } else {
+ walChecked = 1;
+ }
+ }
+ }
+ if (walChecked == 0) {
+ i64 size = 0;
+ rc = sqlite3OsFileSize(pPager->fd, &size);
+ if (rc != SQLITE_OK) {
+ rc = SQLITE_NOTADB;
+ sqlite3_log(SQLITE_WARNING_DUMP, "Meta dwr get db file size go wrong");
+ goto RELEASE_OUT;
+ }
+ i64 expectSz = (i64)hdr->dbSize * (i64)hdr->pageSz;
+ if (size != expectSz) {
+ rc = SQLITE_NOTADB;
+ sqlite3_log(SQLITE_WARNING_DUMP, "Meta dwr expect file size %lu but gotten size %llu",
+ expectSz, size);
+ goto RELEASE_OUT;
+ }
+ }
+#endif
+ rc = SQLITE_NOTADB;
+ for (u32 i = 0; i < hdr->pageCnt; i++) {
+ if (hdr->pages[i] != pgno) {
+ continue;
+ }
+ rc = MetaDwrReadOnePage(pPager, hdr, i, sqlite3PagerGetData(pPage));
+ if (rc == SQLITE_OK) {
+ *pDbPage = pPage;
+ if (pPage->pgno == 1) {
+ memcpy(&pPager->dbFileVers, &((u8 *)pPage->pData)[24], sizeof(pPager->dbFileVers));
+ }
+ pager_set_pagehash(pPage);
+ }
+ break;
+ }
+RELEASE_OUT:
+ if (rc != SQLITE_OK && pPage != NULL) {
+ sqlite3PcacheDrop(pPage);
+ }
+ return rc;
+}
+
+static int MetaDwrRestoreChangedPages(Btree *pBt) {
+ Pager *pPager = pBt->pBt->pPager;
+ MetaDwrHdr *hdr = pPager->metaHdr;
+ u8 *zones = sqlite3MallocZero(hdr->pageBufSize * sizeof(u8));
+ if (zones == NULL) {
+ sqlite3_log(SQLITE_NOMEM_BKPT, "Alloc zones buffer size %u go wrong", hdr->pageBufSize * sizeof(u8));
+ return SQLITE_NOMEM_BKPT;
+ }
+ if (hdr->pageCnt > 0) {
+ memcpy(zones, hdr->zones, hdr->pageCnt * sizeof(u8));
+ }
+ u32 idx = 0;
+ PgHdr *p = 0;
+ PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache);
+ int rc = SQLITE_OK;
+ for (p = pList; p; p = p->pDirty) {
+ if (MetaDwrFindPageIdx(hdr, p->pgno, &idx) == 0) {
+ continue;
+ }
+ rc = MetaDwrWriteOnePage(pBt, p, hdr, zones[idx], idx);
+ if (rc != SQLITE_OK) {
+ break;
+ }
+ }
+ if (rc == SQLITE_OK) {
+ MetaDwrUpdateHeaderDbInfo(pBt->pBt);
+ }
+ sqlite3_free(zones);
+ return rc;
+}
+
+static int MetaDwrUpdateMetaPages(Btree *pBt) {
+ Pager *pPager = pBt->pBt->pPager;
+ if (!pPager || !pPager->metaFd || pPager->memDb || pPager->readOnly || pBt->pBt->pPage1 == NULL) {
+ return SQLITE_OK;
+ }
+ if (pPager->metaChanged == 0) {
+ if ((pBt->pBt->pPage1->pDbPage->flags & PGHDR_DIRTY) == 0) {
+ return SQLITE_OK;
+ }
+ pPager->metaChanged = META_HEADER_CHANGED;
+ }
+ sqlite3BeginBenignMalloc();
+ int rc = MetaDwrLoadHdr(pPager);
+ if (rc != SQLITE_OK) {
+ goto UPDATE_OUT;
+ }
+ // only update header page
+ if (pPager->metaChanged == META_HEADER_CHANGED) {
+ rc = MetaDwrRestoreChangedPages(pBt);
+ goto UPDATE_OUT;
+ }
+ // update schema pages
+ ScanPages metaPages = {0};
+ rc = ScanMetaPages(pBt, &metaPages);
+ if (rc != SQLITE_OK) {
+ goto UPDATE_OUT;
+ }
+ MetaDwrHdr *hdr = pPager->metaHdr;
+ // rewrite
+ if (hdr->pageCnt == 0 || hdr->pageCnt != metaPages.pageCnt || pBt->pBt->nPage > pBt->pBt->maxMetaPage ||
+ memcmp(hdr->pages, metaPages.pages, hdr->pageCnt * sizeof(Pgno))) {
+ // if page numbers unorderred, restore all pages
+ rc = MetaDwrRestoreAllPages(pBt, &metaPages, hdr);
+ } else {
+ rc = MetaDwrRestoreChangedPages(pBt);
+ }
+ if (rc == SQLITE_OK) {
+ pBt->pBt->maxMetaPage = metaPages.maxPageNo;
+ }
+ ReleaseMetaPages(&metaPages);
+UPDATE_OUT:
+ sqlite3EndBenignMalloc();
+ return rc;
+}
+
+static int MetaDwrRecoverSinglePage(Btree *pBt, int pgno, u8 *pData) {
+ if (pgno < 1 || pBt == NULL) {
+ return SQLITE_CORRUPT_BKPT;
+ }
+ Pager *pPager = sqlite3BtreePager(pBt);
+ DbPage *pDbPage = NULL;
+ int rc = sqlite3PagerGet(pPager, pgno, &pDbPage, 0);
+ if (rc) {
+ return rc;
+ }
+ if ((rc = sqlite3PagerWrite(pDbPage)) == SQLITE_OK) {
+ memcpy(sqlite3PagerGetData(pDbPage), pData, pPager->pageSize);
+ } else {
+ sqlite3_log(rc, "Dwr recoverwrite meta page %d failed", pgno);
+ }
+ sqlite3PagerUnref(pDbPage);
+ return rc;
+}
+
+static int MetaDwrCheckMeta(Btree *pBt) {
+ int nErr = 0;
+ Pgno aRoot[2] = {0, 1}; // quick check and only check root btree
+ char *errStr = NULL;
+ int rc = sqlite3BtreeIntegrityCheck(pBt->db, pBt, &aRoot[0], 2, SQLITE_INTEGRITY_CHECK_ERROR_MAX,
+ &nErr, &errStr);
+ if (nErr == 0) {
+ assert(errStr == 0);
+ return SQLITE_OK;
+ }
+ if (rc != SQLITE_OK) {
+ sqlite3_log(rc, "Meta integrity check go wrong");
+ return rc;
+ }
+ sqlite3_log(SQLITE_WARNING_DUMP, "Meta integrity check %s", errStr);
+ sqlite3DbFree(pBt->db, errStr);
+ return SQLITE_CORRUPT;
+}
+
+static int MetaDwrBeginTrans(Btree *pBt, int wrflag) {
+ pBt->pBt->btsFlags &= ~BTS_READ_ONLY;
+ Pager *pPager = pBt->pBt->pPager;
+ void *xGetMethod = pPager->xGet;
+ pPager->xGetMethod = MetaDwrRecoverHeadPage;
+ pPager->xGet = MetaDwrRecoverHeadPage;
+ int rc = sqlite3BtreeBeginTrans(pBt, wrflag, 0);
+ pPager->xGet = xGetMethod;
+ pPager->xGetMethod = 0;
+ if (rc == SQLITE_OK) {
+ sqlite3PagerWrite(pBt->pBt->pPage1->pDbPage);
+ sqlite3_log(rc, "sqlite fix meta header");
+ }
+ return rc;
+}
+
+static int MetaDwrRecoverAndBeginTran(Btree *pBt, int wrflag, int *pSchemaVersion)
+{
+ Pager *pPager = pBt->pBt->pPager;
+ assert(sqlite3_mutex_held(pBt->pBt->mutex));
+ if (!pPager->metaFd || pBt->pBt->metaRecoverStatus || pPager->readOnly || pPager->memDb) {
+ return SQLITE_NOTADB;
+ }
+ int rc = MetaDwrLoadHdr(pPager);
+ if (rc != SQLITE_OK) {
+ sqlite3_log(rc, "MetaDwr load header failed");
+ return rc;
+ }
+ pBt->pBt->metaRecoverStatus = META_IN_RECOVERY;
+ rc = MetaDwrBeginTrans(pBt, 2);
+ if (rc != SQLITE_OK) {
+ return rc;
+ }
+ void *pData = NULL;
+ pPager->metaChanged = META_HEADER_CHANGED;
+ MetaDwrHdr *hdr = pPager->metaHdr;
+ sqlite3_log(SQLITE_WARNING_DUMP, "sqlite meta recover %u frames", hdr->pageCnt);
+ int szPage = sqlite3BtreeGetPageSize(pBt);
+ pData = sqlite3Malloc(szPage);
+ if (pData == NULL) {
+ rc = SQLITE_NOMEM;
+ sqlite3_log(rc, "Dwr malloc mem size %d failed", szPage);
+ goto DWR_RECOVER_OUT;
+ }
+ for (u32 i = 0; i < hdr->pageCnt; i++) {
+ rc = MetaDwrReadOnePage(pPager, hdr, i, pData);
+ if (rc != SQLITE_OK) {
+ sqlite3_log(rc, "Dwr read %d meta page failed ", i);
+ break;
+ }
+ rc = MetaDwrRecoverSinglePage(pBt, hdr->pages[i], pData);
+ if (rc != SQLITE_OK) {
+ sqlite3_log(rc, "Dwr recover %d meta page failed ", i);
+ break;
+ }
+ }
+DWR_RECOVER_OUT:
+ /* Close the transaction, if one was opened. */
+ if (rc == SQLITE_OK) {
+ sqlite3BtreeCommit(pBt);
+ } else {
+ (void)sqlite3BtreeRollback(pBt, SQLITE_ABORT_ROLLBACK, 0);
+ }
+ if (rc == SQLITE_OK) {
+ rc = sqlite3BtreeBeginTrans(pBt, wrflag, pSchemaVersion);
+ }
+ if (rc == SQLITE_OK) {
+ pBt->pBt->metaRecoverStatus = META_RECOVER_SUCCESS;
+ }
+ if (pData) {
+ sqlite3_free(pData);
+ }
+ return rc;
+}
+
+static int Sqlite3MetaDwrCheckRestore(Btree *pBt) {
+ Pager *pPager = pBt->pBt->pPager;
+ int rc = MetaDwrOpenFile(pPager, 1);
+ if (rc != SQLITE_OK) {
+ return rc;
+ }
+ ScanPages metaPages = {0};
+ rc = ScanMetaPages(pBt, &metaPages);
+ if (rc != SQLITE_OK || metaPages.pageCnt == 0) {
+ goto CHK_RESTORE_OUT;
+ }
+ rc = MetaDwrLoadAndCheckMetaFile(pBt->pBt, 0);
+ if (rc != SQLITE_OK) {
+ goto CHK_RESTORE_OUT;
+ }
+ MetaDwrHdr *hdr = pPager->metaHdr;
+ if (hdr->pageCnt == 0 || hdr->pageCnt != metaPages.pageCnt ||
+ memcmp(hdr->pages, metaPages.pages, hdr->pageCnt * sizeof(Pgno))) {
+ sqlite3_log(SQLITE_WARNING_DUMP, "sqlite meta restore all");
+ rc = MetaDwrRestoreAllPages(pBt, &metaPages, hdr);
+ if (rc == SQLITE_OK) {
+ pPager->metaChanged = META_SCHEMA_CHANGED;
+ rc = MetaDwrWriteHeader(pPager, hdr);
+ pPager->metaChanged = 0;
+ }
+ }
+ if (rc == SQLITE_OK) {
+ pBt->pBt->maxMetaPage = metaPages.maxPageNo;
+ }
+CHK_RESTORE_OUT:
+ ReleaseMetaPages(&metaPages);
+ return rc;
+}
+
+static inline u8 IsConnectionValidForCheck(Pager *pPager)
+{
+#if SQLITE_OS_UNIX
+ unixFile *fd = (unixFile *)pPager->fd;
+ // unix and only one connection exist
+ if (fd == NULL || fd->pInode == NULL || pPager->pVfs == NULL ||
+ sqlite3_stricmp(pPager->pVfs->zName, "unix") != 0 || fd->pInode->nRef != 1) {
+ return 0;
+ }
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+static int MetaDwrOpenAndCheck(Btree *pBt)
+{
+ Pager *pPager = pBt->pBt->pPager;
+ if (pPager->memDb || pPager->readOnly || !IsConnectionValidForCheck(pPager)) {
+ return SQLITE_OK;
+ }
+#ifdef SQLITE_HAS_CODEC
+ // not support codec right now
+ if (pPager->xCodec) {
+ return SQLITE_OK;
+ }
+#endif
+ sqlite3BtreeEnter(pBt);
+ int rc = SQLITE_OK;
+ int openedTransaction = 0;
+ int tnxState = sqlite3BtreeTxnState(pBt);
+ if (tnxState == SQLITE_TXN_NONE) {
+ rc = sqlite3BtreeBeginTrans(pBt, 0, 0);
+ if (rc != SQLITE_OK) {
+ goto DWR_OPEN_OUT;
+ }
+ openedTransaction = 1;
+ }
+ rc = MetaDwrCheckMeta(pBt);
+ if (rc == SQLITE_CORRUPT || rc == SQLITE_NOTADB) {
+ // keep txn status after recover
+ rc = MetaDwrRecoverAndBeginTran(pBt, tnxState == SQLITE_TXN_WRITE ? 1 : 0, 0);
+ goto DWR_OPEN_OUT;
+ }
+ rc = Sqlite3MetaDwrCheckRestore(pBt);
+DWR_OPEN_OUT:
+ if (rc == SQLITE_OK && pBt->pBt->metaRecoverStatus == META_RECOVER_SUCCESS) {
+ rc = MetaDwrCheckMeta(pBt);
+ if (rc == SQLITE_OK) {
+ rc = SQLITE_META_RECOVERED;
+ }
+ }
+ /* Close the transaction, if one was opened. */
+ if (openedTransaction) {
+ sqlite3BtreeCommit(pBt);
+ }
+ sqlite3BtreeLeave(pBt);
+ return rc;
+}
+static void MetaDwrDisable(Btree *pBt)
+{
+ Pager *pPager = pBt->pBt->pPager;
+ if (pPager->metaFd == NULL || pPager->memDb || pPager->readOnly || !IsConnectionValidForCheck(pPager)) {
+ return;
+ }
+#ifdef SQLITE_HAS_CODEC
+ // not support codec right now
+ if (pPager->xCodec) {
+ return;
+ }
+#endif
+ sqlite3BtreeEnter(pBt);
+ MetaDwrCloseFile(pPager);
+ MetaDwrReleaseHdr(pPager->metaHdr);
+ pPager->metaHdr = NULL;
+ const char *metaPath = GetMetaFilePath(pPager);
+ if (metaPath != NULL) {
+ (void)osUnlink(metaPath);
+ }
+ if (pPager->metaFd) {
+ sqlite3_free(pPager->metaFd);
+ pPager->metaFd = NULL;
+ }
+ sqlite3BtreeLeave(pBt);
+}
+#endif
#if SQLITE_OS_UNIX
#include <unistd.h>
#include <sys/syscall.h>
--
2.47.0.windows.2