Esempio n. 1
0
        internal RC ptrmapGet(Pgno key, ref PTRMAP pEType, ref Pgno pPgno)
        {
            Debug.Assert(MutexEx.Held(this.Mutex));
            var iPtrmap = (int)MemPage.PTRMAP_PAGENO(this, key);
            var pDbPage = new PgHdr(); // The pointer map page
            var rc      = this.Pager.Get((Pgno)iPtrmap, ref pDbPage);

            if (rc != RC.OK)
            {
                return(rc);
            }
            var pPtrmap = Pager.sqlite3PagerGetData(pDbPage);// Pointer map page data
            var offset  = (int)MemPage.PTRMAP_PTROFFSET((Pgno)iPtrmap, key);

            if (offset < 0)
            {
                Pager.Unref(pDbPage);
                return(SysEx.SQLITE_CORRUPT_BKPT());
            }
            Debug.Assert(offset <= (int)this.UsableSize - 5);
            var v = pPtrmap[offset];

            if (v < 1 || v > 5)
            {
                return(SysEx.SQLITE_CORRUPT_BKPT());
            }
            pEType = (PTRMAP)v;
            pPgno  = ConvertEx.Get4(pPtrmap, offset + 1);
            Pager.Unref(pDbPage);
            return(RC.OK);
        }
Esempio n. 2
0
 internal void releasePage()
 {
     if (this != null)
     {
         Debug.Assert(this.Data != null);
         Debug.Assert(this.Shared != null);
         // TODO -- find out why corrupt9 & diskfull fail on this tests
         //Debug.Assert( Pager.sqlite3PagerGetExtra( pPage.pDbPage ) == pPage );
         //Debug.Assert( Pager.sqlite3PagerGetData( pPage.pDbPage ) == pPage.aData );
         Debug.Assert(MutexEx.Held(this.Shared.Mutex));
         Pager.Unref(this.DbPage);
     }
 }
Esempio n. 3
0
        internal void ptrmapPut(Pgno key, PTRMAP eType, Pgno parent, ref RC rRC)
        {
            if (rRC != RC.OK)
            {
                return;
            }
            Debug.Assert(MutexEx.Held(this.Mutex));
            // The master-journal page number must never be used as a pointer map page
            Debug.Assert(!MemPage.PTRMAP_ISPAGE(this, MemPage.PENDING_BYTE_PAGE(this)));
            Debug.Assert(this.AutoVacuum);
            if (key == 0)
            {
                rRC = SysEx.SQLITE_CORRUPT_BKPT();
                return;
            }
            var iPtrmap = MemPage.PTRMAP_PAGENO(this, key);
            var pDbPage = new PgHdr();  // The pointer map page
            var rc      = this.Pager.Get(iPtrmap, ref pDbPage);

            if (rc != RC.OK)
            {
                rRC = rc;
                return;
            }
            var offset = (int)MemPage.PTRMAP_PTROFFSET(iPtrmap, key);

            if (offset < 0)
            {
                rRC = SysEx.SQLITE_CORRUPT_BKPT();
                goto ptrmap_exit;
            }
            Debug.Assert(offset <= (int)this.UsableSize - 5);
            var pPtrmap = Pager.sqlite3PagerGetData(pDbPage); // The pointer map data

            if (eType != (PTRMAP)pPtrmap[offset] || ConvertEx.Get4(pPtrmap, offset + 1) != parent)
            {
                Btree.TRACE("PTRMAP_UPDATE: {0}->({1},{2})", key, eType, parent);
                rRC = rc = Pager.Write(pDbPage);
                if (rc == RC.OK)
                {
                    pPtrmap[offset] = (byte)eType;
                    ConvertEx.Put4L(pPtrmap, (uint)offset + 1, parent);
                }
            }
ptrmap_exit:
            Pager.Unref(pDbPage);
        }
Esempio n. 4
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. 5
0
        internal RC btreeCreateTable(ref int piTable, CREATETABLE createTabFlags)
        {
            var  pBt      = this.Shared;
            var  pRoot    = new MemPage();
            Pgno pgnoRoot = 0;
            int  ptfFlags;         // Page-type flage for the root page of new table
            RC   rc;

            Debug.Assert(sqlite3BtreeHoldsMutex());
            Debug.Assert(pBt.InTransaction == TRANS.WRITE);
            Debug.Assert(!pBt.ReadOnly);
#if SQLITE_OMIT_AUTOVACUUM
            rc = allocateBtreePage(pBt, ref pRoot, ref pgnoRoot, 1, 0);
            if (rc != SQLITE.OK)
            {
                return(rc);
            }
#else
            if (pBt.AutoVacuum)
            {
                Pgno pgnoMove  = 0;             // Move a page here to make room for the root-page
                var  pPageMove = new MemPage(); // The page to move to.
                // Creating a new table may probably require moving an existing database to make room for the new tables root page. In case this page turns
                // out to be an overflow page, delete all overflow page-map caches held by open cursors.
                invalidateAllOverflowCache(pBt);
                // Read the value of meta[3] from the database to determine where the root page of the new table should go. meta[3] is the largest root-page
                // created so far, so the new root-page is (meta[3]+1).
                GetMeta((int)META.LARGEST_ROOT_PAGE, ref pgnoRoot);
                pgnoRoot++;
                // The new root-page may not be allocated on a pointer-map page, or the PENDING_BYTE page.
                while (pgnoRoot == MemPage.PTRMAP_PAGENO(pBt, pgnoRoot) || pgnoRoot == MemPage.PENDING_BYTE_PAGE(pBt))
                {
                    pgnoRoot++;
                }
                Debug.Assert(pgnoRoot >= 3);
                // Allocate a page. The page that currently resides at pgnoRoot will be moved to the allocated page (unless the allocated page happens to reside at pgnoRoot).
                rc = pBt.allocateBtreePage(ref pPageMove, ref pgnoMove, pgnoRoot, 1);
                if (rc != RC.OK)
                {
                    return(rc);
                }
                if (pgnoMove != pgnoRoot)
                {
                    // pgnoRoot is the page that will be used for the root-page of the new table (assuming an error did not occur). But we were
                    // allocated pgnoMove. If required (i.e. if it was not allocated by extending the file), the current page at position pgnoMove
                    // is already journaled.
                    PTRMAP eType    = 0;
                    Pgno   iPtrPage = 0;
                    pPageMove.releasePage();
                    // Move the page currently at pgnoRoot to pgnoMove.
                    rc = pBt.btreeGetPage(pgnoRoot, ref pRoot, 0);
                    if (rc != RC.OK)
                    {
                        return(rc);
                    }
                    rc = pBt.ptrmapGet(pgnoRoot, ref eType, ref iPtrPage);
                    if (eType == PTRMAP.ROOTPAGE || eType == PTRMAP.FREEPAGE)
                    {
                        rc = SysEx.SQLITE_CORRUPT_BKPT();
                    }
                    if (rc != RC.OK)
                    {
                        pRoot.releasePage();
                        return(rc);
                    }
                    Debug.Assert(eType != PTRMAP.ROOTPAGE);
                    Debug.Assert(eType != PTRMAP.FREEPAGE);
                    rc = MemPage.relocatePage(pBt, pRoot, eType, iPtrPage, pgnoMove, 0);
                    pRoot.releasePage();
                    // Obtain the page at pgnoRoot
                    if (rc != RC.OK)
                    {
                        return(rc);
                    }
                    rc = pBt.btreeGetPage(pgnoRoot, ref pRoot, 0);
                    if (rc != RC.OK)
                    {
                        return(rc);
                    }
                    rc = Pager.Write(pRoot.DbPage);
                    if (rc != RC.OK)
                    {
                        pRoot.releasePage();
                        return(rc);
                    }
                }
                else
                {
                    pRoot = pPageMove;
                }
                // Update the pointer-map and meta-data with the new root-page number.
                pBt.ptrmapPut(pgnoRoot, PTRMAP.ROOTPAGE, 0, ref rc);
                if (rc != RC.OK)
                {
                    pRoot.releasePage();
                    return(rc);
                }
                // When the new root page was allocated, page 1 was made writable in order either to increase the database filesize, or to decrement the
                // freelist count.  Hence, the sqlite3BtreeUpdateMeta() call cannot fail.
                Debug.Assert(Pager.IsPageWriteable(pBt.Page1.DbPage));
                rc = SetMeta(4, pgnoRoot);
                if (Check.NEVER(rc != RC.OK))
                {
                    pRoot.releasePage();
                    return(rc);
                }
            }
            else
            {
                rc = pBt.allocateBtreePage(ref pRoot, ref pgnoRoot, 1, 0);
                if (rc != RC.OK)
                {
                    return(rc);
                }
            }
#endif
            Debug.Assert(Pager.IsPageWriteable(pRoot.DbPage));
            ptfFlags = ((createTabFlags & CREATETABLE.INTKEY) != 0 ? PTF_INTKEY | PTF_LEAFDATA | PTF_LEAF : PTF_ZERODATA | PTF_LEAF);
            pRoot.zeroPage(ptfFlags);
            Pager.Unref(pRoot.DbPage);
            Debug.Assert((pBt.OpenFlags & OPEN.SINGLE) == 0 || pgnoRoot == 2);
            piTable = (int)pgnoRoot;
            return(RC.OK);
        }
Esempio n. 6
0
        // was:fetchPayload
        private RC AccessPayload(uint offset, uint size, byte[] b, bool writeOperation)
        {
            var page = Pages[PageID];   // Btree page of current entry

            Debug.Assert(page != null);
            Debug.Assert(State == CursorState.VALID);
            Debug.Assert(PagesIndexs[PageID] < page.Cells);
            Debug.Assert(HoldsMutex());
            GetCellInfo();
            var payload = Info.Cells;
            var nKey    = (uint)(page.HasIntKey ? 0 : (int)Info.nKey);
            var shared  = Shared; // Btree this cursor belongs to

            if (Check.NEVER(offset + size > nKey + Info.nData) || Info.nLocal > shared.UsableSize)
            {
                // Trying to read or write past the end of the data is an error
                return(SysEx.SQLITE_CORRUPT_BKPT());
            }
            // Check if data must be read/written to/from the btree page itself.
            var  rc      = RC.OK;
            uint bOffset = 0;

            if (offset < Info.nLocal)
            {
                var a = (int)size;
                if (a + offset > Info.nLocal)
                {
                    a = (int)(Info.nLocal - offset);
                }
                rc       = CopyPayload(page.DbPage, payload, (uint)(offset + Info.CellID + Info.nHeader), b, bOffset, (uint)a, writeOperation);
                offset   = 0;
                bOffset += (uint)a;
                size    -= (uint)a;
            }
            else
            {
                offset -= Info.nLocal;
            }
            var iIdx = 0;

            if (rc == RC.OK && size > 0)
            {
                var ovflSize = (uint)(shared.UsableSize - 4);  // Bytes content per ovfl page
                var nextPage = (Pgno)ConvertEx.Get4(payload, Info.nLocal + Info.CellID + Info.nHeader);
#if !SQLITE_OMIT_INCRBLOB
                // If the isIncrblobHandle flag is set and the BtCursor.aOverflow[] has not been allocated, allocate it now. The array is sized at
                // one entry for each overflow page in the overflow chain. The page number of the first overflow page is stored in aOverflow[0],
                // etc. A value of 0 in the aOverflow[] array means "not yet known" (the cache is lazily populated).
                if (IsIncrblob && OverflowIDs == null)
                {
                    var nOvfl = (Info.nPayload - Info.nLocal + ovflSize - 1) / ovflSize;
                    OverflowIDs = new Pgno[nOvfl];
                }
                // If the overflow page-list cache has been allocated and the entry for the first required overflow page is valid, skip directly to it.
                if (OverflowIDs != null && OverflowIDs[offset / ovflSize] != 0)
                {
                    iIdx     = (int)(offset / ovflSize);
                    nextPage = OverflowIDs[iIdx];
                    offset   = (offset % ovflSize);
                }
#endif
                for (; rc == RC.OK && size > 0 && nextPage != 0; iIdx++)
                {
#if !SQLITE_OMIT_INCRBLOB
                    // If required, populate the overflow page-list cache.
                    if (OverflowIDs != null)
                    {
                        Debug.Assert(OverflowIDs[iIdx] == 0 || OverflowIDs[iIdx] == nextPage);
                        OverflowIDs[iIdx] = nextPage;
                    }
#endif
                    MemPage MemPageDummy = null;
                    if (offset >= ovflSize)
                    {
                        // The only reason to read this page is to obtain the page number for the next page in the overflow chain. The page
                        // data is not required. So first try to lookup the overflow page-list cache, if any, then fall back to the getOverflowPage() function.
#if !SQLITE_OMIT_INCRBLOB
                        if (OverflowIDs != null && OverflowIDs[iIdx + 1] != 0)
                        {
                            nextPage = OverflowIDs[iIdx + 1];
                        }
                        else
#endif
                        rc      = shared.getOverflowPage(nextPage, out MemPageDummy, out nextPage);
                        offset -= ovflSize;
                    }
                    else
                    {
                        // Need to read this page properly. It contains some of the range of data that is being read (eOp==null) or written (eOp!=null).
                        var pDbPage = new PgHdr();
                        var a       = (int)size;
                        rc = shared.Pager.Get(nextPage, ref pDbPage);
                        if (rc == RC.OK)
                        {
                            payload  = Pager.sqlite3PagerGetData(pDbPage);
                            nextPage = ConvertEx.Get4(payload);
                            if (a + offset > ovflSize)
                            {
                                a = (int)(ovflSize - offset);
                            }
                            rc = CopyPayload(pDbPage, payload, offset + 4, b, bOffset, (uint)a, writeOperation);
                            Pager.Unref(pDbPage);
                            offset   = 0;
                            size    -= (uint)a;
                            bOffset += (uint)a;
                        }
                    }
                }
            }
            if (rc == RC.OK && size > 0)
            {
                return(SysEx.SQLITE_CORRUPT_BKPT());
            }
            return(rc);
        }