internal static void checkPtrmap(IntegrityCk pCheck, Pgno iChild, byte eType, Pgno iParent, string zContext) { byte ePtrmapType = 0; Pgno iPtrmapParent = 0; var rc = ptrmapGet(pCheck.pBt, iChild, ref ePtrmapType, ref iPtrmapParent); if (rc != SQLITE.OK) { checkAppendMsg(pCheck, zContext, "Failed to read ptrmap key=%d", iChild); return; } if (ePtrmapType != eType || iPtrmapParent != iParent) { checkAppendMsg(pCheck, zContext, "Bad ptr map entry key=%d expected=(%d,%d) got=(%d,%d)", iChild, eType, iParent, ePtrmapType, iPtrmapParent); } }
internal static int checkRef(IntegrityCk pCheck, Pgno iPage, string zContext) { if (iPage == 0) { return(1); } if (iPage > pCheck.nPage) { checkAppendMsg(pCheck, zContext, "invalid page number %d", iPage); return(1); } if (pCheck.anRef[iPage] == 1) { checkAppendMsg(pCheck, zContext, "2nd reference to page %d", iPage); return(1); } return(((pCheck.anRef[iPage]++) > 1) ? 1 : 0); }
internal static void checkAppendMsg(IntegrityCk pCheck, StringBuilder zMsg1, string zFormat, params object[] ap) { if (pCheck.mxErr == 0) { return; } lock (lock_va_list) { pCheck.mxErr--; pCheck.nErr++; va_start(ap, zFormat); if (pCheck.errMsg.zText.Length != 0) { sqlite3StrAccumAppend(pCheck.errMsg, "\n", 1); } if (zMsg1.Length > 0) { sqlite3StrAccumAppend(pCheck.errMsg, zMsg1.ToString(), -1); } sqlite3VXPrintf(pCheck.errMsg, 1, zFormat, ap); va_end(ref ap); } }
public string IntegrityCheck(Pid[] roots, int rootsLength, int maxErrors, out int errors) { BtShared bt = Bt; Enter(); Debug.Assert(InTrans > TRANS.NONE && bt.InTransaction > TRANS.NONE); int refs = bt.Pager.get_Refs(); IntegrityCk check = new IntegrityCk(); check.Bt = bt; check.Pager = bt.Pager; check.Pages = btreePagecount(check.Bt); check.MaxErrors = maxErrors; check.Errors = 0; check.MallocFailed = false; errors = 0; if (check.Pages == 0) { Leave(); return null; } check.PgRefs = C._alloc((int)(check.Pages / 8) + 1); if (check.PgRefs == null) { errors = 1; Leave(); return null; } Pid i = PENDING_BYTE_PAGE(bt); if (i <= check.Pages) setPageReferenced(check, i); TextBuilder.Init(check.ErrMsg, 1000, 20000); // Check the integrity of the freelist checkList(check, true, (Pid)ConvertEx.Get4(bt.Page1.Data, 32), (int)ConvertEx.Get4(bt.Page1.Data, 36), "Main freelist: "); // Check all the tables. for (i = 0; (int)i < rootsLength && check.MaxErrors != 0; i++) { if (roots[i] == 0) continue; #if !OMIT_AUTOVACUUM if (bt.AutoVacuum && roots[i] > 1) checkPtrmap(check, roots[i], PTRMAP.ROOTPAGE, 0, null); #endif checkTreePage(check, roots[i], "List of tree roots: ", ref _nullRef_, false, ref _nullRef_, false); } // Make sure every page in the file is referenced for (i = 1; i <= check.Pages && check.MaxErrors != 0; i++) { #if OMIT_AUTOVACUUM if (check.PgRefs[i] == null) checkAppendMsg(check, null, "Page %d is never used", i); #else // If the database supports auto-vacuum, make sure no tables contain references to pointer-map pages. if (!getPageReferenced(check, i) && (PTRMAP_PAGENO(bt, i) != i || !bt.AutoVacuum)) checkAppendMsg(check, (string)null, "Page %d is never used", i); if (getPageReferenced(check, i) && (PTRMAP_PAGENO(bt, i) == i && bt.AutoVacuum)) checkAppendMsg(check, (string)null, "Pointer map page %d is referenced", i); #endif } // Make sure this analysis did not leave any unref() pages. This is an internal consistency check; an integrity check // of the integrity check. if (C._NEVER(refs != bt.Pager.get_Refs())) checkAppendMsg(check, (string)null, "Outstanding page count goes from %d to %d during this analysis", refs, bt.Pager.get_Refs()); // Clean up and report errors. Leave(); check.PgRefs = null; if (check.MallocFailed) { check.ErrMsg.Reset(); errors = check.Errors + 1; return null; } errors = check.Errors; if (check.Errors == 0) check.ErrMsg.Reset(); return check.ErrMsg.ToString(); }
static int checkTreePage(IntegrityCk check, Pid pageID, string parentContext, ref long parentMinKey, bool hasParentMinKey, ref long parentMaxKey, bool hasParentMaxKey) { var msg = new StringBuilder(100); msg.AppendFormat("Page {0}: ", pageID); // Check that the page exists var bt = check.Bt; var usableSize = (int)bt.UsableSize; if (pageID == 0) return 0; if (checkRef(check, pageID, parentContext)) return 0; RC rc; MemPage page = new MemPage(); if ((rc = btreeGetPage(bt, pageID, ref page, false)) != RC.OK) { checkAppendMsg(check, msg.ToString(), "unable to get the page. error code=%d", rc); return 0; } // Clear MemPage.isInit to make sure the corruption detection code in btreeInitPage() is executed. page.IsInit = false; if ((rc = btreeInitPage(page)) != RC.OK) { Debug.Assert(rc == RC.CORRUPT); // The only possible error from InitPage checkAppendMsg(check, msg.ToString(), "btreeInitPage() returns error code %d", rc); releasePage(page); return 0; } // Check out all the cells. Pid id; uint i; int depth = 0; long minKey = 0; long maxKey = 0; for (i = 0U; i < page.Cells && check.MaxErrors != 0; i++) { // Check payload overflow pages msg.AppendFormat("On tree page {0} cell {1}: ", pageID, i); uint cell_ = findCell(page, i); var info = new CellInfo(); btreeParseCellPtr(page, cell_, ref info); uint sizeCell = info.Data; if (!page.IntKey) sizeCell += (uint)info.Key; // For intKey pages, check that the keys are in order. else if (i == 0) minKey = maxKey = info.Key; else { if (info.Key <= maxKey) checkAppendMsg(check, msg.ToString(), "Rowid %lld out of order (previous was %lld)", info.Key, maxKey); maxKey = info.Key; } Debug.Assert(sizeCell == info.Payload); if (sizeCell > info.Local) //&& pCell[info.iOverflow]<=&pPage.aData[pBt.usableSize] { int pages = (int)(sizeCell - info.Local + usableSize - 5) / (usableSize - 4); Pid ovflID = ConvertEx.Get4(page.Data, cell_ + info.Overflow); #if !OMIT_AUTOVACUUM if (bt.AutoVacuum) checkPtrmap(check, ovflID, PTRMAP.OVERFLOW1, pageID, msg.ToString()); #endif checkList(check, false, ovflID, pages, msg.ToString()); } // Check sanity of left child page. if (!page.Leaf) { id = (Pid)ConvertEx.Get4(page.Data, cell_); #if !OMIT_AUTOVACUUM if (bt.AutoVacuum) checkPtrmap(check, id, PTRMAP.BTREE, pageID, msg.ToString()); #endif int depth2; if (i == 0) depth2 = checkTreePage(check, id, msg.ToString(), ref minKey, true, ref _nullRef_, false); else depth2 = checkTreePage(check, id, msg.ToString(), ref minKey, true, ref maxKey, true); if (i > 0 && depth2 != depth) checkAppendMsg(check, msg, "Child page depth differs"); depth = depth2; } } if (!page.Leaf) { id = (Pid)ConvertEx.Get4(page.Data, page.HdrOffset + 8); msg.AppendFormat("On page {0} at right child: ", pageID); #if !OMIT_AUTOVACUUM if (bt.AutoVacuum) checkPtrmap(check, id, PTRMAP.BTREE, pageID, msg.ToString()); #endif if (page.Cells == 0) checkTreePage(check, id, msg.ToString(), ref _nullRef_, false, ref _nullRef_, false); else checkTreePage(check, id, msg.ToString(), ref _nullRef_, false, ref maxKey, true); } // For intKey leaf pages, check that the min/max keys are in order with any left/parent/right pages. if (page.Leaf && page.IntKey) { // if we are a left child page if (hasParentMinKey) { // if we are the left most child page if (!hasParentMaxKey) { if (maxKey > parentMinKey) checkAppendMsg(check, msg, "Rowid %lld out of order (max larger than parent min of %lld)", maxKey, parentMinKey); } else { if (minKey <= parentMinKey) checkAppendMsg(check, msg, "Rowid %lld out of order (min less than parent min of %lld)", minKey, parentMinKey); if (maxKey > parentMaxKey) checkAppendMsg(check, msg, "Rowid %lld out of order (max larger than parent max of %lld)", maxKey, parentMaxKey); parentMinKey = maxKey; } } // else if we're a right child page else if (hasParentMaxKey) { if (minKey <= parentMaxKey) checkAppendMsg(check, msg, "Rowid %lld out of order (min less than parent max of %lld)", minKey, parentMaxKey); } } // Check for complete coverage of the page byte[] data = page.Data; uint hdr = page.HdrOffset; byte[] hit = PCache.PageAlloc2((int)bt.PageSize); if (hit == null) check.MallocFailed = true; else { uint contentOffset = ConvertEx.Get2nz(data, hdr + 5); Debug.Assert(contentOffset <= usableSize); // Enforced by btreeInitPage() Array.Clear(hit, (int)contentOffset, (int)(usableSize - contentOffset)); { for (uint z = contentOffset - 1; z >= 0; z--) hit[z] = 1; }// memset(hit, 1, contentOffset); uint cells = ConvertEx.Get2(data, hdr + 3); uint cellStart = hdr + 12 - 4 * (page.Leaf ? 1U : 0U); for (i = 0; i < cells; i++) { var sizeCell = 65536U; uint pc = ConvertEx.Get2(data, cellStart + i * 2); if (pc <= usableSize - 4) sizeCell = cellSizePtr(page, data, pc); if ((int)(pc + sizeCell - 1) >= usableSize) checkAppendMsg(check, (string)null, "Corruption detected in cell %d on page %d", i, pageID); else for (var j = (int)(pc + sizeCell - 1); j >= pc; j--) hit[j]++; } i = ConvertEx.Get2(data, hdr + 1); while (i > 0) { Debug.Assert(i <= usableSize - 4); // Enforced by btreeInitPage() uint size = ConvertEx.Get2(data, i + 2); Debug.Assert(i + size <= usableSize); // Enforced by btreeInitPage() uint j; for (j = i + size - 1; j >= i; j--) hit[j]++; j = ConvertEx.Get2(data, i); Debug.Assert(j == 0 || j > i + size); // Enforced by btreeInitPage() Debug.Assert(j <= usableSize - 4); // Enforced by btreeInitPage() i = j; } uint cnt; for (i = cnt = 0; i < usableSize; i++) { if (hit[i] == 0) cnt++; else if (hit[i] > 1) { checkAppendMsg(check, (string)null, "Multiple uses for byte %d of page %d", i, pageID); break; } } if (cnt != data[hdr + 7]) checkAppendMsg(check, (string)null, "Fragmentation of %d bytes reported as %d on page %d", cnt, data[hdr + 7], pageID); } PCache.PageFree2(ref hit); releasePage(page); return depth + 1; }
static void checkList(IntegrityCk check, bool isFreeList, Pid pageID, int length, string context) { int expected = length; Pid firstID = pageID; while (length-- > 0 && check.MaxErrors != 0) { if (pageID < 1) { checkAppendMsg(check, context, "%d of %d pages missing from overflow list starting at %d", length + 1, expected, firstID); break; } if (checkRef(check, pageID, context)) break; PgHdr ovflPage = new PgHdr(); if (check.Pager.Acquire((Pid)pageID, ref ovflPage, false) != RC.OK) { checkAppendMsg(check, context, "failed to get page %d", pageID); break; } byte[] ovflData = Pager.GetData(ovflPage); if (isFreeList) { int n = (int)ConvertEx.Get4(ovflData, 4); #if !OMIT_AUTOVACUUM if (check.Bt.AutoVacuum) checkPtrmap(check, (uint)pageID, PTRMAP.FREEPAGE, 0, context); #endif if (n > (int)check.Bt.UsableSize / 4 - 2) { checkAppendMsg(check, context, "freelist leaf count too big on page %d", pageID); length--; } else { for (int i = 0; i < n; i++) { Pid freePageID = ConvertEx.Get4(ovflData, 8 + i * 4); #if !OMIT_AUTOVACUUM if (check.Bt.AutoVacuum) checkPtrmap(check, freePageID, PTRMAP.FREEPAGE, 0, context); #endif checkRef(check, freePageID, context); } length -= n; } } #if !OMIT_AUTOVACUUM else { // If this database supports auto-vacuum and iPage is not the last page in this overflow list, check that the pointer-map entry for // the following page matches iPage. if (check.Bt.AutoVacuum && length > 0) { int i = (int)ConvertEx.Get4(ovflData); checkPtrmap(check, (uint)i, PTRMAP.OVERFLOW2, (uint)pageID, context); } } #endif pageID = (Pid)ConvertEx.Get4(ovflData); Pager.Unref(ovflPage); } }
static void checkPtrmap(IntegrityCk check, Pid childID, PTRMAP type, Pid parentID, string context) { PTRMAP ptrmapType = 0; Pid ptrmapParentID = 0; RC rc = ptrmapGet(check.Bt, childID, ref ptrmapType, ref ptrmapParentID); if (rc != RC.OK) { if (rc == RC.NOMEM || rc == RC.IOERR_NOMEM) check.MallocFailed = true; checkAppendMsg(check, context, "Failed to read ptrmap key=%d", childID); return; } if (ptrmapType != type || ptrmapParentID != parentID) checkAppendMsg(check, context, "Bad ptr map entry key=%d expected=(%d,%d) got=(%d,%d)", childID, type, parentID, ptrmapType, ptrmapParentID); }
static bool checkRef(IntegrityCk check, Pid pageID, string context) { if (pageID == 0) return true; if (pageID > check.Pages) { checkAppendMsg(check, context, "invalid page number %d", pageID); return true; } if (getPageReferenced(check, pageID)) { checkAppendMsg(check, context, "2nd reference to page %d", pageID); return true; } setPageReferenced(check, pageID); return true; }
static void setPageReferenced(IntegrityCk check, Pid pageID) { Debug.Assert(pageID <= check.Pages && sizeof(byte) == 1); check.PgRefs[pageID / 8] |= (byte)(1 << (int)(pageID & 0x07)); }
static bool getPageReferenced(IntegrityCk check, Pid pageID) { Debug.Assert(pageID <= check.Pages && sizeof(byte) == 1); return ((check.PgRefs[pageID / 8] & (1 << (int)(pageID & 0x07))) != 0); }
static void checkAppendMsg(IntegrityCk check, StringBuilder msg1, string format, params object[] args) { if (check.MaxErrors == 0) return; //va_list ap; //lock (_va_listLock) { check.MaxErrors--; check.Errors++; //va_start(args, format); //if (check.errMsg.zText.Length != 0) // sqlite3StrAccumAppend(check.errMsg, "\n", 1); //if (msg1.Length > 0) // sqlite3StrAccumAppend(check.errMsg, msg1.ToString(), -1); //sqlite3VXPrintf(check.errMsg, 1, format, args); //va_end(ref args); } }
static string sqlite3BtreeIntegrityCheck( Btree p, /* The btree to be checked */ int[] aRoot, /* An array of root pages numbers for individual trees */ int nRoot, /* Number of entries in aRoot[] */ int mxErr, /* Stop reporting errors after this many */ ref int pnErr /* Write number of errors seen to this variable */ ) { Pgno i; int nRef; IntegrityCk sCheck = new IntegrityCk(); BtShared pBt = p.pBt; StringBuilder zErr = new StringBuilder(100);//char zErr[100]; sqlite3BtreeEnter(p); Debug.Assert(p.inTrans > TRANS_NONE && pBt.inTransaction > TRANS_NONE); nRef = sqlite3PagerRefcount(pBt.pPager); sCheck.pBt = pBt; sCheck.pPager = pBt.pPager; sCheck.nPage = btreePagecount(sCheck.pBt); sCheck.mxErr = mxErr; sCheck.nErr = 0; //sCheck.mallocFailed = 0; pnErr = 0; if (sCheck.nPage == 0) { sqlite3BtreeLeave(p); return(""); } sCheck.anRef = sqlite3Malloc(sCheck.anRef, (int)sCheck.nPage + 1); //if( !sCheck.anRef ){ // pnErr = 1; // sqlite3BtreeLeave(p); // return 0; //} // for (i = 0; i <= sCheck.nPage; i++) { sCheck.anRef[i] = 0; } i = PENDING_BYTE_PAGE(pBt); if (i <= sCheck.nPage) { sCheck.anRef[i] = 1; } sqlite3StrAccumInit(sCheck.errMsg, null, 1000, 20000); //sCheck.errMsg.useMalloc = 2; /* Check the integrity of the freelist */ checkList(sCheck, 1, (int)sqlite3Get4byte(pBt.pPage1.aData, 32), (int)sqlite3Get4byte(pBt.pPage1.aData, 36), "Main freelist: "); /* Check all the tables. */ for (i = 0; (int)i < nRoot && sCheck.mxErr != 0; i++) { if (aRoot[i] == 0) { continue; } #if !SQLITE_OMIT_AUTOVACUUM if (pBt.autoVacuum && aRoot[i] > 1) { checkPtrmap(sCheck, (u32)aRoot[i], PTRMAP_ROOTPAGE, 0, ""); } #endif checkTreePage(sCheck, aRoot[i], "List of tree roots: ", ref refNULL, ref refNULL, null, null); } /* Make sure every page in the file is referenced */ for (i = 1; i <= sCheck.nPage && sCheck.mxErr != 0; i++) { #if SQLITE_OMIT_AUTOVACUUM if (sCheck.anRef[i] == null) { checkAppendMsg(sCheck, 0, "Page %d is never used", i); } #else /* If the database supports auto-vacuum, make sure no tables contain ** references to pointer-map pages. */ if (sCheck.anRef[i] == 0 && (PTRMAP_PAGENO(pBt, i) != i || !pBt.autoVacuum)) { checkAppendMsg(sCheck, "", "Page %d is never used", i); } if (sCheck.anRef[i] != 0 && (PTRMAP_PAGENO(pBt, i) == i && pBt.autoVacuum)) { checkAppendMsg(sCheck, "", "Pointer map page %d is referenced", i); } #endif } /* Make sure this analysis did not leave any unref() pages. ** This is an internal consistency check; an integrity check ** of the integrity check. */ if (NEVER(nRef != sqlite3PagerRefcount(pBt.pPager))) { checkAppendMsg(sCheck, "", "Outstanding page count goes from %d to %d during this analysis", nRef, sqlite3PagerRefcount(pBt.pPager) ); } /* Clean up and report errors. */ sqlite3BtreeLeave(p); sCheck.anRef = null;// sqlite3_free( ref sCheck.anRef ); //if( sCheck.mallocFailed ){ // sqlite3StrAccumReset(sCheck.errMsg); // pnErr = sCheck.nErr+1; // return 0; //} pnErr = sCheck.nErr; if (sCheck.nErr == 0) { sqlite3StrAccumReset(sCheck.errMsg); } return(sqlite3StrAccumFinish(sCheck.errMsg)); }
static i64 refNULL = 0; //Dummy for C# ref NULL static int checkTreePage( IntegrityCk pCheck, /* Context for the sanity check */ int iPage, /* Page number of the page to check */ string zParentContext, /* Parent context */ ref i64 pnParentMinKey, ref i64 pnParentMaxKey, object _pnParentMinKey, /* C# Needed to determine if content passed*/ object _pnParentMaxKey /* C# Needed to determine if content passed*/ ) { MemPage pPage = new MemPage(); int i, rc, depth, d2, pgno, cnt; int hdr, cellStart; int nCell; u8[] data; BtShared pBt; int usableSize; StringBuilder zContext = new StringBuilder(100); byte[] hit = null; i64 nMinKey = 0; i64 nMaxKey = 0; sqlite3_snprintf(200, zContext, "Page %d: ", iPage); /* Check that the page exists */ pBt = pCheck.pBt; usableSize = (int)pBt.usableSize; if (iPage == 0) { return(0); } if (checkRef(pCheck, (u32)iPage, zParentContext) != 0) { return(0); } if ((rc = btreeGetPage(pBt, (Pgno)iPage, ref pPage, 0)) != 0) { checkAppendMsg(pCheck, zContext.ToString(), "unable to get the page. error code=%d", rc); return(0); } /* Clear MemPage.isInit to make sure the corruption detection code in ** btreeInitPage() is executed. */ pPage.isInit = 0; if ((rc = btreeInitPage(pPage)) != 0) { Debug.Assert(rc == SQLITE_CORRUPT); /* The only possible error from InitPage */ checkAppendMsg(pCheck, zContext.ToString(), "btreeInitPage() returns error code %d", rc); releasePage(pPage); return(0); } /* Check out all the cells. */ depth = 0; for (i = 0; i < pPage.nCell && pCheck.mxErr != 0; i++) { u8[] pCell; u32 sz; CellInfo info = new CellInfo(); /* Check payload overflow pages */ sqlite3_snprintf(200, zContext, "On tree page %d cell %d: ", iPage, i); int iCell = findCell(pPage, i); //pCell = findCell( pPage, i ); pCell = pPage.aData; btreeParseCellPtr(pPage, iCell, ref info); //btreeParseCellPtr( pPage, pCell, info ); sz = info.nData; if (0 == pPage.intKey) { sz += (u32)info.nKey; } /* For intKey pages, check that the keys are in order. */ else if (i == 0) { nMinKey = nMaxKey = info.nKey; } else { if (info.nKey <= nMaxKey) { checkAppendMsg(pCheck, zContext.ToString(), "Rowid %lld out of order (previous was %lld)", info.nKey, nMaxKey); } nMaxKey = info.nKey; } Debug.Assert(sz == info.nPayload); if ((sz > info.nLocal) //&& (pCell[info.iOverflow]<=&pPage.aData[pBt.usableSize]) ) { int nPage = (int)(sz - info.nLocal + usableSize - 5) / (usableSize - 4); Pgno pgnoOvfl = sqlite3Get4byte(pCell, iCell, info.iOverflow); #if !SQLITE_OMIT_AUTOVACUUM if (pBt.autoVacuum) { checkPtrmap(pCheck, pgnoOvfl, PTRMAP_OVERFLOW1, (u32)iPage, zContext.ToString()); } #endif checkList(pCheck, 0, (int)pgnoOvfl, nPage, zContext.ToString()); } /* Check sanity of left child page. */ if (0 == pPage.leaf) { pgno = (int)sqlite3Get4byte(pCell, iCell); //sqlite3Get4byte( pCell ); #if !SQLITE_OMIT_AUTOVACUUM if (pBt.autoVacuum) { checkPtrmap(pCheck, (u32)pgno, PTRMAP_BTREE, (u32)iPage, zContext.ToString()); } #endif if (i == 0) { d2 = checkTreePage(pCheck, pgno, zContext.ToString(), ref nMinKey, ref refNULL, pCheck, null); } else { d2 = checkTreePage(pCheck, pgno, zContext.ToString(), ref nMinKey, ref nMaxKey, pCheck, pCheck); } if (i > 0 && d2 != depth) { checkAppendMsg(pCheck, zContext, "Child page depth differs"); } depth = d2; } } if (0 == pPage.leaf) { pgno = (int)sqlite3Get4byte(pPage.aData, pPage.hdrOffset + 8); sqlite3_snprintf(200, zContext, "On page %d at right child: ", iPage); #if !SQLITE_OMIT_AUTOVACUUM if (pBt.autoVacuum) { checkPtrmap(pCheck, (u32)pgno, PTRMAP_BTREE, (u32)iPage, zContext.ToString()); } #endif // checkTreePage(pCheck, pgno, zContext, NULL, !pPage->nCell ? NULL : &nMaxKey); if (0 == pPage.nCell) { checkTreePage(pCheck, pgno, zContext.ToString(), ref refNULL, ref refNULL, null, null); } else { checkTreePage(pCheck, pgno, zContext.ToString(), ref refNULL, ref nMaxKey, null, pCheck); } } /* For intKey leaf pages, check that the min/max keys are in order ** with any left/parent/right pages. */ if (pPage.leaf != 0 && pPage.intKey != 0) { /* if we are a left child page */ if (_pnParentMinKey != null) { /* if we are the left most child page */ if (_pnParentMaxKey == null) { if (nMaxKey > pnParentMinKey) { checkAppendMsg(pCheck, zContext, "Rowid %lld out of order (max larger than parent min of %lld)", nMaxKey, pnParentMinKey); } } else { if (nMinKey <= pnParentMinKey) { checkAppendMsg(pCheck, zContext, "Rowid %lld out of order (min less than parent min of %lld)", nMinKey, pnParentMinKey); } if (nMaxKey > pnParentMaxKey) { checkAppendMsg(pCheck, zContext, "Rowid %lld out of order (max larger than parent max of %lld)", nMaxKey, pnParentMaxKey); } pnParentMinKey = nMaxKey; } /* else if we're a right child page */ } else if (_pnParentMaxKey != null) { if (nMinKey <= pnParentMaxKey) { checkAppendMsg(pCheck, zContext, "Rowid %lld out of order (min less than parent max of %lld)", nMinKey, pnParentMaxKey); } } } /* Check for complete coverage of the page */ data = pPage.aData; hdr = pPage.hdrOffset; hit = sqlite3Malloc(pBt.pageSize); //if( hit==null ){ // pCheck.mallocFailed = 1; //}else { int contentOffset = get2byteNotZero(data, hdr + 5); Debug.Assert(contentOffset <= usableSize); /* Enforced by btreeInitPage() */ Array.Clear(hit, contentOffset, usableSize - contentOffset); //memset(hit+contentOffset, 0, usableSize-contentOffset); for (int iLoop = contentOffset - 1; iLoop >= 0; iLoop--) { hit[iLoop] = 1;//memset(hit, 1, contentOffset); } nCell = get2byte(data, hdr + 3); cellStart = hdr + 12 - 4 * pPage.leaf; for (i = 0; i < nCell; i++) { int pc = get2byte(data, cellStart + i * 2); u32 size = 65536; int j; if (pc <= usableSize - 4) { size = cellSizePtr(pPage, data, pc); } if ((int)(pc + size - 1) >= usableSize) { checkAppendMsg(pCheck, "", "Corruption detected in cell %d on page %d", i, iPage); } else { for (j = (int)(pc + size - 1); j >= pc; j--) { hit[j]++; } } } i = get2byte(data, hdr + 1); while (i > 0) { int size, j; Debug.Assert(i <= usableSize - 4); /* Enforced by btreeInitPage() */ size = get2byte(data, i + 2); Debug.Assert(i + size <= usableSize); /* Enforced by btreeInitPage() */ for (j = i + size - 1; j >= i; j--) { hit[j]++; } j = get2byte(data, i); Debug.Assert(j == 0 || j > i + size); /* Enforced by btreeInitPage() */ Debug.Assert(j <= usableSize - 4); /* Enforced by btreeInitPage() */ i = j; } for (i = cnt = 0; i < usableSize; i++) { if (hit[i] == 0) { cnt++; } else if (hit[i] > 1) { checkAppendMsg(pCheck, "", "Multiple uses for byte %d of page %d", i, iPage); break; } } if (cnt != data[hdr + 7]) { checkAppendMsg(pCheck, "", "Fragmentation of %d bytes reported as %d on page %d", cnt, data[hdr + 7], iPage); } } sqlite3PageFree(ref hit); releasePage(pPage); return(depth + 1); }
internal static void checkList(IntegrityCk pCheck, int isFreeList, int iPage, int N, string zContext) { int i; int expected = N; int iFirst = iPage; while (N-- > 0 && pCheck.mxErr != 0) { var pOvflPage = new PgHdr(); if (iPage < 1) { checkAppendMsg(pCheck, zContext, "%d of %d pages missing from overflow list starting at %d", N + 1, expected, iFirst); break; } if (checkRef(pCheck, (uint)iPage, zContext) != 0) { break; } byte[] pOvflData; if (Pager.sqlite3PagerGet(pCheck.pPager, (Pgno)iPage, ref pOvflPage) != 0) { checkAppendMsg(pCheck, zContext, "failed to get page %d", iPage); break; } pOvflData = Pager.sqlite3PagerGetData(pOvflPage); if (isFreeList != 0) { int n = (int)ConvertEx.sqlite3Get4byte(pOvflData, 4); #if !SQLITE_OMIT_AUTOVACUUM if (pCheck.pBt.autoVacuum) { checkPtrmap(pCheck, (uint)iPage, PTRMAP_FREEPAGE, 0, zContext); } #endif if (n > (int)pCheck.pBt.usableSize / 4 - 2) { checkAppendMsg(pCheck, zContext, "freelist leaf count too big on page %d", iPage); N--; } else { for (i = 0; i < n; i++) { Pgno iFreePage = ConvertEx.sqlite3Get4byte(pOvflData, 8 + i * 4); #if !SQLITE_OMIT_AUTOVACUUM if (pCheck.pBt.autoVacuum) { checkPtrmap(pCheck, iFreePage, PTRMAP_FREEPAGE, 0, zContext); } #endif checkRef(pCheck, iFreePage, zContext); } N -= n; } } #if !SQLITE_OMIT_AUTOVACUUM else { /* If this database supports auto-vacuum and iPage is not the last ** page in this overflow list, check that the pointer-map entry for ** the following page matches iPage. */ if (pCheck.pBt.autoVacuum && N > 0) { i = (int)ConvertEx.sqlite3Get4byte(pOvflData); checkPtrmap(pCheck, (uint)i, PTRMAP_OVERFLOW2, (uint)iPage, zContext); } } #endif iPage = (int)ConvertEx.sqlite3Get4byte(pOvflData); Pager.sqlite3PagerUnref(pOvflPage); } }