Пример #1
0
 internal RC modifyPagePointer(Pgno iFrom, Pgno iTo, PTRMAP eType)
 {
     Debug.Assert(MutexEx.Held(this.Shared.Mutex));
     Debug.Assert(Pager.IsPageWriteable(this.DbPage));
     if (eType == PTRMAP.OVERFLOW2)
     {
         // The pointer is always the first 4 bytes of the page in this case.
         if (ConvertEx.Get4(this.Data) != iFrom)
         {
             return(SysEx.SQLITE_CORRUPT_BKPT());
         }
         ConvertEx.Put4L(this.Data, iTo);
     }
     else
     {
         var isInitOrig = this.HasInit;
         btreeInitPage();
         var nCell = this.Cells;
         int i;
         for (i = 0; i < nCell; i++)
         {
             var pCell = FindCell(i);
             if (eType == PTRMAP.OVERFLOW1)
             {
                 var info = new CellInfo();
                 btreeParseCellPtr(pCell, ref info);
                 if (info.iOverflow != 0)
                 {
                     if (iFrom == ConvertEx.Get4(this.Data, pCell + info.iOverflow))
                     {
                         ConvertEx.Put4(this.Data, pCell + info.iOverflow, (int)iTo);
                         break;
                     }
                 }
             }
             else
             {
                 if (ConvertEx.Get4(this.Data, pCell) == iFrom)
                 {
                     ConvertEx.Put4(this.Data, pCell, (int)iTo);
                     break;
                 }
             }
         }
         if (i == nCell)
         {
             if (eType != PTRMAP.BTREE || ConvertEx.Get4(this.Data, this.HeaderOffset + 8) != iFrom)
             {
                 return(SysEx.SQLITE_CORRUPT_BKPT());
             }
             ConvertEx.Put4L(this.Data, (uint)this.HeaderOffset + 8, iTo);
         }
         this.HasInit = isInitOrig;
     }
     return(RC.OK);
 }
Пример #2
0
        static void checkPtrmap(IntegrityCk check, Pid childID, PTRMAP type, Pid parentID, string context)
        {
            PTRMAP ptrmapType = 0;
            Pid ptrmapParentID = 0;
            RC rc = ptrmapGet(check.Bt, childID, ref ptrmapType, ref ptrmapParentID);
            if (rc != RC.OK)
            {
                if (rc == RC.NOMEM || rc == RC.IOERR_NOMEM) check.MallocFailed = true;
                checkAppendMsg(check, context, "Failed to read ptrmap key=%d", childID);
                return;
            }

            if (ptrmapType != type || ptrmapParentID != parentID)
                checkAppendMsg(check, context, "Bad ptr map entry key=%d expected=(%d,%d) got=(%d,%d)", childID, type, parentID, ptrmapType, ptrmapParentID);
        }
Пример #3
0
        static RC ptrmapGet(BtShared bt, Pid key, ref PTRMAP type, ref Pid id)
        {
            Debug.Assert(MutexEx.Held(bt.Mutex));

            var page = (IPage)new PgHdr(); // The pointer map page
            var ptrmapIdx = (Pid)PTRMAP_PAGENO(bt, key); // Pointer map page index
            var rc = bt.Pager.Acquire(ptrmapIdx, ref page, false);
            if (rc != RC.OK)
                return rc;
            var ptrmap = Pager.GetData(page); // Pointer map page data

            var offset = (int)PTRMAP_PTROFFSET(ptrmapIdx, key); // Offset of entry in pointer map
            if (offset < 0)
            {
                Pager.Unref(page);
                return SysEx.CORRUPT_BKPT();
            }
            Debug.Assert(offset <= (int)bt.UsableSize - 5);
            Debug.Assert(type != 0);
            type = (PTRMAP)ptrmap[offset];
            id = ConvertEx.Get4(ptrmap, offset + 1);

            Pager.Unref(page);
            if ((byte)type < 1 || (byte)type > 5) return SysEx.CORRUPT_BKPT();
            return RC.OK;
        }
Пример #4
0
        static void ptrmapPut(BtShared bt, Pid key, PTRMAP type, Pid parent, ref RC rcRef)
        {
            if (rcRef != RC.OK) return;

            Debug.Assert(MutexEx.Held(bt.Mutex));
            // The master-journal page number must never be used as a pointer map page
            Debug.Assert(!PTRMAP_ISPAGE(bt, PENDING_BYTE_PAGE(bt)));

            Debug.Assert(bt.AutoVacuum);
            if (key == 0)
            {
                rcRef = SysEx.CORRUPT_BKPT();
                return;
            }
            var ptrmapIdx = PTRMAP_PAGENO(bt, key); // The pointer map page number
            var page = (IPage)new PgHdr(); // The pointer map page
            var rc = bt.Pager.Acquire(ptrmapIdx, ref page, false);
            if (rc != RC.OK)
            {
                rcRef = rc;
                return;
            }
            var offset = (int)PTRMAP_PTROFFSET(ptrmapIdx, key); // Offset in pointer map page
            if (offset < 0)
            {
                rcRef = SysEx.CORRUPT_BKPT();
                goto ptrmap_exit;
            }
            Debug.Assert(offset <= (int)bt.UsableSize - 5);
            var ptrmap = Pager.GetData(page); // The pointer map page

            if (type != (PTRMAP)ptrmap[offset] || ConvertEx.Get4(ptrmap, offset + 1) != parent)
            {
                TRACE("PTRMAP_UPDATE: %d->(%d,%d)\n", key, type, parent);
                rcRef = rc = Pager.Write(page);
                if (rc == RC.OK)
                {
                    ptrmap[offset] = (byte)type;
                    ConvertEx.Put4(ptrmap, offset + 1, parent);
                }
            }

        ptrmap_exit:
            Pager.Unref(page);
        }
Пример #5
0
        static RC relocatePage(BtShared bt, MemPage page, PTRMAP type, Pid ptrPageID, Pid freePageID, bool isCommit)
        {
            Debug.Assert(type == PTRMAP.OVERFLOW2 || type == PTRMAP.OVERFLOW1 || type == PTRMAP.BTREE || type == PTRMAP.ROOTPAGE);
            Debug.Assert(MutexEx.Held(bt.Mutex));
            Debug.Assert(page.Bt == bt);

            // Move page iDbPage from its current location to page number iFreePage
            var lastID = page.ID;
            TRACE("AUTOVACUUM: Moving %d to free page %d (ptr page %d type %d)\n", lastID, freePageID, ptrPageID, type);
            Pager pager = bt.Pager;
            var rc = pager.Movepage(page.DBPage, freePageID, isCommit);
            if (rc != RC.OK)
                return rc;
            page.ID = freePageID;

            // If pDbPage was a btree-page, then it may have child pages and/or cells that point to overflow pages. The pointer map entries for all these
            // pages need to be changed.
            //
            // If pDbPage is an overflow page, then the first 4 bytes may store a pointer to a subsequent overflow page. If this is the case, then
            // the pointer map needs to be updated for the subsequent overflow page.
            if (type == PTRMAP.BTREE || type == PTRMAP.ROOTPAGE)
            {
                rc = setChildPtrmaps(page);
                if (rc != RC.OK)
                    return rc;
            }
            else
            {
                Pid nextOvfl = ConvertEx.Get4(page.Data);
                if (nextOvfl != 0)
                {
                    ptrmapPut(bt, nextOvfl, PTRMAP.OVERFLOW2, freePageID, ref rc);
                    if (rc != RC.OK)
                        return rc;
                }
            }

            // Fix the database pointer on page iPtrPage that pointed at iDbPage so that it points at iFreePage. Also fix the pointer map entry for iPtrPage.
            if (type != PTRMAP.ROOTPAGE)
            {
                var ptrPage = new MemPage(); // The page that contains a pointer to pDbPage
                rc = btreeGetPage(bt, ptrPageID, ref ptrPage, false);
                if (rc != RC.OK)
                    return rc;
                rc = Pager.Write(ptrPage.DBPage);
                if (rc != RC.OK)
                {
                    releasePage(ptrPage);
                    return rc;
                }
                rc = modifyPagePointer(ptrPage, lastID, freePageID, type);
                releasePage(ptrPage);
                if (rc == RC.OK)
                    ptrmapPut(bt, freePageID, type, ptrPageID, ref rc);
            }
            return rc;
        }
Пример #6
0
        static RC modifyPagePointer(MemPage page, Pid from, Pid to, PTRMAP type)
        {
            Debug.Assert(MutexEx.Held(page.Bt.Mutex));
            Debug.Assert(Pager.Iswriteable(page.DBPage));
            if (type == PTRMAP.OVERFLOW2)
            {
                // The pointer is always the first 4 bytes of the page in this case.
                if (ConvertEx.Get4(page.Data) != from)
                    return SysEx.CORRUPT_BKPT();
                ConvertEx.Put4(page.Data, to);
            }
            else
            {
                var isInitOrig = page.IsInit;

                btreeInitPage(page);
                ushort cells = page.Cells;

                uint i;
                for (i = 0U; i < cells; i++)
                {
                    uint cell_ = findCell(page, i);
                    if (type == PTRMAP.OVERFLOW1)
                    {
                        var info = new CellInfo();
                        btreeParseCellPtr(page, cell_, ref info);
                        if (info.Overflow != 0 &&
                            //cell + info.Overflow + 3 <= page.Data + page.MaskPage &&
                            from == ConvertEx.Get4(page.Data, cell_ + info.Overflow))
                        {
                            ConvertEx.Put4(page.Data, cell_ + info.Overflow, (int)to);
                            break;
                        }
                    }
                    else
                        if (ConvertEx.Get4(page.Data, cell_) == from)
                        {
                            ConvertEx.Put4(page.Data, cell_, (int)to);
                            break;
                        }
                }

                if (i == cells)
                {
                    if (type != PTRMAP.BTREE || ConvertEx.Get4(page.Data, page.HdrOffset + 8) != from)
                        return SysEx.CORRUPT_BKPT();
                    ConvertEx.Put4(page.Data, page.HdrOffset + 8, to);
                }

                page.IsInit = isInitOrig;
            }
            return RC.OK;
        }
Пример #7
0
        internal static RC incrVacuumStep(BtShared pBt, Pgno nFin, Pgno iLastPg)
        {
            Pgno nFreeList;           // Number of pages still on the free-list

            Debug.Assert(MutexEx.Held(pBt.Mutex));
            Debug.Assert(iLastPg > nFin);
            if (!PTRMAP_ISPAGE(pBt, iLastPg) && iLastPg != PENDING_BYTE_PAGE(pBt))
            {
                PTRMAP eType    = 0;
                Pgno   iPtrPage = 0;
                nFreeList = ConvertEx.Get4(pBt.Page1.Data, 36);
                if (nFreeList == 0)
                {
                    return(RC.DONE);
                }
                var rc = pBt.ptrmapGet(iLastPg, ref eType, ref iPtrPage);
                if (rc != RC.OK)
                {
                    return(rc);
                }
                if (eType == PTRMAP.ROOTPAGE)
                {
                    return(SysEx.SQLITE_CORRUPT_BKPT());
                }
                if (eType == PTRMAP.FREEPAGE)
                {
                    if (nFin == 0)
                    {
                        // Remove the page from the files free-list. This is not required if nFin is non-zero. In that case, the free-list will be
                        // truncated to zero after this function returns, so it doesn't matter if it still contains some garbage entries.
                        Pgno iFreePg = 0;
                        var  pFreePg = new MemPage();
                        rc = pBt.allocateBtreePage(ref pFreePg, ref iFreePg, iLastPg, 1);
                        if (rc != RC.OK)
                        {
                            return(rc);
                        }
                        Debug.Assert(iFreePg == iLastPg);
                        pFreePg.releasePage();
                    }
                }
                else
                {
                    Pgno iFreePg = 0; // Index of free page to move pLastPg to
                    var  pLastPg = new MemPage();
                    rc = pBt.btreeGetPage(iLastPg, ref pLastPg, 0);
                    if (rc != RC.OK)
                    {
                        return(rc);
                    }
                    // If nFin is zero, this loop runs exactly once and page pLastPg is swapped with the first free page pulled off the free list.
                    // On the other hand, if nFin is greater than zero, then keep looping until a free-page located within the first nFin pages of the file is found.
                    do
                    {
                        var pFreePg = new MemPage();
                        rc = pBt.allocateBtreePage(ref pFreePg, ref iFreePg, 0, 0);
                        if (rc != RC.OK)
                        {
                            pLastPg.releasePage();
                            return(rc);
                        }
                        pFreePg.releasePage();
                    } while (nFin != 0 && iFreePg > nFin);
                    Debug.Assert(iFreePg < iLastPg);
                    rc = Pager.Write(pLastPg.DbPage);
                    if (rc == RC.OK)
                    {
                        rc = relocatePage(pBt, pLastPg, eType, iPtrPage, iFreePg, (nFin != 0) ? 1 : 0);
                    }
                    pLastPg.releasePage();
                    if (rc != RC.OK)
                    {
                        return(rc);
                    }
                }
            }
            if (nFin == 0)
            {
                iLastPg--;
                while (iLastPg == PENDING_BYTE_PAGE(pBt) || PTRMAP_ISPAGE(pBt, iLastPg))
                {
                    if (PTRMAP_ISPAGE(pBt, iLastPg))
                    {
                        var pPg = new MemPage();
                        var rc  = pBt.btreeGetPage(iLastPg, ref pPg, 0);
                        if (rc != RC.OK)
                        {
                            return(rc);
                        }
                        rc = Pager.Write(pPg.DbPage);
                        pPg.releasePage();
                        if (rc != RC.OK)
                        {
                            return(rc);
                        }
                    }
                    iLastPg--;
                }
                pBt.Pager.TruncateImage(iLastPg);
                pBt.Pages = iLastPg;
            }
            return(RC.OK);
        }
Пример #8
0
        internal static RC relocatePage(BtShared pBt, MemPage pDbPage, PTRMAP eType, Pgno iPtrPage, Pgno iFreePage, int isCommit)
        {
            var pPtrPage = new MemPage();   // The page that contains a pointer to pDbPage
            var iDbPage  = pDbPage.ID;
            var pPager   = pBt.Pager;

            Debug.Assert(eType == PTRMAP.OVERFLOW2 || eType == PTRMAP.OVERFLOW1 || eType == PTRMAP.BTREE || eType == PTRMAP.ROOTPAGE);
            Debug.Assert(MutexEx.Held(pBt.Mutex));
            Debug.Assert(pDbPage.Shared == pBt);
            // Move page iDbPage from its current location to page number iFreePage
            Btree.TRACE("AUTOVACUUM: Moving %d to free page %d (ptr page %d type %d)\n", iDbPage, iFreePage, iPtrPage, eType);
            var rc = pPager.sqlite3PagerMovepage(pDbPage.DbPage, iFreePage, isCommit);

            if (rc != RC.OK)
            {
                return(rc);
            }
            pDbPage.ID = iFreePage;
            // If pDbPage was a btree-page, then it may have child pages and/or cells that point to overflow pages. The pointer map entries for all these
            // pages need to be changed.
            // If pDbPage is an overflow page, then the first 4 bytes may store a pointer to a subsequent overflow page. If this is the case, then
            // the pointer map needs to be updated for the subsequent overflow page.
            if (eType == PTRMAP.BTREE || eType == PTRMAP.ROOTPAGE)
            {
                rc = pDbPage.setChildPtrmaps();
                if (rc != RC.OK)
                {
                    return(rc);
                }
            }
            else
            {
                var nextOvfl = (Pgno)ConvertEx.Get4(pDbPage.Data);
                if (nextOvfl != 0)
                {
                    pBt.ptrmapPut(nextOvfl, PTRMAP.OVERFLOW2, iFreePage, ref rc);
                    if (rc != RC.OK)
                    {
                        return(rc);
                    }
                }
            }
            // Fix the database pointer on page iPtrPage that pointed at iDbPage so that it points at iFreePage. Also fix the pointer map entry for iPtrPage.
            if (eType != PTRMAP.ROOTPAGE)
            {
                rc = pBt.btreeGetPage(iPtrPage, ref pPtrPage, 0);
                if (rc != RC.OK)
                {
                    return(rc);
                }
                rc = Pager.Write(pPtrPage.DbPage);
                if (rc != RC.OK)
                {
                    pPtrPage.releasePage();
                    return(rc);
                }
                rc = pPtrPage.modifyPagePointer(iDbPage, iFreePage, eType);
                pPtrPage.releasePage();
                if (rc == RC.OK)
                {
                    pBt.ptrmapPut(iFreePage, eType, iPtrPage, ref rc);
                }
            }
            return(rc);
        }
Пример #9
0
 internal static RC relocatePage(BtShared pBt, MemPage pDbPage, PTRMAP eType, Pgno iPtrPage, Pgno iFreePage, int isCommit)
 {
     var pPtrPage = new MemPage();   // The page that contains a pointer to pDbPage
     var iDbPage = pDbPage.ID;
     var pPager = pBt.Pager;
     Debug.Assert(eType == PTRMAP.OVERFLOW2 || eType == PTRMAP.OVERFLOW1 || eType == PTRMAP.BTREE || eType == PTRMAP.ROOTPAGE);
     Debug.Assert(MutexEx.Held(pBt.Mutex));
     Debug.Assert(pDbPage.Shared == pBt);
     // Move page iDbPage from its current location to page number iFreePage
     Btree.TRACE("AUTOVACUUM: Moving %d to free page %d (ptr page %d type %d)\n", iDbPage, iFreePage, iPtrPage, eType);
     var rc = pPager.sqlite3PagerMovepage(pDbPage.DbPage, iFreePage, isCommit);
     if (rc != RC.OK)
         return rc;
     pDbPage.ID = iFreePage;
     // If pDbPage was a btree-page, then it may have child pages and/or cells that point to overflow pages. The pointer map entries for all these
     // pages need to be changed.
     // If pDbPage is an overflow page, then the first 4 bytes may store a pointer to a subsequent overflow page. If this is the case, then
     // the pointer map needs to be updated for the subsequent overflow page.
     if (eType == PTRMAP.BTREE || eType == PTRMAP.ROOTPAGE)
     {
         rc = pDbPage.setChildPtrmaps();
         if (rc != RC.OK)
             return rc;
     }
     else
     {
         var nextOvfl = (Pgno)ConvertEx.Get4(pDbPage.Data);
         if (nextOvfl != 0)
         {
             pBt.ptrmapPut(nextOvfl, PTRMAP.OVERFLOW2, iFreePage, ref rc);
             if (rc != RC.OK)
                 return rc;
         }
     }
     // Fix the database pointer on page iPtrPage that pointed at iDbPage so that it points at iFreePage. Also fix the pointer map entry for iPtrPage.
     if (eType != PTRMAP.ROOTPAGE)
     {
         rc = pBt.btreeGetPage(iPtrPage, ref pPtrPage, 0);
         if (rc != RC.OK)
             return rc;
         rc = Pager.Write(pPtrPage.DbPage);
         if (rc != RC.OK)
         {
             pPtrPage.releasePage();
             return rc;
         }
         rc = pPtrPage.modifyPagePointer(iDbPage, iFreePage, eType);
         pPtrPage.releasePage();
         if (rc == RC.OK)
             pBt.ptrmapPut(iFreePage, eType, iPtrPage, ref rc);
     }
     return rc;
 }
Пример #10
0
 internal RC modifyPagePointer(Pgno iFrom, Pgno iTo, PTRMAP eType)
 {
     Debug.Assert(MutexEx.Held(this.Shared.Mutex));
     Debug.Assert(Pager.IsPageWriteable(this.DbPage));
     if (eType == PTRMAP.OVERFLOW2)
     {
         // The pointer is always the first 4 bytes of the page in this case.
         if (ConvertEx.Get4(this.Data) != iFrom)
             return SysEx.SQLITE_CORRUPT_BKPT();
         ConvertEx.Put4L(this.Data, iTo);
     }
     else
     {
         var isInitOrig = this.HasInit;
         btreeInitPage();
         var nCell = this.Cells;
         int i;
         for (i = 0; i < nCell; i++)
         {
             var pCell = FindCell(i);
             if (eType == PTRMAP.OVERFLOW1)
             {
                 var info = new CellInfo();
                 btreeParseCellPtr(pCell, ref info);
                 if (info.iOverflow != 0)
                     if (iFrom == ConvertEx.Get4(this.Data, pCell, info.iOverflow))
                     {
                         ConvertEx.Put4(this.Data, pCell + info.iOverflow, (int)iTo);
                         break;
                     }
             }
             else
             {
                 if (ConvertEx.Get4(this.Data, pCell) == iFrom)
                 {
                     ConvertEx.Put4(this.Data, pCell, (int)iTo);
                     break;
                 }
             }
         }
         if (i == nCell)
         {
             if (eType != PTRMAP.BTREE || ConvertEx.Get4(this.Data, this.HeaderOffset + 8) != iFrom)
                 return SysEx.SQLITE_CORRUPT_BKPT();
             ConvertEx.Put4L(this.Data, this.HeaderOffset + 8, iTo);
         }
         this.HasInit = isInitOrig;
     }
     return RC.OK;
 }