示例#1
0
 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();
 }
示例#2
0
        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);
        }
示例#3
0
        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);
        }
示例#4
0
        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);
        }
示例#5
0
        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);
                }