internal RC ptrmapGet(Pgno key, ref PTRMAP pEType, ref Pgno pPgno) { Debug.Assert(MutexEx.Held(this.Mutex)); var iPtrmap = (int)MemPage.PTRMAP_PAGENO(this, key); var pDbPage = new PgHdr(); // The pointer map page var rc = this.Pager.Get((Pgno)iPtrmap, ref pDbPage); if (rc != RC.OK) { return(rc); } var pPtrmap = Pager.sqlite3PagerGetData(pDbPage);// Pointer map page data var offset = (int)MemPage.PTRMAP_PTROFFSET((Pgno)iPtrmap, key); if (offset < 0) { Pager.Unref(pDbPage); return(SysEx.SQLITE_CORRUPT_BKPT()); } Debug.Assert(offset <= (int)this.UsableSize - 5); var v = pPtrmap[offset]; if (v < 1 || v > 5) { return(SysEx.SQLITE_CORRUPT_BKPT()); } pEType = (PTRMAP)v; pPgno = ConvertEx.Get4(pPtrmap, offset + 1); Pager.Unref(pDbPage); return(RC.OK); }
internal void releasePage() { if (this != null) { Debug.Assert(this.Data != null); Debug.Assert(this.Shared != null); // TODO -- find out why corrupt9 & diskfull fail on this tests //Debug.Assert( Pager.sqlite3PagerGetExtra( pPage.pDbPage ) == pPage ); //Debug.Assert( Pager.sqlite3PagerGetData( pPage.pDbPage ) == pPage.aData ); Debug.Assert(MutexEx.Held(this.Shared.Mutex)); Pager.Unref(this.DbPage); } }
internal void ptrmapPut(Pgno key, PTRMAP eType, Pgno parent, ref RC rRC) { if (rRC != RC.OK) { return; } Debug.Assert(MutexEx.Held(this.Mutex)); // The master-journal page number must never be used as a pointer map page Debug.Assert(!MemPage.PTRMAP_ISPAGE(this, MemPage.PENDING_BYTE_PAGE(this))); Debug.Assert(this.AutoVacuum); if (key == 0) { rRC = SysEx.SQLITE_CORRUPT_BKPT(); return; } var iPtrmap = MemPage.PTRMAP_PAGENO(this, key); var pDbPage = new PgHdr(); // The pointer map page var rc = this.Pager.Get(iPtrmap, ref pDbPage); if (rc != RC.OK) { rRC = rc; return; } var offset = (int)MemPage.PTRMAP_PTROFFSET(iPtrmap, key); if (offset < 0) { rRC = SysEx.SQLITE_CORRUPT_BKPT(); goto ptrmap_exit; } Debug.Assert(offset <= (int)this.UsableSize - 5); var pPtrmap = Pager.sqlite3PagerGetData(pDbPage); // The pointer map data if (eType != (PTRMAP)pPtrmap[offset] || ConvertEx.Get4(pPtrmap, offset + 1) != parent) { Btree.TRACE("PTRMAP_UPDATE: {0}->({1},{2})", key, eType, parent); rRC = rc = Pager.Write(pDbPage); if (rc == RC.OK) { pPtrmap[offset] = (byte)eType; ConvertEx.Put4L(pPtrmap, (uint)offset + 1, parent); } } ptrmap_exit: Pager.Unref(pDbPage); }
internal RC clearCell(int pCell) { Debug.Assert(MutexEx.Held(this.Shared.Mutex)); var info = new CellInfo(); btreeParseCellPtr(pCell, ref info); if (info.iOverflow == 0) { return(RC.OK); // No overflow pages. Return without doing anything } var pBt = this.Shared; var ovflPgno = (Pgno)ConvertEx.Get4(this.Data, pCell + info.iOverflow); Debug.Assert(pBt.UsableSize > 4); var ovflPageSize = (uint)(pBt.UsableSize - 4); var nOvfl = (int)((info.nPayload - info.nLocal + ovflPageSize - 1) / ovflPageSize); Debug.Assert(ovflPgno == 0 || nOvfl > 0); RC rc; while (nOvfl-- != 0) { Pgno iNext = 0; MemPage pOvfl = null; if (ovflPgno < 2 || ovflPgno > pBt.btreePagecount()) { // 0 is not a legal page number and page 1 cannot be an overflow page. Therefore if ovflPgno<2 or past the end of the file the database must be corrupt. return(SysEx.SQLITE_CORRUPT_BKPT()); } if (nOvfl != 0) { rc = pBt.getOverflowPage(ovflPgno, out pOvfl, out iNext); if (rc != RC.OK) { return(rc); } } if ((pOvfl != null || ((pOvfl = pBt.btreePageLookup(ovflPgno)) != null)) && Pager.GetPageRefCount(pOvfl.DbPage) != 1) { // There is no reason any cursor should have an outstanding reference to an overflow page belonging to a cell that is being deleted/updated. // So if there exists more than one reference to this page, then it must not really be an overflow page and the database must be corrupt. // It is helpful to detect this before calling freePage2(), as freePage2() may zero the page contents if secure-delete mode is // enabled. If this 'overflow' page happens to be a page that the caller is iterating through or using in some other way, this // can be problematic. rc = SysEx.SQLITE_CORRUPT_BKPT(); } else { rc = pBt.freePage2(pOvfl, ovflPgno); } if (pOvfl != null) { Pager.Unref(pOvfl.DbPage); } if (rc != RC.OK) { return(rc); } ovflPgno = iNext; } return(RC.OK); }
internal RC btreeCreateTable(ref int piTable, CREATETABLE createTabFlags) { var pBt = this.Shared; var pRoot = new MemPage(); Pgno pgnoRoot = 0; int ptfFlags; // Page-type flage for the root page of new table RC rc; Debug.Assert(sqlite3BtreeHoldsMutex()); Debug.Assert(pBt.InTransaction == TRANS.WRITE); Debug.Assert(!pBt.ReadOnly); #if SQLITE_OMIT_AUTOVACUUM rc = allocateBtreePage(pBt, ref pRoot, ref pgnoRoot, 1, 0); if (rc != SQLITE.OK) { return(rc); } #else if (pBt.AutoVacuum) { Pgno pgnoMove = 0; // Move a page here to make room for the root-page var pPageMove = new MemPage(); // The page to move to. // Creating a new table may probably require moving an existing database to make room for the new tables root page. In case this page turns // out to be an overflow page, delete all overflow page-map caches held by open cursors. invalidateAllOverflowCache(pBt); // Read the value of meta[3] from the database to determine where the root page of the new table should go. meta[3] is the largest root-page // created so far, so the new root-page is (meta[3]+1). GetMeta((int)META.LARGEST_ROOT_PAGE, ref pgnoRoot); pgnoRoot++; // The new root-page may not be allocated on a pointer-map page, or the PENDING_BYTE page. while (pgnoRoot == MemPage.PTRMAP_PAGENO(pBt, pgnoRoot) || pgnoRoot == MemPage.PENDING_BYTE_PAGE(pBt)) { pgnoRoot++; } Debug.Assert(pgnoRoot >= 3); // Allocate a page. The page that currently resides at pgnoRoot will be moved to the allocated page (unless the allocated page happens to reside at pgnoRoot). rc = pBt.allocateBtreePage(ref pPageMove, ref pgnoMove, pgnoRoot, 1); if (rc != RC.OK) { return(rc); } if (pgnoMove != pgnoRoot) { // pgnoRoot is the page that will be used for the root-page of the new table (assuming an error did not occur). But we were // allocated pgnoMove. If required (i.e. if it was not allocated by extending the file), the current page at position pgnoMove // is already journaled. PTRMAP eType = 0; Pgno iPtrPage = 0; pPageMove.releasePage(); // Move the page currently at pgnoRoot to pgnoMove. rc = pBt.btreeGetPage(pgnoRoot, ref pRoot, 0); if (rc != RC.OK) { return(rc); } rc = pBt.ptrmapGet(pgnoRoot, ref eType, ref iPtrPage); if (eType == PTRMAP.ROOTPAGE || eType == PTRMAP.FREEPAGE) { rc = SysEx.SQLITE_CORRUPT_BKPT(); } if (rc != RC.OK) { pRoot.releasePage(); return(rc); } Debug.Assert(eType != PTRMAP.ROOTPAGE); Debug.Assert(eType != PTRMAP.FREEPAGE); rc = MemPage.relocatePage(pBt, pRoot, eType, iPtrPage, pgnoMove, 0); pRoot.releasePage(); // Obtain the page at pgnoRoot if (rc != RC.OK) { return(rc); } rc = pBt.btreeGetPage(pgnoRoot, ref pRoot, 0); if (rc != RC.OK) { return(rc); } rc = Pager.Write(pRoot.DbPage); if (rc != RC.OK) { pRoot.releasePage(); return(rc); } } else { pRoot = pPageMove; } // Update the pointer-map and meta-data with the new root-page number. pBt.ptrmapPut(pgnoRoot, PTRMAP.ROOTPAGE, 0, ref rc); if (rc != RC.OK) { pRoot.releasePage(); return(rc); } // When the new root page was allocated, page 1 was made writable in order either to increase the database filesize, or to decrement the // freelist count. Hence, the sqlite3BtreeUpdateMeta() call cannot fail. Debug.Assert(Pager.IsPageWriteable(pBt.Page1.DbPage)); rc = SetMeta(4, pgnoRoot); if (Check.NEVER(rc != RC.OK)) { pRoot.releasePage(); return(rc); } } else { rc = pBt.allocateBtreePage(ref pRoot, ref pgnoRoot, 1, 0); if (rc != RC.OK) { return(rc); } } #endif Debug.Assert(Pager.IsPageWriteable(pRoot.DbPage)); ptfFlags = ((createTabFlags & CREATETABLE.INTKEY) != 0 ? PTF_INTKEY | PTF_LEAFDATA | PTF_LEAF : PTF_ZERODATA | PTF_LEAF); pRoot.zeroPage(ptfFlags); Pager.Unref(pRoot.DbPage); Debug.Assert((pBt.OpenFlags & OPEN.SINGLE) == 0 || pgnoRoot == 2); piTable = (int)pgnoRoot; return(RC.OK); }
// was:fetchPayload private RC AccessPayload(uint offset, uint size, byte[] b, bool writeOperation) { var page = Pages[PageID]; // Btree page of current entry Debug.Assert(page != null); Debug.Assert(State == CursorState.VALID); Debug.Assert(PagesIndexs[PageID] < page.Cells); Debug.Assert(HoldsMutex()); GetCellInfo(); var payload = Info.Cells; var nKey = (uint)(page.HasIntKey ? 0 : (int)Info.nKey); var shared = Shared; // Btree this cursor belongs to if (Check.NEVER(offset + size > nKey + Info.nData) || Info.nLocal > shared.UsableSize) { // Trying to read or write past the end of the data is an error return(SysEx.SQLITE_CORRUPT_BKPT()); } // Check if data must be read/written to/from the btree page itself. var rc = RC.OK; uint bOffset = 0; if (offset < Info.nLocal) { var a = (int)size; if (a + offset > Info.nLocal) { a = (int)(Info.nLocal - offset); } rc = CopyPayload(page.DbPage, payload, (uint)(offset + Info.CellID + Info.nHeader), b, bOffset, (uint)a, writeOperation); offset = 0; bOffset += (uint)a; size -= (uint)a; } else { offset -= Info.nLocal; } var iIdx = 0; if (rc == RC.OK && size > 0) { var ovflSize = (uint)(shared.UsableSize - 4); // Bytes content per ovfl page var nextPage = (Pgno)ConvertEx.Get4(payload, Info.nLocal + Info.CellID + Info.nHeader); #if !SQLITE_OMIT_INCRBLOB // If the isIncrblobHandle flag is set and the BtCursor.aOverflow[] has not been allocated, allocate it now. The array is sized at // one entry for each overflow page in the overflow chain. The page number of the first overflow page is stored in aOverflow[0], // etc. A value of 0 in the aOverflow[] array means "not yet known" (the cache is lazily populated). if (IsIncrblob && OverflowIDs == null) { var nOvfl = (Info.nPayload - Info.nLocal + ovflSize - 1) / ovflSize; OverflowIDs = new Pgno[nOvfl]; } // If the overflow page-list cache has been allocated and the entry for the first required overflow page is valid, skip directly to it. if (OverflowIDs != null && OverflowIDs[offset / ovflSize] != 0) { iIdx = (int)(offset / ovflSize); nextPage = OverflowIDs[iIdx]; offset = (offset % ovflSize); } #endif for (; rc == RC.OK && size > 0 && nextPage != 0; iIdx++) { #if !SQLITE_OMIT_INCRBLOB // If required, populate the overflow page-list cache. if (OverflowIDs != null) { Debug.Assert(OverflowIDs[iIdx] == 0 || OverflowIDs[iIdx] == nextPage); OverflowIDs[iIdx] = nextPage; } #endif MemPage MemPageDummy = null; if (offset >= ovflSize) { // The only reason to read this page is to obtain the page number for the next page in the overflow chain. The page // data is not required. So first try to lookup the overflow page-list cache, if any, then fall back to the getOverflowPage() function. #if !SQLITE_OMIT_INCRBLOB if (OverflowIDs != null && OverflowIDs[iIdx + 1] != 0) { nextPage = OverflowIDs[iIdx + 1]; } else #endif rc = shared.getOverflowPage(nextPage, out MemPageDummy, out nextPage); offset -= ovflSize; } else { // Need to read this page properly. It contains some of the range of data that is being read (eOp==null) or written (eOp!=null). var pDbPage = new PgHdr(); var a = (int)size; rc = shared.Pager.Get(nextPage, ref pDbPage); if (rc == RC.OK) { payload = Pager.sqlite3PagerGetData(pDbPage); nextPage = ConvertEx.Get4(payload); if (a + offset > ovflSize) { a = (int)(ovflSize - offset); } rc = CopyPayload(pDbPage, payload, offset + 4, b, bOffset, (uint)a, writeOperation); Pager.Unref(pDbPage); offset = 0; size -= (uint)a; bOffset += (uint)a; } } } } if (rc == RC.OK && size > 0) { return(SysEx.SQLITE_CORRUPT_BKPT()); } return(rc); }