private void pagerUnlockAndRollback() { if (this.eState != PAGER.ERROR && this.eState != PAGER.OPEN) { Debug.Assert(assert_pager_state()); if (this.eState >= PAGER.WRITER_LOCKED) { MallocEx.sqlite3BeginBenignMalloc(); Rollback(); MallocEx.sqlite3EndBenignMalloc(); } else if (!this.exclusiveMode) { Debug.Assert(this.eState == PAGER.READER); pager_end_transaction(0); } } pager_unlock(); }
private RC pcache1ResizeHash() { Debug.Assert(MutexEx.Held(pGroup.mutex)); var nNew = nHash * 2; if (nNew < 256) { nNew = 256; } pcache1LeaveMutex(pGroup); if (nHash != 0) { MallocEx.sqlite3BeginBenignMalloc(); } var apNew = new PgHdr1[nNew]; if (nHash != 0) { MallocEx.sqlite3EndBenignMalloc(); } pcache1EnterMutex(pGroup); if (apNew != null) { for (var i = 0; i < nHash; i++) { var pNext = apHash[i]; PgHdr1 pPage; while ((pPage = pNext) != null) { var h = (Pgno)(pPage.iKey % nNew); pNext = pPage.pNext; pPage.pNext = apNew[h]; apNew[h] = pPage; } } apHash = apNew; nHash = nNew; } return(apHash != null ? RC.OK : RC.NOMEM); }
public PgHdr xFetch(Pgno key, int createFlag) { Debug.Assert(bPurgeable || createFlag != 1); Debug.Assert(bPurgeable || nMin == 0); Debug.Assert(!bPurgeable || nMin == 10); Debug.Assert(nMin == 0 || bPurgeable); PGroup pGroup; pcache1EnterMutex(pGroup = this.pGroup); // Step 1: Search the hash table for an existing entry. PgHdr1 pPage = null; if (nHash > 0) { var h = (int)(key % nHash); for (pPage = apHash[h]; pPage != null && pPage.iKey != key; pPage = pPage.pNext) { ; } } // Step 2: Abort if no existing page is found and createFlag is 0 if (pPage != null || createFlag == 0) { pcache1PinPage(pPage); goto fetch_out; } // The pGroup local variable will normally be initialized by the pcache1EnterMutex() macro above. But if SQLITE_MUTEX_OMIT is defined, // then pcache1EnterMutex() is a no-op, so we have to initialize the local variable here. Delaying the initialization of pGroup is an // optimization: The common case is to exit the module before reaching // this point. #if SQLITE_MUTEX_OMIT pGroup = pCache.pGroup; #endif // Step 3: Abort if createFlag is 1 but the cache is nearly full var nPinned = nPage - nRecyclable; Debug.Assert(nPinned >= 0); Debug.Assert(pGroup.mxPinned == pGroup.nMaxPage + 10 - pGroup.nMinPage); Debug.Assert(n90pct == nMax * 9 / 10); if (createFlag == 1 && (nPinned >= pGroup.mxPinned || nPinned >= (int)n90pct || pcache1UnderMemoryPressure())) { goto fetch_out; } if (nPage >= nHash && pcache1ResizeHash() != 0) { goto fetch_out; } // Step 4. Try to recycle a page. if (bPurgeable && pGroup.pLruTail != null && ((nPage + 1 >= nMax) || pGroup.nCurrentPage >= pGroup.nMaxPage || pcache1UnderMemoryPressure())) { pPage = pGroup.pLruTail; pcache1RemoveFromHash(pPage); pcache1PinPage(pPage); PCache1 pOtherCache; if ((pOtherCache = pPage.pCache).szPage != szPage) { pcache1FreePage(ref pPage); pPage = null; } else { pGroup.nCurrentPage -= (pOtherCache.bPurgeable ? 1 : 0) - (bPurgeable ? 1 : 0); } } // Step 5. If a usable page buffer has still not been found, attempt to allocate a new one. if (null == pPage) { if (createFlag == 1) { MallocEx.sqlite3BeginBenignMalloc(); } pcache1LeaveMutex(pGroup); pPage = pcache1AllocPage(); pcache1EnterMutex(pGroup); if (createFlag == 1) { MallocEx.sqlite3EndBenignMalloc(); } } if (pPage != null) { var h = (int)(key % nHash); nPage++; pPage.iKey = key; pPage.pNext = apHash[h]; pPage.pCache = this; pPage.pLruPrev = null; pPage.pLruNext = null; PGHDR1_TO_PAGE(pPage).ClearState(); pPage.pPgHdr.PgHdr1 = pPage; apHash[h] = pPage; } fetch_out: if (pPage != null && key > iMaxKey) { iMaxKey = key; } pcache1LeaveMutex(pGroup); return(pPage != null ? PGHDR1_TO_PAGE(pPage) : null); }
private RC hasHotJournal(ref int pExists) { var pVfs = this.pVfs; var jrnlOpen = (this.jfd.IsOpen ? 1 : 0); Debug.Assert(this.useJournal != 0); Debug.Assert(this.fd.IsOpen); Debug.Assert(this.eState == PAGER.OPEN); Debug.Assert(jrnlOpen == 0 || (this.jfd.DeviceCharacteristics & VirtualFile.IOCAP.UNDELETABLE_WHEN_OPEN) != 0); pExists = 0; var rc = RC.OK; var exists = 1; // True if a journal file is present if (0 == jrnlOpen) { rc = pVfs.xAccess(this.zJournal, VirtualFileSystem.ACCESS.EXISTS, out exists); } if (rc == RC.OK && exists != 0) { int locked = 0; // True if some process holds a RESERVED lock // Race condition here: Another process might have been holding the the RESERVED lock and have a journal open at the sqlite3OsAccess() // call above, but then delete the journal and drop the lock before we get to the following sqlite3OsCheckReservedLock() call. If that // is the case, this routine might think there is a hot journal when in fact there is none. This results in a false-positive which will // be dealt with by the playback routine. rc = this.fd.CheckReservedLock(ref locked); if (rc == RC.OK && locked == 0) { Pgno nPage = 0; // Number of pages in database file // Check the size of the database file. If it consists of 0 pages, then delete the journal file. See the header comment above for // the reasoning here. Delete the obsolete journal file under a RESERVED lock to avoid race conditions and to avoid violating [H33020]. rc = pagerPagecount(ref nPage); if (rc == RC.OK) { if (nPage == 0) { MallocEx.sqlite3BeginBenignMalloc(); if (pagerLockDb(VFSLOCK.RESERVED) == RC.OK) { pVfs.xDelete(this.zJournal, 0); if (!this.exclusiveMode) { pagerUnlockDb(VFSLOCK.SHARED); } } MallocEx.sqlite3EndBenignMalloc(); } else { // The journal file exists and no other connection has a reserved or greater lock on the database file. Now check that there is // at least one non-zero bytes at the start of the journal file. If there is, then we consider this journal to be hot. If not, // it can be ignored. if (0 == jrnlOpen) { var f = VirtualFileSystem.OPEN.READONLY | VirtualFileSystem.OPEN.MAIN_JOURNAL; rc = FileEx.OsOpen(pVfs, this.zJournal, this.jfd, f, ref f); } if (rc == RC.OK) { var first = new byte[1]; rc = this.jfd.Read(first, 1, 0); if (rc == RC.IOERR_SHORT_READ) { rc = RC.OK; } if (0 == jrnlOpen) { FileEx.OSClose(this.jfd); } pExists = (first[0] != 0) ? 1 : 0; } else if (rc == RC.CANTOPEN) { // If we cannot open the rollback journal file in order to see if its has a zero header, that might be due to an I/O error, or // it might be due to the race condition. Either way, assume that the journal is hot. This might be a false positive. But if it is, then the // automatic journal playback and recovery mechanism will deal with it under an EXCLUSIVE lock where we do not need to // worry so much with race conditions. pExists = 1; rc = RC.OK; } } } } } 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); }