Esempio n. 1
0
        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();
                }
            }
        }
Esempio n. 2
0
        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);
        }
Esempio n. 3
0
        internal RC allocateBtreePage(ref MemPage ppPage, ref Pgno pPgno, Pgno nearby, byte exact)
        {
            MemPage pTrunk     = null;
            MemPage pPrevTrunk = null;

            Debug.Assert(MutexEx.Held(this.Mutex));
            var pPage1 = this.Page1;
            var mxPage = btreePagecount();                // Total size of the database file
            var n      = ConvertEx.Get4(pPage1.Data, 36); // Number of pages on the freelist

            if (n >= mxPage)
            {
                return(SysEx.SQLITE_CORRUPT_BKPT());
            }
            RC rc;

            if (n > 0)
            {
                // There are pages on the freelist.  Reuse one of those pages.
                Pgno iTrunk;
                byte searchList = 0; // If the free-list must be searched for 'nearby'
                // If the 'exact' parameter was true and a query of the pointer-map shows that the page 'nearby' is somewhere on the free-list, then the entire-list will be searched for that page.
#if !SQLITE_OMIT_AUTOVACUUM
                if (exact != 0 && nearby <= mxPage)
                {
                    Debug.Assert(nearby > 0);
                    Debug.Assert(this.AutoVacuum);
                    PTRMAP eType  = 0;
                    uint   dummy0 = 0;
                    rc = ptrmapGet(nearby, ref eType, ref dummy0);
                    if (rc != RC.OK)
                    {
                        return(rc);
                    }
                    if (eType == PTRMAP.FREEPAGE)
                    {
                        searchList = 1;
                    }
                    pPgno = nearby;
                }
#endif
                // Decrement the free-list count by 1. Set iTrunk to the index of the first free-list trunk page. iPrevTrunk is initially 1.
                rc = Pager.Write(pPage1.DbPage);
                if (rc != RC.OK)
                {
                    return(rc);
                }
                ConvertEx.Put4(pPage1.Data, 36, n - 1);
                // The code within this loop is run only once if the 'searchList' variable is not true. Otherwise, it runs once for each trunk-page on the
                // free-list until the page 'nearby' is located.
                do
                {
                    pPrevTrunk = pTrunk;
                    iTrunk     = (pPrevTrunk != null ? ConvertEx.Get4(pPrevTrunk.Data, 0) : ConvertEx.Get4(pPage1.Data, 32));
                    rc         = (iTrunk > mxPage ? SysEx.SQLITE_CORRUPT_BKPT() : btreeGetPage(iTrunk, ref pTrunk, 0));
                    if (rc != RC.OK)
                    {
                        pTrunk = null;
                        goto end_allocate_page;
                    }
                    var k = ConvertEx.Get4(pTrunk.Data, 4); // # of leaves on this trunk page
                    if (k == 0 && searchList == 0)
                    {
                        // The trunk has no leaves and the list is not being searched. So extract the trunk page itself and use it as the newly allocated page
                        Debug.Assert(pPrevTrunk == null);
                        rc = Pager.Write(pTrunk.DbPage);
                        if (rc != RC.OK)
                        {
                            goto end_allocate_page;
                        }
                        pPgno = iTrunk;
                        Buffer.BlockCopy(pTrunk.Data, 0, pPage1.Data, 32, 4);
                        ppPage = pTrunk;
                        pTrunk = null;
                        Btree.TRACE("ALLOCATE: %d trunk - %d free pages left\n", pPgno, n - 1);
                    }
                    else if (k > (uint)(this.UsableSize / 4 - 2))
                    {
                        // Value of k is out of range. Database corruption
                        rc = SysEx.SQLITE_CORRUPT_BKPT();
                        goto end_allocate_page;
#if !SQLITE_OMIT_AUTOVACUUM
                    }
                    else if (searchList != 0 && nearby == iTrunk)
                    {
                        // The list is being searched and this trunk page is the page to allocate, regardless of whether it has leaves.
                        Debug.Assert(pPgno == iTrunk);
                        ppPage     = pTrunk;
                        searchList = 0;
                        rc         = Pager.Write(pTrunk.DbPage);
                        if (rc != RC.OK)
                        {
                            goto end_allocate_page;
                        }
                        if (k == 0)
                        {
                            if (pPrevTrunk == null)
                            {
                                pPage1.Data[32 + 0] = pTrunk.Data[0 + 0];
                                pPage1.Data[32 + 1] = pTrunk.Data[0 + 1];
                                pPage1.Data[32 + 2] = pTrunk.Data[0 + 2];
                                pPage1.Data[32 + 3] = pTrunk.Data[0 + 3];
                            }
                            else
                            {
                                rc = Pager.Write(pPrevTrunk.DbPage);
                                if (rc != RC.OK)
                                {
                                    goto end_allocate_page;
                                }
                                pPrevTrunk.Data[0 + 0] = pTrunk.Data[0 + 0];
                                pPrevTrunk.Data[0 + 1] = pTrunk.Data[0 + 1];
                                pPrevTrunk.Data[0 + 2] = pTrunk.Data[0 + 2];
                                pPrevTrunk.Data[0 + 3] = pTrunk.Data[0 + 3];
                            }
                        }
                        else
                        {
                            // The trunk page is required by the caller but it contains pointers to free-list leaves. The first leaf becomes a trunk page in this case.
                            var pNewTrunk = new MemPage();
                            var iNewTrunk = (Pgno)ConvertEx.Get4(pTrunk.Data, 8);
                            if (iNewTrunk > mxPage)
                            {
                                rc = SysEx.SQLITE_CORRUPT_BKPT();
                                goto end_allocate_page;
                            }
                            rc = btreeGetPage(iNewTrunk, ref pNewTrunk, 0);
                            if (rc != RC.OK)
                            {
                                goto end_allocate_page;
                            }
                            rc = Pager.Write(pNewTrunk.DbPage);
                            if (rc != RC.OK)
                            {
                                pNewTrunk.releasePage();
                                goto end_allocate_page;
                            }
                            pNewTrunk.Data[0 + 0] = pTrunk.Data[0 + 0];
                            pNewTrunk.Data[0 + 1] = pTrunk.Data[0 + 1];
                            pNewTrunk.Data[0 + 2] = pTrunk.Data[0 + 2];
                            pNewTrunk.Data[0 + 3] = pTrunk.Data[0 + 3];
                            ConvertEx.Put4(pNewTrunk.Data, 4, (uint)(k - 1));
                            Buffer.BlockCopy(pTrunk.Data, 12, pNewTrunk.Data, 8, (int)(k - 1) * 4);
                            pNewTrunk.releasePage();
                            if (pPrevTrunk == null)
                            {
                                Debug.Assert(Pager.IsPageWriteable(pPage1.DbPage));
                                ConvertEx.Put4(pPage1.Data, 32, iNewTrunk);
                            }
                            else
                            {
                                rc = Pager.Write(pPrevTrunk.DbPage);
                                if (rc != RC.OK)
                                {
                                    goto end_allocate_page;
                                }
                                ConvertEx.Put4(pPrevTrunk.Data, 0, iNewTrunk);
                            }
                        }
                        pTrunk = null;
                        Btree.TRACE("ALLOCATE: %d trunk - %d free pages left\n", pPgno, n - 1);
#endif
                    }
                    else if (k > 0)
                    {
                        // Extract a leaf from the trunk
                        uint closest;
                        var  aData = pTrunk.Data;
                        if (nearby > 0)
                        {
                            closest = 0;
                            var dist = Math.Abs((int)(ConvertEx.Get4(aData, 8) - nearby));
                            for (uint i = 1; i < k; i++)
                            {
                                int dist2 = Math.Abs((int)(ConvertEx.Get4(aData, 8 + i * 4) - nearby));
                                if (dist2 < dist)
                                {
                                    closest = i;
                                    dist    = dist2;
                                }
                            }
                        }
                        else
                        {
                            closest = 0;
                        }
                        //
                        var iPage = (Pgno)ConvertEx.Get4(aData, 8 + closest * 4);
                        if (iPage > mxPage)
                        {
                            rc = SysEx.SQLITE_CORRUPT_BKPT();
                            goto end_allocate_page;
                        }
                        if (searchList == 0 || iPage == nearby)
                        {
                            pPgno = iPage;
                            Btree.TRACE("ALLOCATE: %d was leaf %d of %d on trunk %d" + ": %d more free pages\n", pPgno, closest + 1, k, pTrunk.ID, n - 1);
                            rc = Pager.Write(pTrunk.DbPage);
                            if (rc != RC.OK)
                            {
                                goto end_allocate_page;
                            }
                            if (closest < k - 1)
                            {
                                Buffer.BlockCopy(aData, (int)(4 + k * 4), aData, 8 + (int)closest * 4, 4);
                            }
                            ConvertEx.Put4(aData, 4, (k - 1));
                            var noContent = (!btreeGetHasContent(pPgno) ? 1 : 0);
                            rc = btreeGetPage(pPgno, ref ppPage, noContent);
                            if (rc == RC.OK)
                            {
                                rc = Pager.Write((ppPage).DbPage);
                                if (rc != RC.OK)
                                {
                                    ppPage.releasePage();
                                }
                            }
                            searchList = 0;
                        }
                    }
                    pPrevTrunk.releasePage();
                    pPrevTrunk = null;
                } while (searchList != 0);
            }
            else
            {
                // There are no pages on the freelist, so create a new page at the end of the file
                rc = Pager.Write(this.Page1.DbPage);
                if (rc != RC.OK)
                {
                    return(rc);
                }
                this.Pages++;
                if (this.Pages == MemPage.PENDING_BYTE_PAGE(this))
                {
                    this.Pages++;
                }
#if !SQLITE_OMIT_AUTOVACUUM
                if (this.AutoVacuum && MemPage.PTRMAP_ISPAGE(this, this.Pages))
                {
                    // If pPgno refers to a pointer-map page, allocate two new pages at the end of the file instead of one. The first allocated page
                    // becomes a new pointer-map page, the second is used by the caller.
                    MemPage pPg = null;
                    Btree.TRACE("ALLOCATE: %d from end of file (pointer-map page)\n", pPgno);
                    Debug.Assert(this.Pages != MemPage.PENDING_BYTE_PAGE(this));
                    rc = btreeGetPage(this.Pages, ref pPg, 1);
                    if (rc == RC.OK)
                    {
                        rc = Pager.Write(pPg.DbPage);
                        pPg.releasePage();
                    }
                    if (rc != RC.OK)
                    {
                        return(rc);
                    }
                    this.Pages++;
                    if (this.Pages == MemPage.PENDING_BYTE_PAGE(this))
                    {
                        this.Pages++;
                    }
                }
#endif
                ConvertEx.Put4(this.Page1.Data, 28, this.Pages);
                pPgno = this.Pages;
                Debug.Assert(pPgno != MemPage.PENDING_BYTE_PAGE(this));
                rc = btreeGetPage(pPgno, ref ppPage, 1);
                if (rc != RC.OK)
                {
                    return(rc);
                }
                rc = Pager.Write((ppPage).DbPage);
                if (rc != RC.OK)
                {
                    ppPage.releasePage();
                }
                Btree.TRACE("ALLOCATE: %d from end of file\n", pPgno);
            }
            Debug.Assert(pPgno != MemPage.PENDING_BYTE_PAGE(this));

end_allocate_page:
            pTrunk.releasePage();
            pPrevTrunk.releasePage();
            if (rc == RC.OK)
            {
                if (Pager.GetPageRefCount((ppPage).DbPage) > 1)
                {
                    ppPage.releasePage();
                    return(SysEx.SQLITE_CORRUPT_BKPT());
                }
                (ppPage).HasInit = false;
            }
            else
            {
                ppPage = null;
            }
            Debug.Assert(rc != RC.OK || Pager.IsPageWriteable((ppPage).DbPage));
            return(rc);
        }