// was:sqlite3PagerUnref public static void Unref(DbPage pPg) { if (pPg != null) { var pPager = pPg.Pager; PCache.ReleasePage(pPg); pPager.pagerUnlockIfUnused(); } }
internal static MemPage btreePageFromDbPage(DbPage pDbPage, Pgno pgno, BtShared pBt) { var pPage = (MemPage)Pager.sqlite3PagerGetExtra<MemPage>(pDbPage); pPage.Data = Pager.sqlite3PagerGetData(pDbPage); pPage.DbPage = pDbPage; pPage.Shared = pBt; pPage.ID = pgno; pPage.HeaderOffset = (byte)(pPage.ID == 1 ? 100 : 0); return pPage; }
internal static MemPage btreePageFromDbPage(DbPage pDbPage, Pgno pgno, BtShared pBt) { var pPage = (MemPage)Pager.sqlite3PagerGetExtra <MemPage>(pDbPage); pPage.Data = Pager.sqlite3PagerGetData(pDbPage); pPage.DbPage = pDbPage; pPage.Shared = pBt; pPage.ID = pgno; pPage.HeaderOffset = (byte)(pPage.ID == 1 ? 100 : 0); return(pPage); }
internal RC btreeGetPage(Pgno pgno, ref MemPage ppPage, int noContent) { Debug.Assert(MutexEx.Held(this.Mutex)); DbPage pDbPage = null; var rc = this.Pager.Get(pgno, ref pDbPage, (byte)noContent); if (rc != RC.OK) { return(rc); } ppPage = MemPage.btreePageFromDbPage(pDbPage, pgno, this); return(RC.OK); }
// was:fetchPayload private static RC CopyPayload(DbPage dbPage, byte[] payload, uint payloadOffset, byte[] b, uint bOffset, uint count, bool writeOperation) { if (writeOperation) { // Copy data from buffer to page (a write operation) var rc = Pager.Write(dbPage); if (rc != RC.OK) return rc; Buffer.BlockCopy(b, (int)bOffset, payload, (int)payloadOffset, (int)count); } else // Copy data from page to buffer (a read operation) Buffer.BlockCopy(payload, (int)payloadOffset, b, (int)bOffset, (int)count); return RC.OK; }
internal static void pageReinit(DbPage pData) { var pPage = Pager.sqlite3PagerGetExtra<MemPage>(pData); Debug.Assert(Pager.GetPageRefCount(pData) > 0); if (pPage.HasInit) { Debug.Assert(MutexEx.Held(pPage.Shared.Mutex)); pPage.HasInit = false; if (Pager.GetPageRefCount(pData) > 1) // pPage might not be a btree page; it might be an overflow page or ptrmap page or a free page. In those cases, the following // call to btreeInitPage() will likely return SQLITE_CORRUPT. But no harm is done by this. And it is very important that // btreeInitPage() be called on every btree page so we make the call for every page that comes in for re-initing. pPage.btreeInitPage(); } }
internal static void pageReinit(DbPage pData) { var pPage = Pager.sqlite3PagerGetExtra <MemPage>(pData); Debug.Assert(Pager.GetPageRefCount(pData) > 0); if (pPage.HasInit) { Debug.Assert(MutexEx.Held(pPage.Shared.Mutex)); pPage.HasInit = false; if (Pager.GetPageRefCount(pData) > 1) { // pPage might not be a btree page; it might be an overflow page or ptrmap page or a free page. In those cases, the following // call to btreeInitPage() will likely return SQLITE_CORRUPT. But no harm is done by this. And it is very important that // btreeInitPage() be called on every btree page so we make the call for every page that comes in for re-initing. pPage.btreeInitPage(); } } }
// was:fetchPayload private static RC CopyPayload(DbPage dbPage, byte[] payload, uint payloadOffset, byte[] b, uint bOffset, uint count, bool writeOperation) { if (writeOperation) { // Copy data from buffer to page (a write operation) var rc = Pager.Write(dbPage); if (rc != RC.OK) { return(rc); } Buffer.BlockCopy(b, (int)bOffset, payload, (int)payloadOffset, (int)count); } else { // Copy data from page to buffer (a read operation) Buffer.BlockCopy(payload, (int)payloadOffset, b, (int)bOffset, (int)count); } return(RC.OK); }
// was:sqlite3PagerPageRefcount public static int GetPageRefCount(DbPage pPage) { return(PCache.sqlite3PcachePageRefcount(pPage)); }
public static bool IsPageWriteable(DbPage pPg) { return((pPg.flags & PgHdr.PGHDR.DIRTY) != 0); }
// was:sqlite3PagerIswriteable public static bool IsPageWriteable(DbPage pPg) { return(true); }
// was:sqlite3PagerRef public static void AddPageRef(DbPage pPg) { PCache.AddPageRef(pPg); }
// was:sqlite3PagerPagenumber public static Pgno GetPageID(DbPage pPg) { return(pPg.ID); }
// was:sqlite3PagerGet,sqlite3PagerAcquire public RC Get(Pgno pgno, ref DbPage ppPage) { return(Get(pgno, ref ppPage, 0)); }
// was:sqlite3PagerGet,sqlite3PagerAcquire public RC Get(Pgno pgno, ref DbPage ppPage) { return Get(pgno, ref ppPage, 0); }
// was:sqlite3PagerWrite public static RC Write(DbPage pDbPage) { var rc = RC.OK; var pPg = pDbPage; var pPager = pPg.Pager; var nPagePerSector = (uint)(pPager.sectorSize / pPager.pageSize); Debug.Assert(pPager.eState >= PAGER.WRITER_LOCKED); Debug.Assert(pPager.eState != PAGER.ERROR); Debug.Assert(pPager.assert_pager_state()); if (nPagePerSector > 1) { Pgno nPageCount = 0; // Total number of pages in database file Pgno pg1; // First page of the sector pPg is located on. Pgno nPage = 0; // Number of pages starting at pg1 to journal bool needSync = false; // True if any page has PGHDR_NEED_SYNC // Set the doNotSyncSpill flag to 1. This is because we cannot allow a journal header to be written between the pages journaled by // this function. Debug.Assert( #if SQLITE_OMIT_MEMORYDB 0==MEMDB #else 0 == pPager.memDb #endif ); Debug.Assert(pPager.doNotSyncSpill == 0); pPager.doNotSyncSpill++; // This trick assumes that both the page-size and sector-size are an integer power of 2. It sets variable pg1 to the identifier // of the first page of the sector pPg is located on. pg1 = (Pgno)((pPg.ID - 1) & ~(nPagePerSector - 1)) + 1; nPageCount = pPager.dbSize; if (pPg.ID > nPageCount) nPage = (pPg.ID - pg1) + 1; else if ((pg1 + nPagePerSector - 1) > nPageCount) nPage = nPageCount + 1 - pg1; else nPage = nPagePerSector; Debug.Assert(nPage > 0); Debug.Assert(pg1 <= pPg.ID); Debug.Assert((pg1 + nPage) > pPg.ID); for (var ii = 0; ii < nPage && rc == RC.OK; ii++) { var pg = (Pgno)(pg1 + ii); var pPage = new PgHdr(); if (pg == pPg.ID || pPager.pInJournal.Get(pg) == 0) { if (pg != ((VirtualFile.PENDING_BYTE / (pPager.pageSize)) + 1)) { rc = pPager.Get(pg, ref pPage); if (rc == RC.OK) { rc = pager_write(pPage); if ((pPage.Flags & PgHdr.PGHDR.NEED_SYNC) != 0) needSync = true; Unref(pPage); } } } else if ((pPage = pPager.pager_lookup(pg)) != null) { if ((pPage.Flags & PgHdr.PGHDR.NEED_SYNC) != 0) needSync = true; Unref(pPage); } } // If the PGHDR_NEED_SYNC flag is set for any of the nPage pages starting at pg1, then it needs to be set for all of them. Because // writing to any of these nPage pages may damage the others, the journal file must contain sync()ed copies of all of them // before any of them can be written out to the database file. if (rc == RC.OK && needSync) { Debug.Assert( #if SQLITE_OMIT_MEMORYDB 0==MEMDB #else 0 == pPager.memDb #endif ); for (var ii = 0; ii < nPage; ii++) { var pPage = pPager.pager_lookup((Pgno)(pg1 + ii)); if (pPage != null) { pPage.Flags |= PgHdr.PGHDR.NEED_SYNC; Unref(pPage); } } } Debug.Assert(pPager.doNotSyncSpill == 1); pPager.doNotSyncSpill--; } else rc = pager_write(pDbPage); return rc; }
public RC sqlite3PagerMovepage(DbPage pPg, Pgno pgno, int isCommit) { Debug.Assert(pPg.Refs > 0); Debug.Assert(this.eState == PAGER.WRITER_CACHEMOD || this.eState == PAGER.WRITER_DBMOD); Debug.Assert(assert_pager_state()); // In order to be able to rollback, an in-memory database must journal the page we are moving from. var rc = RC.OK; if ( #if SQLITE_OMIT_MEMORYDB 1==MEMDB #else this.memDb != 0 #endif ) { rc = Write(pPg); if (rc != RC.OK) return rc; } PgHdr pPgOld; // The page being overwritten. uint needSyncPgno = 0; // Old value of pPg.pgno, if sync is required Pgno origPgno; // The original page number // If the page being moved is dirty and has not been saved by the latest savepoint, then save the current contents of the page into the // sub-journal now. This is required to handle the following scenario: // BEGIN; // <journal page X, then modify it in memory> // SAVEPOINT one; // <Move page X to location Y> // ROLLBACK TO one; // If page X were not written to the sub-journal here, it would not be possible to restore its contents when the "ROLLBACK TO one" // statement were is processed. // subjournalPage() may need to allocate space to store pPg.pgno into one or more savepoint bitvecs. This is the reason this function // may return SQLITE_NOMEM. if ((pPg.Flags & PgHdr.PGHDR.DIRTY) != 0 && subjRequiresPage(pPg) && RC.OK != (rc = subjournalPage(pPg))) return rc; PAGERTRACE("MOVE {0} page {1} (needSync={2}) moves to {3}", PAGERID(this), pPg.ID, (pPg.Flags & PgHdr.PGHDR.NEED_SYNC) != 0 ? 1 : 0, pgno); SysEx.IOTRACE("MOVE {0} {1} {2}", this.GetHashCode(), pPg.ID, pgno); // If the journal needs to be sync()ed before page pPg.pgno can be written to, store pPg.pgno in local variable needSyncPgno. // If the isCommit flag is set, there is no need to remember that the journal needs to be sync()ed before database page pPg.pgno // can be written to. The caller has already promised not to write to it. if (((pPg.Flags & PgHdr.PGHDR.NEED_SYNC) != 0) && 0 == isCommit) { needSyncPgno = pPg.ID; Debug.Assert(pageInJournal(pPg) || pPg.ID > this.dbOrigSize); Debug.Assert((pPg.Flags & PgHdr.PGHDR.DIRTY) != 0); } // If the cache contains a page with page-number pgno, remove it from its hash chain. Also, if the PGHDR_NEED_SYNC was set for // page pgno before the 'move' operation, it needs to be retained for the page moved there. pPg.Flags &= ~PgHdr.PGHDR.NEED_SYNC; pPgOld = pager_lookup(pgno); Debug.Assert(null == pPgOld || pPgOld.Refs == 1); if (pPgOld != null) { pPg.Flags |= (pPgOld.Flags & PgHdr.PGHDR.NEED_SYNC); if ( #if SQLITE_OMIT_MEMORYDB 1==MEMDB #else this.memDb != 0 #endif ) // Do not discard pages from an in-memory database since we might need to rollback later. Just move the page out of the way. PCache.MovePage(pPgOld, this.dbSize + 1); else PCache.DropPage(pPgOld); } origPgno = pPg.ID; PCache.MovePage(pPg, pgno); PCache.MakePageDirty(pPg); // For an in-memory database, make sure the original page continues to exist, in case the transaction needs to roll back. Use pPgOld // as the original page since it has already been allocated. if ( #if SQLITE_OMIT_MEMORYDB 0!=MEMDB #else 0 != this.memDb #endif ) { Debug.Assert(pPgOld); PCache.MovePage(pPgOld, origPgno); Unref(pPgOld); } if (needSyncPgno != 0) { // If needSyncPgno is non-zero, then the journal file needs to be sync()ed before any data is written to database file page needSyncPgno. // Currently, no such page exists in the page-cache and the "is journaled" bitvec flag has been set. This needs to be remedied by // loading the page into the pager-cache and setting the PGHDR_NEED_SYNC flag. // If the attempt to load the page into the page-cache fails, (due to a malloc() or IO failure), clear the bit in the pInJournal[] // array. Otherwise, if the page is loaded and written again in this transaction, it may be written to the database file before // it is synced into the journal file. This way, it may end up in the journal file twice, but that is not a problem. PgHdr pPgHdr = null; rc = Get(needSyncPgno, ref pPgHdr); if (rc != RC.OK) { if (needSyncPgno <= this.dbOrigSize) { Debug.Assert(this.pTmpSpace != null); var pTemp = new uint[this.pTmpSpace.Length]; this.pInJournal.Clear(needSyncPgno, pTemp); } return rc; } pPgHdr.Flags |= PgHdr.PGHDR.NEED_SYNC; PCache.MakePageDirty(pPgHdr); Unref(pPgHdr); } return RC.OK; }
// was:sqlite3PagerWrite public static RC Write(DbPage pDbPage) { var rc = RC.OK; var pPg = pDbPage; var pPager = pPg.Pager; var nPagePerSector = (uint)(pPager.sectorSize / pPager.pageSize); Debug.Assert(pPager.eState >= PAGER.WRITER_LOCKED); Debug.Assert(pPager.eState != PAGER.ERROR); Debug.Assert(pPager.assert_pager_state()); if (nPagePerSector > 1) { Pgno nPageCount = 0; // Total number of pages in database file Pgno pg1; // First page of the sector pPg is located on. Pgno nPage = 0; // Number of pages starting at pg1 to journal bool needSync = false; // True if any page has PGHDR_NEED_SYNC // Set the doNotSyncSpill flag to 1. This is because we cannot allow a journal header to be written between the pages journaled by // this function. Debug.Assert( #if SQLITE_OMIT_MEMORYDB 0 == MEMDB #else 0 == pPager.memDb #endif ); Debug.Assert(pPager.doNotSyncSpill == 0); pPager.doNotSyncSpill++; // This trick assumes that both the page-size and sector-size are an integer power of 2. It sets variable pg1 to the identifier // of the first page of the sector pPg is located on. pg1 = (Pgno)((pPg.ID - 1) & ~(nPagePerSector - 1)) + 1; nPageCount = pPager.dbSize; if (pPg.ID > nPageCount) { nPage = (pPg.ID - pg1) + 1; } else if ((pg1 + nPagePerSector - 1) > nPageCount) { nPage = nPageCount + 1 - pg1; } else { nPage = nPagePerSector; } Debug.Assert(nPage > 0); Debug.Assert(pg1 <= pPg.ID); Debug.Assert((pg1 + nPage) > pPg.ID); for (var ii = 0; ii < nPage && rc == RC.OK; ii++) { var pg = (Pgno)(pg1 + ii); var pPage = new PgHdr(); if (pg == pPg.ID || pPager.pInJournal.Get(pg) == 0) { if (pg != ((VirtualFile.PENDING_BYTE / (pPager.pageSize)) + 1)) { rc = pPager.Get(pg, ref pPage); if (rc == RC.OK) { rc = pager_write(pPage); if ((pPage.Flags & PgHdr.PGHDR.NEED_SYNC) != 0) { needSync = true; } Unref(pPage); } } } else if ((pPage = pPager.pager_lookup(pg)) != null) { if ((pPage.Flags & PgHdr.PGHDR.NEED_SYNC) != 0) { needSync = true; } Unref(pPage); } } // If the PGHDR_NEED_SYNC flag is set for any of the nPage pages starting at pg1, then it needs to be set for all of them. Because // writing to any of these nPage pages may damage the others, the journal file must contain sync()ed copies of all of them // before any of them can be written out to the database file. if (rc == RC.OK && needSync) { Debug.Assert( #if SQLITE_OMIT_MEMORYDB 0 == MEMDB #else 0 == pPager.memDb #endif ); for (var ii = 0; ii < nPage; ii++) { var pPage = pPager.pager_lookup((Pgno)(pg1 + ii)); if (pPage != null) { pPage.Flags |= PgHdr.PGHDR.NEED_SYNC; Unref(pPage); } } } Debug.Assert(pPager.doNotSyncSpill == 1); pPager.doNotSyncSpill--; } else { rc = pager_write(pDbPage); } return(rc); }
public RC Get(Pgno pgno, ref DbPage ppPage, byte noContent) { Debug.Assert(eState >= PAGER.READER); Debug.Assert(assert_pager_state()); if (pgno == 0) return SysEx.SQLITE_CORRUPT_BKPT(); // If the pager is in the error state, return an error immediately. Otherwise, request the page from the PCache layer. var rc = (errCode != RC.OK ? errCode : pPCache.FetchPage(pgno, 1, ref ppPage)); PgHdr pPg = null; if (rc != RC.OK) { // Either the call to sqlite3PcacheFetch() returned an error or the pager was already in the error-state when this function was called. // Set pPg to 0 and jump to the exception handler. */ pPg = null; goto pager_get_err; } Debug.Assert((ppPage).ID == pgno); Debug.Assert((ppPage).Pager == this || (ppPage).Pager == null); if ((ppPage).Pager != null && 0 == noContent) { // In this case the pcache already contains an initialized copy of the page. Return without further ado. Debug.Assert(pgno <= PAGER_MAX_PGNO && pgno != PAGER_MJ_PGNO(this)); return RC.OK; } else { // The pager cache has created a new page. Its content needs to be initialized. pPg = ppPage; pPg.Pager = this; pPg.Extra = _memPageBuilder; // The maximum page number is 2^31. Return SQLITE_CORRUPT if a page number greater than this, or the unused locking-page, is requested. if (pgno > PAGER_MAX_PGNO || pgno == PAGER_MJ_PGNO(this)) { rc = SysEx.SQLITE_CORRUPT_BKPT(); goto pager_get_err; } if ( #if SQLITE_OMIT_MEMORYDB 1==MEMDB #else memDb != 0 #endif || dbSize < pgno || noContent != 0 || !fd.IsOpen) { if (pgno > mxPgno) { rc = RC.FULL; goto pager_get_err; } if (noContent != 0) { // Failure to set the bits in the InJournal bit-vectors is benign. It merely means that we might do some extra work to journal a // page that does not need to be journaled. Nevertheless, be sure to test the case where a malloc error occurs while trying to set // a bit in a bit vector. MallocEx.sqlite3BeginBenignMalloc(); if (pgno <= dbOrigSize) pInJournal.Set(pgno); addToSavepointBitvecs(pgno); MallocEx.sqlite3EndBenignMalloc(); } Array.Clear(pPg.Data, 0, pageSize); SysEx.IOTRACE("ZERO {0:x} {1}\n", this.GetHashCode(), pgno); } else { Debug.Assert(pPg.Pager == this); rc = readDbPage(pPg); if (rc != RC.OK) goto pager_get_err; } pager_set_pagehash(pPg); } return RC.OK; pager_get_err: Debug.Assert(rc != RC.OK); if (pPg != null) PCache.DropPage(pPg); pagerUnlockIfUnused(); ppPage = null; return rc; }
public RC Get(Pgno pgno, ref DbPage ppPage, byte noContent) { Debug.Assert(eState >= PAGER.READER); Debug.Assert(assert_pager_state()); if (pgno == 0) { return(SysEx.SQLITE_CORRUPT_BKPT()); } // If the pager is in the error state, return an error immediately. Otherwise, request the page from the PCache layer. var rc = (errCode != RC.OK ? errCode : pPCache.FetchPage(pgno, 1, ref ppPage)); PgHdr pPg = null; if (rc != RC.OK) { // Either the call to sqlite3PcacheFetch() returned an error or the pager was already in the error-state when this function was called. // Set pPg to 0 and jump to the exception handler. */ pPg = null; goto pager_get_err; } Debug.Assert((ppPage).ID == pgno); Debug.Assert((ppPage).Pager == this || (ppPage).Pager == null); if ((ppPage).Pager != null && 0 == noContent) { // In this case the pcache already contains an initialized copy of the page. Return without further ado. Debug.Assert(pgno <= PAGER_MAX_PGNO && pgno != PAGER_MJ_PGNO(this)); return(RC.OK); } else { // The pager cache has created a new page. Its content needs to be initialized. pPg = ppPage; pPg.Pager = this; pPg.Extra = _memPageBuilder; // The maximum page number is 2^31. Return SQLITE_CORRUPT if a page number greater than this, or the unused locking-page, is requested. if (pgno > PAGER_MAX_PGNO || pgno == PAGER_MJ_PGNO(this)) { rc = SysEx.SQLITE_CORRUPT_BKPT(); goto pager_get_err; } if ( #if SQLITE_OMIT_MEMORYDB 1 == MEMDB #else memDb != 0 #endif || dbSize < pgno || noContent != 0 || !fd.IsOpen) { if (pgno > mxPgno) { rc = RC.FULL; goto pager_get_err; } if (noContent != 0) { // Failure to set the bits in the InJournal bit-vectors is benign. It merely means that we might do some extra work to journal a // page that does not need to be journaled. Nevertheless, be sure to test the case where a malloc error occurs while trying to set // a bit in a bit vector. MallocEx.sqlite3BeginBenignMalloc(); if (pgno <= dbOrigSize) { pInJournal.Set(pgno); } addToSavepointBitvecs(pgno); MallocEx.sqlite3EndBenignMalloc(); } Array.Clear(pPg.Data, 0, pageSize); SysEx.IOTRACE("ZERO {0:x} {1}\n", this.GetHashCode(), pgno); }