diff --git a/BUILD.gn b/BUILD.gn index 3ae3aedb3d2bed28974d64e207082c12a855c5ef..8993c2439eb57b91b1a790b9576f37652ac19f0e 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -115,6 +115,7 @@ ohos_shared_library("sqlite") { "FDSAN_ENABLE", "HARMONY_OS", "SQLITE_ENABLE_ICU", + "SQLITE_META_DWR", ] cflags_c = [ "-fvisibility=hidden", diff --git a/src/sqlite3.c b/src/sqlite3.c index 4c27691bad16bcc3487e17d09ae46508819da57d..c7dd21e2a91ff0ca49625ca99529b32a3e5f369b 100644 --- a/src/sqlite3.c +++ b/src/sqlite3.c @@ -781,6 +781,7 @@ SQLITE_API int sqlite3_exec( #define SQLITE_NOTADB 26 /* File opened that is not a database file */ #define SQLITE_NOTICE 27 /* Notifications from sqlite3_log() */ #define SQLITE_WARNING 28 /* Warnings from sqlite3_log() */ +#define SQLITE_META_RECOVERED 66 /* meta page recovered*/ #define SQLITE_ROW 100 /* sqlite3_step() has another row ready */ #define SQLITE_DONE 101 /* sqlite3_step() has finished executing */ /* end-of-error-codes */ @@ -14992,7 +14993,6 @@ typedef int VList; # define SQLITE_OS_UNIX 0 #endif - #endif /* SQLITE_OS_SETUP_H */ /************** End of os_setup.h ********************************************/ @@ -56094,6 +56094,21 @@ struct PagerSavepoint { #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 MetaDwrDoRecover(Btree *pBt, u8 headCorrput); +static int MetaDwrOpenAndCheck(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. */ @@ -56359,6 +56374,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 }; /* @@ -56718,7 +56740,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 } } @@ -57769,7 +57795,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 @@ -59850,6 +59883,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 ){ @@ -59879,7 +59915,6 @@ SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager, sqlite3 *db){ sqlite3OsClose(pPager->fd); sqlite3PageFree(pTmp); sqlite3PcacheClose(pPager->pPCache); - #ifdef SQLITE_HAS_CODEC if( pPager->xCodecFree ) pPager->xCodecFree(pPager->pCodec); #endif @@ -60699,7 +60734,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 ){ @@ -61131,7 +61170,6 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){ if( pPager->tempFile==0 && pPager->eState==PAGER_OPEN && rc==SQLITE_OK ){ rc = pagerPagecount(pPager, &pPager->dbSize); } - failed: if( rc!=SQLITE_OK ){ assert( !MEMDB ); @@ -62411,7 +62449,6 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager *pPager){ pPager->eState = PAGER_READER; return SQLITE_OK; } - PAGERTRACE(("COMMIT %d\n", PAGERID(pPager))); rc = pager_end_transaction(pPager, pPager->setSuper, 1); return pager_error(pPager, rc); @@ -68250,6 +68287,11 @@ 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 + int metaFileChecked; + int maxMetaPage; + int metaRecoverStatus; +#endif }; /* @@ -71794,8 +71836,8 @@ SQLITE_PRIVATE int sqlite3BtreeOpen( } } #endif - *ppBtree = p; + *ppBtree = p; btree_open_out: if( rc!=SQLITE_OK ){ if( pBt && pBt->pPager ){ @@ -72760,7 +72802,14 @@ trans_begun: rc = sqlite3PagerOpenSavepoint(pPager, p->db->nSavepoint); } } - +#ifdef SQLITE_META_DWR + if (rc == SQLITE_NOTADB) { + rc = MetaDwrDoRecover(p, 1); + if (rc == SQLITE_OK) { + rc = sqlite3BtreeBeginTrans(p, wrflag, pSchemaVersion); + } + } +#endif btreeIntegrity(p); sqlite3BtreeLeave(p); return rc; @@ -73152,6 +73201,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; @@ -73236,6 +73288,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); @@ -73292,6 +73347,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); @@ -79293,6 +79351,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); @@ -79663,7 +79726,6 @@ static int checkTreePage( "unable to get the page. error code=%d", rc); goto end_of_check; } - /* Clear MemPage.isInit to make sure the corruption detection code in ** btreeInitPage() is executed. */ savedIsInit = pPage->isInit; @@ -135314,7 +135376,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 ){ @@ -149750,7 +149817,6 @@ static void updateVirtualTable( } } - if( eOnePass==ONEPASS_OFF ){ /* End the virtual table scan */ if( pSrc->nSrc==1 ){ @@ -175720,6 +175786,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) && rccheckFileId) { + 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) { + parse->nErr++; + parse->rc = rc; + } + 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--) { + Pgno iNext = 0; + MemPage *pOvfl = 0; + if (ovflPgno < 2 || ovflPgno > btreePagecount(pBt)) { + return SQLITE_CORRUPT_BKPT; + } + 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, 0); + if (rc) { + return rc; + } + rc = fn(pPage, args); + if (rc) { + goto ScanBtreePage_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 ScanBtreePage_out; + } + } + pPage->xParseCell(pPage, pCell, &info); + if (info.nLocal != info.nPayload) { + rc = ScanOverflowPages(pPage, pCell, &info, fn, args); + if (rc) { + goto ScanBtreePage_out; + } + } + } + if (!pPage->leaf) { + rc = ScanBtreePage(pBt, get4byte(&pPage->aData[hdr + 8]), fn, args); + if (rc) { + goto ScanBtreePage_out; + } + } +ScanBtreePage_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, int minimalPageCnt) { + 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 (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); + 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); + } + sqlite3OsCloseFree(pPager->metaFd); + pPager->metaFd = NULL; +} + +static void MetaDwrPagerRelease(Pager *pPager) { + MetaDwrCloseFile(pPager); + MetaDwrReleaseHdr(pPager->metaHdr); + pPager->metaHdr = 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); + 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, int pgno, int *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, int idx) { + int rc = SQLITE_OK; + u8 pageExpand = 0; + if (hdr->pageCnt <= idx) { + rc = ExpandMetaPageBuf(hdr, idx + 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; + } + } + MetaDwrUpdateHeaderDbInfo(pBt->pBt); + 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"); + sqlite3_file *metaFd = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile + size); + char *metaPath = (char *)metaFd + pVfs->szOsFile; + if (metaFd == NULL) { + sqlite3EndBenignMalloc(); + sqlite3_log(SQLITE_NOMEM_BKPT, "sqlite alloc memsize %d go wrong", size); + return SQLITE_NOMEM_BKPT; + } + sqlite3_snprintf(size, metaPath, "%s-dwr", pPager->zFilename); + int exists = 0; + int rc = sqlite3OsAccess(pVfs, metaPath, SQLITE_ACCESS_EXISTS, &exists); + if (rc != SQLITE_OK) { + goto INIT_META_OUT; + } + if (!exists && !openCreate) { + sqlite3_free(metaFd); + 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)); + } + int 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 = sqlite3BtreeIntegrityCheck(pBt->db, pBt, &aRoot[0], 2, SQLITE_INTEGRITY_CHECK_ERROR_MAX, + &nErr); + if (nErr == 0) { + assert(errStr == 0); + return SQLITE_OK; + } + if (errStr == 0) { + sqlite3_log(SQLITE_NOMEM, "Integrity check no mem"); + return SQLITE_NOMEM; + } + sqlite3_log(SQLITE_WARNING_DUMP, "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 MetaDwrDoRecover(Btree *pBt, u8 headCorrput) { + Pager *pPager = pBt->pBt->pPager; + assert(sqlite3_mutex_held(pBt->pBt->mutex)); + if (!pPager->metaFd || pBt->pBt->metaRecoverStatus || pPager->readOnly || pPager->memDb) { + return headCorrput ? SQLITE_NOTADB : SQLITE_OK; + } + pBt->pBt->metaRecoverStatus = META_IN_RECOVERY; + int rc = SQLITE_OK; + void *pData = NULL; + int openedTransaction = 0; + int txnState = sqlite3BtreeTxnState(pBt); + if (txnState <= SQLITE_TXN_READ) { + rc = MetaDwrBeginTrans(pBt, 2); + if (rc != SQLITE_OK) { + goto CLEAN_OUT; + } + openedTransaction = 1; + } else { + rc = MetaDwrLoadAndCheckMetaFile(pBt->pBt, 1); + if (rc != SQLITE_OK) { + sqlite3_log(SQLITE_WARNING_DUMP, "sqlite meta recover check failed"); + goto CLEAN_OUT; + } + } + pPager->metaChanged = META_HEADER_CHANGED; + MetaDwrHdr *hdr = pPager->metaHdr; + sqlite3_log(SQLITE_WARNING_DUMP, "sqlite meta recover %u frames", hdr->pageCnt); + // page 1 recovered in MetaDwrBeginTrans + if (hdr->pageCnt == 1) { + goto CLEAN_OUT; + } + int szPage = sqlite3BtreeGetPageSize(pBt); + pData = sqlite3Malloc(szPage); + if (pData == NULL) { + rc = SQLITE_NOMEM; + sqlite3_log(rc, "Dwr malloc mem size %d failed", szPage); + goto CLEAN_OUT; + } + for (u32 i = 1; 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; + } + } +CLEAN_OUT: + /* Close the transaction, if one was opened. */ + if (openedTransaction) { + if (rc == SQLITE_OK) { + sqlite3BtreeCommit(pBt); + } else { + (void)sqlite3BtreeRollback(pBt, SQLITE_ABORT_ROLLBACK, 0); + } + } + 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 int MetaDwrOpenAndCheck(Btree *pBt) { + Pager *pPager = pBt->pBt->pPager; + if (pPager->memDb || pPager->readOnly) { + return SQLITE_OK; + } +#ifdef SQLITE_HAS_CODEC + // not support codec right now + if (pPager->xCodec) { + return SQLITE_OK; + } +#endif + if (pPager->metaFd != NULL && pBt->pBt->metaFileChecked) { + return SQLITE_OK; + } + sqlite3BtreeEnter(pBt); + int rc = SQLITE_OK; + int openedTransaction = 0; + if (sqlite3BtreeTxnState(pBt) <= SQLITE_TXN_READ) { + rc = sqlite3BtreeBeginTrans(pBt, 2, 0); + if (rc != SQLITE_OK) { + goto CLEAN_OUT; + } + openedTransaction = 1; + } + rc = MetaDwrCheckMeta(pBt); + if (rc == SQLITE_CORRUPT || rc == SQLITE_NOTADB) { + rc = MetaDwrDoRecover(pBt, 0); + goto CLEAN_OUT; + } + rc = Sqlite3MetaDwrCheckRestore(pBt); +CLEAN_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) { + if (rc == SQLITE_OK) { + sqlite3BtreeCommit(pBt); + } else { + (void)sqlite3BtreeRollback(pBt, SQLITE_ABORT_ROLLBACK, 0); + } + } + sqlite3BtreeLeave(pBt); + pBt->pBt->metaFileChecked = 1; + return rc; +} +#endif #if SQLITE_OS_UNIX #include #include