Beispiel #1
0
        static int sqlite3_key(sqlite3 db, string pKey, int nKey)
        {
            CODEC_TRACE("sqlite3_key: entered db=%d pKey=%s nKey=%d\n", db, pKey, nKey);
            /* attach key if db and pKey are not null and nKey is > 0 */
            if (db != null && pKey != null)
            {
                sqlite3CodecAttach(db, 0, pKey, nKey); // operate only on the main db
                //
                // If we are reopening an existing database, redo the header information setup
                //
                BtShared pBt       = db.aDb[0].pBt.pBt;
                byte[]   zDbHeader = sqlite3MemMalloc(pBt.pageSize); // pBt.pPager.pCodec.buffer;
                sqlite3PagerReadFileheader(pBt.pPager, zDbHeader.Length, zDbHeader);
                if (sqlite3Get4byte(zDbHeader) > 0)                  // Existing Database, need to reset some values
                {
                    CODEC2(pBt.pPager, zDbHeader, 2, SQLITE_DECRYPT, ref zDbHeader);
                    byte nReserve = zDbHeader[20];
                    pBt.pageSizeFixed = true;
#if !SQLITE_OMIT_AUTOVACUUM
                    pBt.autoVacuum = sqlite3Get4byte(zDbHeader, 36 + 4 * 4) != 0;
                    pBt.incrVacuum = sqlite3Get4byte(zDbHeader, 36 + 7 * 4) != 0;
#endif
                    sqlite3PagerSetPagesize(pBt.pPager, ref pBt.pageSize, nReserve);
                    pBt.usableSize = (u16)(pBt.pageSize - nReserve);
                }

                return(SQLITE_OK);
            }
            return(SQLITE_ERROR);
        }
Beispiel #2
0
 static Pid finalDbSize(BtShared bt, Pid origs, Pid frees)
 {
     int entrys = (int)(bt.UsableSize / 5); // Number of entries on one ptrmap page
     Pid ptrmaps = (Pid)((frees - origs + PTRMAP_PAGENO(bt, origs) + entrys) / entrys); // Number of PtrMap pages to be freed
     Pid fins = origs - frees - ptrmaps; // Return value
     if (origs > PENDING_BYTE_PAGE(bt) && fins < PENDING_BYTE_PAGE(bt))
         fins--;
     while (PTRMAP_ISPAGE(bt, fins) || fins == PENDING_BYTE_PAGE(bt))
         fins--;
     return fins;
 }
Beispiel #3
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;
        }
Beispiel #4
0
 static void unlockBtreeIfUnused(BtShared bt)
 {
     Debug.Assert(MutexEx.Held(bt.Mutex));
     Debug.Assert(bt.Cursor == null || bt.InTransaction > TRANS.NONE);
     if (bt.InTransaction == TRANS.NONE && bt.Page1 != null)
     {
         Debug.Assert(bt.Page1.Data != null);
         Debug.Assert(bt.Pager.get_Refs() == 1);
         releasePage(bt.Page1);
         bt.Page1 = null;
     }
 }
Beispiel #5
0
 static void freeTempSpace(BtShared bt)
 {
     PCache.PageFree2(ref bt.TmpSpace);
     bt.TmpSpace = null;
 }
Beispiel #6
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;
        }
Beispiel #7
0
 static RC saveAllCursors(BtShared bt, Pid root, BtCursor except)
 {
     Debug.Assert(MutexEx.Held(bt.Mutex));
     Debug.Assert(except == null || except.Bt == bt);
     for (var p = bt.Cursor; p != null; p = p.Next)
     {
         if (p != except && (root == 0 || p.RootID == root) && p.State == CURSOR.VALID)
         {
             var rc = saveCursorPosition(p);
             if (rc != RC.OK)
                 return rc;
         }
     }
     return RC.OK;
 }
Beispiel #8
0
 static RC btreeSetHasContent(BtShared bt, Pid id)
 {
     var rc = RC.OK;
     if (bt.HasContent == null)
     {
         Debug.Assert(id <= bt.Pages);
         bt.HasContent = new Bitvec(bt.Pages);
     }
     if (rc == RC.OK && id <= bt.HasContent.Length)
         rc = bt.HasContent.Set(id);
     return rc;
 }
Beispiel #9
0
 static bool PTRMAP_ISPAGE(BtShared bt, Pid id)
 {
     return(PTRMAP_PAGENO((bt), (id)) == (id));
 }
Beispiel #10
0
 static Pid PTRMAP_PAGENO(BtShared bt, Pid id)
 {
     return(ptrmapPageno(bt, id));
 }
Beispiel #11
0
 public static Pid PENDING_BYTE_PAGE(BtShared bt)
 {
     return(Pager.MJ_PID(bt.Pager));
 }
Beispiel #12
0
 static ushort MX_CELL(BtShared bt)
 {
     return((ushort)((bt.PageSize - 8) / 6));
 }
Beispiel #13
0
 static ushort MX_CELL_SIZE(BtShared bt)
 {
     return((ushort)(bt.PageSize - 8));
 }
Beispiel #14
0
 //#define PTRMAP_ISPAGE(pBt, pgno) (PTRMAP_PAGENO((pBt),(pgno))==(pgno))
 private static bool PTRMAP_ISPAGE(BtShared pBt, uint pgno)
 {
     return(PTRMAP_PAGENO(pBt, pgno) == pgno);
 }
Beispiel #15
0
 static int countWriteCursors(BtShared bt)
 {
     int r = 0;
     for (var cur = bt.Cursor; cur != null; cur = cur.Next)
         if (cur.WrFlag && cur.State != CURSOR.FAULT) r++;
     return r;
 }
Beispiel #16
0
 static void invalidateAllOverflowCache(BtShared bt)
 {
     Debug.Assert(MutexEx.Held(bt.Mutex));
     for (var p = bt.Cursor; p != null; p = p.Next)
         invalidateOverflowCache(p);
 }
Beispiel #17
0
        /*
        ** 2004 April 6
        **
        ** The author disclaims copyright to this source code.  In place of
        ** a legal notice, here is a blessing:
        **
        **    May you do good and not evil.
        **    May you find forgiveness for yourself and forgive others.
        **    May you share freely, never taking more than you give.
        **
        *************************************************************************
        **
        ** This file implements a external (disk-based) database using BTrees.
        ** For a detailed discussion of BTrees, refer to
        **
        **     Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
        **     "Sorting And Searching", pages 473-480. Addison-Wesley
        **     Publishing Company, Reading, Massachusetts.
        **
        ** The basic idea is that each page of the file contains N database
        ** entries and N+1 pointers to subpages.
        **
        **   ----------------------------------------------------------------
        **   |  Ptr(0) | Key(0) | Ptr(1) | Key(1) | ... | Key(N-1) | Ptr(N) |
        **   ----------------------------------------------------------------
        **
        ** All of the keys on the page that Ptr(0) points to have values less
        ** than Key(0).  All of the keys on page Ptr(1) and its subpages have
        ** values greater than Key(0) and less than Key(1).  All of the keys
        ** on Ptr(N) and its subpages have values greater than Key(N-1).  And
        ** so forth.
        **
        ** Finding a particular key requires reading O(log(M)) pages from the
        ** disk where M is the number of entries in the tree.
        **
        ** In this implementation, a single file can hold one or more separate
        ** BTrees.  Each BTree is identified by the index of its root page.  The
        ** key and data for any entry are combined to form the "payload".  A
        ** fixed amount of payload can be carried directly on the database
        ** page.  If the payload is larger than the preset amount then surplus
        ** bytes are stored on overflow pages.  The payload for an entry
        ** and the preceding pointer are combined to form a "Cell".  Each
        ** page has a small header which contains the Ptr(N) pointer and other
        ** information such as the size of key and data.
        **
        ** FORMAT DETAILS
        **
        ** The file is divided into pages.  The first page is called page 1,
        ** the second is page 2, and so forth.  A page number of zero indicates
        ** "no such page".  The page size can be any power of 2 between 512 and 65536.
        ** Each page can be either a btree page, a freelist page, an overflow
        ** page, or a pointer-map page.
        **
        ** The first page is always a btree page.  The first 100 bytes of the first
        ** page contain a special header (the "file header") that describes the file.
        ** The format of the file header is as follows:
        **
        **   OFFSET   SIZE    DESCRIPTION
        **      0      16     Header string: "SQLite format 3\000"
        **     16       2     Page size in bytes.
        **     18       1     File format write version
        **     19       1     File format read version
        **     20       1     Bytes of unused space at the end of each page
        **     21       1     Max embedded payload fraction
        **     22       1     Min embedded payload fraction
        **     23       1     Min leaf payload fraction
        **     24       4     File change counter
        **     28       4     Reserved for future use
        **     32       4     First freelist page
        **     36       4     Number of freelist pages in the file
        **     40      60     15 4-byte meta values passed to higher layers
        **
        **     40       4     Schema cookie
        **     44       4     File format of schema layer
        **     48       4     Size of page cache
        **     52       4     Largest root-page (auto/incr_vacuum)
        **     56       4     1=UTF-8 2=UTF16le 3=UTF16be
        **     60       4     User version
        **     64       4     Incremental vacuum mode
        **     68       4     unused
        **     72       4     unused
        **     76       4     unused
        **
        ** All of the integer values are big-endian (most significant byte first).
        **
        ** The file change counter is incremented when the database is changed
        ** This counter allows other processes to know when the file has changed
        ** and thus when they need to flush their cache.
        **
        ** The max embedded payload fraction is the amount of the total usable
        ** space in a page that can be consumed by a single cell for standard
        ** B-tree (non-LEAFDATA) tables.  A value of 255 means 100%.  The default
        ** is to limit the maximum cell size so that at least 4 cells will fit
        ** on one page.  Thus the default max embedded payload fraction is 64.
        **
        ** If the payload for a cell is larger than the max payload, then extra
        ** payload is spilled to overflow pages.  Once an overflow page is allocated,
        ** as many bytes as possible are moved into the overflow pages without letting
        ** the cell size drop below the min embedded payload fraction.
        **
        ** The min leaf payload fraction is like the min embedded payload fraction
        ** except that it applies to leaf nodes in a LEAFDATA tree.  The maximum
        ** payload fraction for a LEAFDATA tree is always 100% (or 255) and it
        ** not specified in the header.
        **
        ** Each btree pages is divided into three sections:  The header, the
        ** cell pointer array, and the cell content area.  Page 1 also has a 100-byte
        ** file header that occurs before the page header.
        **
        **      |----------------|
        **      | file header    |   100 bytes.  Page 1 only.
        **      |----------------|
        **      | page header    |   8 bytes for leaves.  12 bytes for interior nodes
        **      |----------------|
        **      | cell pointer   |   |  2 bytes per cell.  Sorted order.
        **      | array          |   |  Grows downward
        **      |                |   v
        **      |----------------|
        **      | unallocated    |
        **      | space          |
        **      |----------------|   ^  Grows upwards
        **      | cell content   |   |  Arbitrary order interspersed with freeblocks.
        **      | area           |   |  and free space fragments.
        **      |----------------|
        **
        ** The page headers looks like this:
        **
        **   OFFSET   SIZE     DESCRIPTION
        **      0       1      Flags. 1: intkey, 2: zerodata, 4: leafdata, 8: leaf
        **      1       2      byte offset to the first freeblock
        **      3       2      number of cells on this page
        **      5       2      first byte of the cell content area
        **      7       1      number of fragmented free bytes
        **      8       4      Right child (the Ptr(N) value).  Omitted on leaves.
        **
        ** The flags define the format of this btree page.  The leaf flag means that
        ** this page has no children.  The zerodata flag means that this page carries
        ** only keys and no data.  The intkey flag means that the key is a integer
        ** which is stored in the key size entry of the cell header rather than in
        ** the payload area.
        **
        ** The cell pointer array begins on the first byte after the page header.
        ** The cell pointer array contains zero or more 2-byte numbers which are
        ** offsets from the beginning of the page to the cell content in the cell
        ** content area.  The cell pointers occur in sorted order.  The system strives
        ** to keep free space after the last cell pointer so that new cells can
        ** be easily added without having to defragment the page.
        **
        ** Cell content is stored at the very end of the page and grows toward the
        ** beginning of the page.
        **
        ** Unused space within the cell content area is collected into a linked list of
        ** freeblocks.  Each freeblock is at least 4 bytes in size.  The byte offset
        ** to the first freeblock is given in the header.  Freeblocks occur in
        ** increasing order.  Because a freeblock must be at least 4 bytes in size,
        ** any group of 3 or fewer unused bytes in the cell content area cannot
        ** exist on the freeblock chain.  A group of 3 or fewer free bytes is called
        ** a fragment.  The total number of bytes in all fragments is recorded.
        ** in the page header at offset 7.
        **
        **    SIZE    DESCRIPTION
        **      2     Byte offset of the next freeblock
        **      2     Bytes in this freeblock
        **
        ** Cells are of variable length.  Cells are stored in the cell content area at
        ** the end of the page.  Pointers to the cells are in the cell pointer array
        ** that immediately follows the page header.  Cells is not necessarily
        ** contiguous or in order, but cell pointers are contiguous and in order.
        **
        ** Cell content makes use of variable length integers.  A variable
        ** length integer is 1 to 9 bytes where the lower 7 bits of each
        ** byte are used.  The integer consists of all bytes that have bit 8 set and
        ** the first byte with bit 8 clear.  The most significant byte of the integer
        ** appears first.  A variable-length integer may not be more than 9 bytes long.
        ** As a special case, all 8 bytes of the 9th byte are used as data.  This
        ** allows a 64-bit integer to be encoded in 9 bytes.
        **
        **    0x00                      becomes  0x00000000
        **    0x7f                      becomes  0x0000007f
        **    0x81 0x00                 becomes  0x00000080
        **    0x82 0x00                 becomes  0x00000100
        **    0x80 0x7f                 becomes  0x0000007f
        **    0x8a 0x91 0xd1 0xac 0x78  becomes  0x12345678
        **    0x81 0x81 0x81 0x81 0x01  becomes  0x10204081
        **
        ** Variable length integers are used for rowids and to hold the number of
        ** bytes of key and data in a btree cell.
        **
        ** The content of a cell looks like this:
        **
        **    SIZE    DESCRIPTION
        **      4     Page number of the left child. Omitted if leaf flag is set.
        **     var    Number of bytes of data. Omitted if the zerodata flag is set.
        **     var    Number of bytes of key. Or the key itself if intkey flag is set.
        **      *     Payload
        **      4     First page of the overflow chain.  Omitted if no overflow
        **
        ** Overflow pages form a linked list.  Each page except the last is completely
        ** filled with data (pagesize - 4 bytes).  The last page can have as little
        ** as 1 byte of data.
        **
        **    SIZE    DESCRIPTION
        **      4     Page number of next overflow page
        **      *     Data
        **
        ** Freelist pages come in two subtypes: trunk pages and leaf pages.  The
        ** file header points to the first in a linked list of trunk page.  Each trunk
        ** page points to multiple leaf pages.  The content of a leaf page is
        ** unspecified.  A trunk page looks like this:
        **
        **    SIZE    DESCRIPTION
        **      4     Page number of next trunk page
        **      4     Number of leaf pointers on this page
        **      *     zero or more pages numbers of leaves
        *************************************************************************
        **  Included in SQLite3 port to C#-SQLite;  2008 Noah B Hart
        **  C#-SQLite is an independent reimplementation of the SQLite software library
        **
        **  SQLITE_SOURCE_ID: 2011-05-19 13:26:54 ed1da510a239ea767a01dc332b667119fa3c908e
        **
        *************************************************************************
        */
        //#include "sqliteInt.h"

        /* The following value is the maximum cell size assuming a maximum page
        ** size give above.
        */

        //#define MX_CELL_SIZE(pBt)  ((int)(pBt->pageSize-8))
        private static int MX_CELL_SIZE(BtShared pBt)
        {
            return((int)(pBt.pageSize - 8));
        }
Beispiel #18
0
 static void btreeClearHasContent(BtShared bt)
 {
     Bitvec.Destroy(ref bt.HasContent);
     bt.HasContent = null;
 }
Beispiel #19
0
        /*
        ** 2004 April 6
        **
        ** The author disclaims copyright to this source code.  In place of
        ** a legal notice, here is a blessing:
        **
        **    May you do good and not evil.
        **    May you find forgiveness for yourself and forgive others.
        **    May you share freely, never taking more than you give.
        **
        *************************************************************************
        ** $Id: btreeInt.h,v 1.52 2009/07/15 17:25:46 drh Exp $
        **
        *************************************************************************
        **  Included in SQLite3 port to C#-SQLite;  2008 Noah B Hart
        **  C#-SQLite is an independent reimplementation of the SQLite software library
        **
        **  $Header$
        *************************************************************************
        **
        ** This file implements a external (disk-based) database using BTrees.
        ** For a detailed discussion of BTrees, refer to
        **
        **     Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
        **     "Sorting And Searching", pages 473-480. Addison-Wesley
        **     Publishing Company, Reading, Massachusetts.
        **
        ** The basic idea is that each page of the file contains N database
        ** entries and N+1 pointers to subpages.
        **
        **   ----------------------------------------------------------------
        **   |  Ptr(0) | Key(0) | Ptr(1) | Key(1) | ... | Key(N-1) | Ptr(N) |
        **   ----------------------------------------------------------------
        **
        ** All of the keys on the page that Ptr(0) points to have values less
        ** than Key(0).  All of the keys on page Ptr(1) and its subpages have
        ** values greater than Key(0) and less than Key(1).  All of the keys
        ** on Ptr(N) and its subpages have values greater than Key(N-1).  And
        ** so forth.
        **
        ** Finding a particular key requires reading O(log(M)) pages from the
        ** disk where M is the number of entries in the tree.
        **
        ** In this implementation, a single file can hold one or more separate
        ** BTrees.  Each BTree is identified by the index of its root page.  The
        ** key and data for any entry are combined to form the "payload".  A
        ** fixed amount of payload can be carried directly on the database
        ** page.  If the payload is larger than the preset amount then surplus
        ** bytes are stored on overflow pages.  The payload for an entry
        ** and the preceding pointer are combined to form a "Cell".  Each
        ** page has a small header which contains the Ptr(N) pointer and other
        ** information such as the size of key and data.
        **
        ** FORMAT DETAILS
        **
        ** The file is divided into pages.  The first page is called page 1,
        ** the second is page 2, and so forth.  A page number of zero indicates
        ** "no such page".  The page size can be anything between 512 and 65536.
        ** Each page can be either a btree page, a freelist page or an overflow
        ** page.
        **
        ** The first page is always a btree page.  The first 100 bytes of the first
        ** page contain a special header (the "file header") that describes the file.
        ** The format of the file header is as follows:
        **
        **   OFFSET   SIZE    DESCRIPTION
        **      0      16     Header string: "SQLite format 3\000"
        **     16       2     Page size in bytes.
        **     18       1     File format write version
        **     19       1     File format read version
        **     20       1     Bytes of unused space at the end of each page
        **     21       1     Max embedded payload fraction
        **     22       1     Min embedded payload fraction
        **     23       1     Min leaf payload fraction
        **     24       4     File change counter
        **     28       4     Reserved for future use
        **     32       4     First freelist page
        **     36       4     Number of freelist pages in the file
        **     40      60     15 4-byte meta values passed to higher layers
        **
        **     40       4     Schema cookie
        **     44       4     File format of schema layer
        **     48       4     Size of page cache
        **     52       4     Largest root-page (auto/incr_vacuum)
        **     56       4     1=UTF-8 2=UTF16le 3=UTF16be
        **     60       4     User version
        **     64       4     Incremental vacuum mode
        **     68       4     unused
        **     72       4     unused
        **     76       4     unused
        **
        ** All of the integer values are big-endian (most significant byte first).
        **
        ** The file change counter is incremented when the database is changed
        ** This counter allows other processes to know when the file has changed
        ** and thus when they need to flush their cache.
        **
        ** The max embedded payload fraction is the amount of the total usable
        ** space in a page that can be consumed by a single cell for standard
        ** B-tree (non-LEAFDATA) tables.  A value of 255 means 100%.  The default
        ** is to limit the maximum cell size so that at least 4 cells will fit
        ** on one page.  Thus the default max embedded payload fraction is 64.
        **
        ** If the payload for a cell is larger than the max payload, then extra
        ** payload is spilled to overflow pages.  Once an overflow page is allocated,
        ** as many bytes as possible are moved into the overflow pages without letting
        ** the cell size drop below the min embedded payload fraction.
        **
        ** The min leaf payload fraction is like the min embedded payload fraction
        ** except that it applies to leaf nodes in a LEAFDATA tree.  The maximum
        ** payload fraction for a LEAFDATA tree is always 100% (or 255) and it
        ** not specified in the header.
        **
        ** Each btree pages is divided into three sections:  The header, the
        ** cell pointer array, and the cell content area.  Page 1 also has a 100-byte
        ** file header that occurs before the page header.
        **
        **      |----------------|
        **      | file header    |   100 bytes.  Page 1 only.
        **      |----------------|
        **      | page header    |   8 bytes for leaves.  12 bytes for interior nodes
        **      |----------------|
        **      | cell pointer   |   |  2 bytes per cell.  Sorted order.
        **      | array          |   |  Grows downward
        **      |                |   v
        **      |----------------|
        **      | unallocated    |
        **      | space          |
        **      |----------------|   ^  Grows upwards
        **      | cell content   |   |  Arbitrary order interspersed with freeblocks.
        **      | area           |   |  and free space fragments.
        **      |----------------|
        **
        ** The page headers looks like this:
        **
        **   OFFSET   SIZE     DESCRIPTION
        **      0       1      Flags. 1: intkey, 2: zerodata, 4: leafdata, 8: leaf
        **      1       2      byte offset to the first freeblock
        **      3       2      number of cells on this page
        **      5       2      first byte of the cell content area
        **      7       1      number of fragmented free bytes
        **      8       4      Right child (the Ptr(N) value).  Omitted on leaves.
        **
        ** The flags define the format of this btree page.  The leaf flag means that
        ** this page has no children.  The zerodata flag means that this page carries
        ** only keys and no data.  The intkey flag means that the key is a integer
        ** which is stored in the key size entry of the cell header rather than in
        ** the payload area.
        **
        ** The cell pointer array begins on the first byte after the page header.
        ** The cell pointer array contains zero or more 2-byte numbers which are
        ** offsets from the beginning of the page to the cell content in the cell
        ** content area.  The cell pointers occur in sorted order.  The system strives
        ** to keep free space after the last cell pointer so that new cells can
        ** be easily added without having to defragment the page.
        **
        ** Cell content is stored at the very end of the page and grows toward the
        ** beginning of the page.
        **
        ** Unused space within the cell content area is collected into a linked list of
        ** freeblocks.  Each freeblock is at least 4 bytes in size.  The byte offset
        ** to the first freeblock is given in the header.  Freeblocks occur in
        ** increasing order.  Because a freeblock must be at least 4 bytes in size,
        ** any group of 3 or fewer unused bytes in the cell content area cannot
        ** exist on the freeblock chain.  A group of 3 or fewer free bytes is called
        ** a fragment.  The total number of bytes in all fragments is recorded.
        ** in the page header at offset 7.
        **
        **    SIZE    DESCRIPTION
        **      2     Byte offset of the next freeblock
        **      2     Bytes in this freeblock
        **
        ** Cells are of variable length.  Cells are stored in the cell content area at
        ** the end of the page.  Pointers to the cells are in the cell pointer array
        ** that immediately follows the page header.  Cells is not necessarily
        ** contiguous or in order, but cell pointers are contiguous and in order.
        **
        ** Cell content makes use of variable length integers.  A variable
        ** length integer is 1 to 9 bytes where the lower 7 bits of each
        ** byte are used.  The integer consists of all bytes that have bit 8 set and
        ** the first byte with bit 8 clear.  The most significant byte of the integer
        ** appears first.  A variable-length integer may not be more than 9 bytes long.
        ** As a special case, all 8 bytes of the 9th byte are used as data.  This
        ** allows a 64-bit integer to be encoded in 9 bytes.
        **
        **    0x00                      becomes  0x00000000
        **    0x7f                      becomes  0x0000007f
        **    0x81 0x00                 becomes  0x00000080
        **    0x82 0x00                 becomes  0x00000100
        **    0x80 0x7f                 becomes  0x0000007f
        **    0x8a 0x91 0xd1 0xac 0x78  becomes  0x12345678
        **    0x81 0x81 0x81 0x81 0x01  becomes  0x10204081
        **
        ** Variable length integers are used for rowids and to hold the number of
        ** bytes of key and data in a btree cell.
        **
        ** The content of a cell looks like this:
        **
        **    SIZE    DESCRIPTION
        **      4     Page number of the left child. Omitted if leaf flag is set.
        **     var    Number of bytes of data. Omitted if the zerodata flag is set.
        **     var    Number of bytes of key. Or the key itself if intkey flag is set.
        **      *     Payload
        **      4     First page of the overflow chain.  Omitted if no overflow
        **
        ** Overflow pages form a linked list.  Each page except the last is completely
        ** filled with data (pagesize - 4 bytes).  The last page can have as little
        ** as 1 byte of data.
        **
        **    SIZE    DESCRIPTION
        **      4     Page number of next overflow page
        **      *     Data
        **
        ** Freelist pages come in two subtypes: trunk pages and leaf pages.  The
        ** file header points to the first in a linked list of trunk page.  Each trunk
        ** page points to multiple leaf pages.  The content of a leaf page is
        ** unspecified.  A trunk page looks like this:
        **
        **    SIZE    DESCRIPTION
        **      4     Page number of next trunk page
        **      4     Number of leaf pointers on this page
        **      *     zero or more pages numbers of leaves
        */
        //#include "sqliteInt.h"

        /* The following value is the maximum cell size assuming a maximum page
        ** size give above.
        */
        //#define MX_CELL_SIZE(pBt)  (pBt.pageSize-8)
        static int MX_CELL_SIZE(BtShared pBt)
        {
            return(pBt.pageSize - 8);
        }
Beispiel #20
0
 static Pid ptrmapPageno(BtShared bt, Pid id)
 {
     Debug.Assert(MutexEx.Held(bt.Mutex));
     if (id < 2) return 0;
     var pagesPerMapPage = (int)(bt.UsableSize / 5 + 1);
     var ptrMap = (Pid)((id - 2) / pagesPerMapPage);
     var ret = (Pid)(ptrMap * pagesPerMapPage) + 2;
     if (ret == PENDING_BYTE_PAGE(bt))
         ret++;
     return ret;
 }
Beispiel #21
0
 /* The maximum number of cells on a single page of the database.  This
 ** assumes a minimum cell size of 6 bytes  (4 bytes for the cell itself
 ** plus 2 bytes for the index to the cell in the page header).  Such
 ** small cells will be rare, but they are possible.
 */
 //#define MX_CELL(pBt) ((pBt.pageSize-8)/6)
 static int MX_CELL(BtShared pBt)
 {
     return((pBt.pageSize - 8) / 6);
 }
Beispiel #22
0
 /*
 ** These macros define the location of the pointer-map entry for a
 ** database page. The first argument to each is the number of usable
 ** bytes on each page of the database (often 1024). The second is the
 ** page number to look up in the pointer map.
 **
 ** PTRMAP_PAGENO returns the database page number of the pointer-map
 ** page that stores the required pointer. PTRMAP_PTROFFSET returns
 ** the offset of the requested map entry.
 **
 ** If the pgno argument passed to PTRMAP_PAGENO is a pointer-map page,
 ** then pgno is returned. So (pgno==PTRMAP_PAGENO(pgsz, pgno)) can be
 ** used to test if pgno is a pointer-map page. PTRMAP_ISPAGE implements
 ** this test.
 */
 //#define PTRMAP_PAGENO(pBt, pgno) ptrmapPageno(pBt, pgno)
 private static uint PTRMAP_PAGENO(BtShared pBt, uint pgno)
 {
     return(ptrmapPageno(pBt, pgno));
 }
Beispiel #23
0
 /*
 ** The database page the PENDING_BYTE occupies. This page is never used.
 */
 //# define PENDING_BYTE_PAGE(pBt) PAGER_MJ_PGNO(pBt)
 // TODO -- Convert PENDING_BYTE_PAGE to inline
 static u32 PENDING_BYTE_PAGE(BtShared pBt)
 {
     return((u32)PAGER_MJ_PGNO(pBt.pPager));
 }
Beispiel #24
0
        static RC lockBtree(BtShared bt)
        {
            Debug.Assert(MutexEx.Held(bt.Mutex));
            Debug.Assert(bt.Page1 == null);
            var rc = bt.Pager.SharedLock();
            if (rc != RC.OK) return rc;
            MemPage page1 = null; // Page 1 of the database file
            rc = btreeGetPage(bt, 1, ref page1, false);
            if (rc != RC.OK) return rc;

            // Do some checking to help insure the file we opened really is a valid database file. 
            Pid pagesHeader; // Number of pages in the database according to hdr
            Pid pages = pagesHeader = ConvertEx.Get4(page1.Data, 28); // Number of pages in the database
            Pid pagesFile = 0; // Number of pages in the database file
            bt.Pager.Pages(out pagesFile);
            if (pages == 0 || C.memcmp(page1.Data, 24, page1.Data, 92, 4) != 0)
                pages = pagesFile;
            if (pages > 0)
            {
                var page1Data = page1.Data;
                rc = RC.NOTADB;
                if (C.memcmp(page1Data, _magicHeader, 16) != 0)
                    goto page1_init_failed;

#if OMIT_WAL
                if (page1Data[18] > 1)
                    bt.BtsFlags |= BTS.READ_ONLY;
                if (page1Data[19] > 1)
                    goto page1_init_failed;
#else
                if (page1Data[18] > 2)
                    bt.BtsFlags |= BTS.READ_ONLY;
                if (page1Data[19] > 2)
                    goto page1_init_failed;

                // return SQLITE_OK and return without populating BtShared.pPage1. The caller detects this and calls this function again. This is
                // required as the version of page 1 currently in the page1 buffer may not be the latest version - there may be a newer one in the log file.
                if (page1Data[19] == 2 && (bt.BtsFlags & BTS.NO_WAL) == 0)
                {
                    int isOpen = 0;
                    rc = bt.Pager.OpenWal(ref isOpen);
                    if (rc != RC.OK)
                        goto page1_init_failed;
                    else if (isOpen == 0)
                    {
                        releasePage(page1);
                        return RC.OK;
                    }
                    rc = RC.NOTADB;
                }
#endif

                // The maximum embedded fraction must be exactly 25%.  And the minimum embedded fraction must be 12.5% for both leaf-data and non-leaf-data.
                // The original design allowed these amounts to vary, but as of version 3.6.0, we require them to be fixed.
                if (C.memcmp(page1Data, 21, "\x0040\x0020\x0020", 3) != 0) // "\100\040\040"
                    goto page1_init_failed;
                uint pageSize = (uint)((page1Data[16] << 8) | (page1Data[17] << 16));
                if (((pageSize - 1) & pageSize) != 0 ||
                    pageSize > Pager.MAX_PAGE_SIZE ||
                    pageSize <= 256)
                    goto page1_init_failed;
                Debug.Assert((pageSize & 7) == 0);
                uint usableSize = pageSize - page1Data[20];
                if (pageSize != bt.PageSize)
                {
                    // After reading the first page of the database assuming a page size of BtShared.pageSize, we have discovered that the page-size is
                    // actually pageSize. Unlock the database, leave pBt->pPage1 at zero and return SQLITE_OK. The caller will call this function
                    // again with the correct page-size.
                    releasePage(page1);
                    bt.UsableSize = usableSize;
                    bt.PageSize = pageSize;
                    freeTempSpace(bt);
                    rc = bt.Pager.SetPageSize(ref bt.PageSize, (int)(pageSize - usableSize));
                    return rc;
                }
                if ((bt.Ctx.Flags & BContext.FLAG.RecoveryMode) == 0 && pages > pagesFile)
                {
                    rc = SysEx.CORRUPT_BKPT();
                    goto page1_init_failed;
                }
                if (usableSize < 480)
                    goto page1_init_failed;
                bt.PageSize = pageSize;
                bt.UsableSize = usableSize;
#if !OMIT_AUTOVACUUM
                bt.AutoVacuum = (ConvertEx.Get4(page1Data, 36 + 4 * 4) != 0);
                bt.IncrVacuum = (ConvertEx.Get4(page1Data, 36 + 7 * 4) != 0);
#endif
            }

            // maxLocal is the maximum amount of payload to store locally for a cell.  Make sure it is small enough so that at least minFanout
            // cells can will fit on one page.  We assume a 10-byte page header. Besides the payload, the cell must store:
            //     2-byte pointer to the cell
            //     4-byte child pointer
            //     9-byte nKey value
            //     4-byte nData value
            //     4-byte overflow page pointer
            // So a cell consists of a 2-byte pointer, a header which is as much as 17 bytes long, 0 to N bytes of payload, and an optional 4 byte overflow
            // page pointer.
            bt.MaxLocal = (ushort)((bt.UsableSize - 12) * 64 / 255 - 23);
            bt.MinLocal = (ushort)((bt.UsableSize - 12) * 32 / 255 - 23);
            bt.MaxLeaf = (ushort)(bt.UsableSize - 35);
            bt.MinLeaf = (ushort)((bt.UsableSize - 12) * 32 / 255 - 23);
            Debug.Assert(bt.MaxLeaf + 23 <= MX_CELL_SIZE(bt));
            bt.Page1 = page1;
            bt.Pages = pages;
            return RC.OK;

        page1_init_failed:
            releasePage(page1);
            bt.Page1 = null;
            return rc;
        }
Beispiel #25
0
 /*
 ** These macros define the location of the pointer-map entry for a
 ** database page. The first argument to each is the number of usable
 ** bytes on each page of the database (often 1024). The second is the
 ** page number to look up in the pointer map.
 **
 ** PTRMAP_PAGENO returns the database page number of the pointer-map
 ** page that stores the required pointer. PTRMAP_PTROFFSET returns
 ** the offset of the requested map entry.
 **
 ** If the pgno argument passed to PTRMAP_PAGENO is a pointer-map page,
 ** then pgno is returned. So (pgno==PTRMAP_PAGENO(pgsz, pgno)) can be
 ** used to test if pgno is a pointer-map page. PTRMAP_ISPAGE implements
 ** this test.
 */
 //#define PTRMAP_PAGENO(pBt, pgno) ptrmapPageno(pBt, pgno)
 static Pgno PTRMAP_PAGENO(BtShared pBt, Pgno pgno)
 {
     return(ptrmapPageno(pBt, pgno));
 }
Beispiel #26
0
        static RC newDatabase(BtShared bt)
        {
            Debug.Assert(MutexEx.Held(bt.Mutex));
            if (bt.Pages > 0)
                return RC.OK;
            var p1 = bt.Page1;
            Debug.Assert(p1 != null);
            var data = p1.Data;
            var rc = Pager.Write(p1.DBPage);
            if (rc != RC.OK) return rc;
            Buffer.BlockCopy(_magicHeader, 0, data, 0, _magicHeader.Length);
            Debug.Assert(_magicHeader.Length == 16);
            data[16] = (byte)((bt.PageSize >> 8) & 0xff);
            data[17] = (byte)((bt.PageSize >> 16) & 0xff);
            data[18] = 1;
            data[19] = 1;
            Debug.Assert(bt.UsableSize <= bt.PageSize && bt.UsableSize + 255 >= bt.PageSize);
            data[20] = (byte)(bt.PageSize - bt.UsableSize);
            data[21] = 64;
            data[22] = 32;
            data[23] = 32;
            //_memset(&data[24], 0, 100 - 24);
            zeroPage(p1, PTF_INTKEY | PTF_LEAF | PTF_LEAFDATA);
            bt.BtsFlags |= BTS.PAGESIZE_FIXED;
#if !SQLITE_OMIT_AUTOVACUUM
            ConvertEx.Put4(data, 36 + 4 * 4, bt.AutoVacuum ? 1 : 0);
            ConvertEx.Put4(data, 36 + 7 * 4, bt.IncrVacuum ? 1 : 0);
#endif
            bt.Pages = 1;
            data[31] = 1;
            return RC.OK;
        }
Beispiel #27
0
 //#define PTRMAP_ISPAGE(pBt, pgno) (PTRMAP_PAGENO((pBt),(pgno))==(pgno))
 static bool PTRMAP_ISPAGE(BtShared pBt, u32 pgno)
 {
     return(PTRMAP_PAGENO((pBt), (pgno)) == (pgno));
 }
Beispiel #28
0
        static RC incrVacuumStep(BtShared bt, Pid fins, Pid lastPageID, bool commit)
        {
            Debug.Assert(MutexEx.Held(bt.Mutex));
            Debug.Assert(lastPageID > fins);

            if (!PTRMAP_ISPAGE(bt, lastPageID) && lastPageID != PENDING_BYTE_PAGE(bt))
            {
                Pid freesList = ConvertEx.Get4(bt.Page1.Data, 36); // Number of pages still on the free-list
                if (freesList == 0)
                    return RC.DONE;

                PTRMAP type = 0;
                Pid ptrPageID = 0;
                var rc = ptrmapGet(bt, lastPageID, ref type, ref ptrPageID);
                if (rc != RC.OK)
                    return rc;
                if (type == PTRMAP.ROOTPAGE)
                    return SysEx.CORRUPT_BKPT();

                if (type == PTRMAP.FREEPAGE)
                {
                    if (!commit)
                    {
                        // Remove the page from the files free-list. This is not required if bCommit 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.
                        Pid freePageID = 0;
                        var freePage = new MemPage();
                        rc = allocateBtreePage(bt, ref freePage, ref freePageID, lastPageID, BTALLOC.EXACT);
                        if (rc != RC.OK)
                            return rc;
                        Debug.Assert(freePageID == lastPageID);
                        releasePage(freePage);
                    }
                }
                else
                {
                    MemPage lastPage = new MemPage();
                    rc = btreeGetPage(bt, lastPageID, ref lastPage, false);
                    if (rc != RC.OK)
                        return rc;

                    // If bCommit 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 bCommit is greater than zero, then keep looping until a free-page located within the first nFin pages
                    // of the file is found.
                    BTALLOC mode = BTALLOC.ANY; // Mode parameter for allocateBtreePage()
                    Pid nearID = 0; // nearby parameter for allocateBtreePage()
                    if (!commit)
                    {
                        mode = BTALLOC.LE;
                        nearID = fins;
                    }
                    Pid freePageID = 0; // Index of free page to move pLastPg to
                    do
                    {
                        MemPage freePage = new MemPage();
                        rc = allocateBtreePage(bt, ref freePage, ref freePageID, nearID, mode);
                        if (rc != RC.OK)
                        {
                            releasePage(lastPage);
                            return rc;
                        }
                        releasePage(freePage);
                    } while (commit && freePageID > fins);
                    Debug.Assert(freePageID < lastPageID);

                    rc = relocatePage(bt, lastPage, type, ptrPageID, freePageID, commit);
                    releasePage(lastPage);
                    if (rc != RC.OK)
                        return rc;
                }
            }

            if (!commit)
            {
                do
                {
                    lastPageID--;
                } while (lastPageID == PENDING_BYTE_PAGE(bt) || PTRMAP_ISPAGE(bt, lastPageID));
                bt.DoTruncate = true;
                bt.Pages = lastPageID;
            }
            return RC.OK;
        }
Beispiel #29
0
 static MemPage btreePageFromDbPage(IPage dbPage, Pid id, BtShared bt)
 {
     MemPage page = Pager.GetExtra<MemPage>(dbPage);
     page.Data = Pager.GetData(dbPage);
     page.DBPage = dbPage;
     page.Bt = bt;
     page.ID = id;
     page.HdrOffset = (byte)(page.ID == 1 ? 100 : 0);
     return page;
 }
Beispiel #30
0
        static RC autoVacuumCommit(BtShared bt)
        {
            var pager = bt.Pager;
#if DEBUG
            int refs = pager.get_Refs();
#endif

            Debug.Assert(MutexEx.Held(bt.Mutex));
            invalidateAllOverflowCache(bt);
            Debug.Assert(bt.AutoVacuum);
            var rc = RC.OK;
            if (!bt.IncrVacuum)
            {
                Pid origs = btreePagecount(bt); // Database size before freeing
                if (PTRMAP_ISPAGE(bt, origs) || origs == PENDING_BYTE_PAGE(bt))
                {
                    // It is not possible to create a database for which the final page is either a pointer-map page or the pending-byte page. If one
                    // is encountered, this indicates corruption.
                    return SysEx.CORRUPT_BKPT();
                }

                Pid frees = ConvertEx.Get4(bt.Page1.Data, 36); // Number of pages on the freelist initially
                Pid fins = finalDbSize(bt, origs, frees); // Number of pages in database after autovacuuming
                if (fins > origs) return SysEx.CORRUPT_BKPT();

                for (var freeID = origs; freeID > fins && rc == RC.OK; freeID--) // The next page to be freed
                    rc = incrVacuumStep(bt, fins, freeID, true);
                if ((rc == RC.DONE || rc == RC.OK) && frees > 0)
                {
                    rc = Pager.Write(bt.Page1.DBPage);
                    ConvertEx.Put4(bt.Page1.Data, 32, 0);
                    ConvertEx.Put4(bt.Page1.Data, 36, 0);
                    ConvertEx.Put4(bt.Page1.Data, 28, fins);
                    bt.DoTruncate = true;
                    bt.Pages = fins;
                }
                if (rc != RC.OK)
                    pager.Rollback();
            }
#if DEBUG
            Debug.Assert(refs == pager.get_Refs());
#endif
            return rc;
        }
Beispiel #31
0
 static RC btreeGetPage(BtShared bt, Pid id, ref MemPage page, bool noContent)
 {
     Debug.Assert(MutexEx.Held(bt.Mutex));
     IPage dbPage = null;
     var rc = bt.Pager.Acquire(id, ref dbPage, noContent);
     if (rc != RC.OK) return rc;
     page = btreePageFromDbPage(dbPage, id, bt);
     return RC.OK;
 }
Beispiel #32
0
        static RC getOverflowPage(BtShared bt, Pid ovfl, out MemPage pageOut, out Pid idNextOut)
        {
            Pid next = 0;
            MemPage page = null;
            pageOut = null;
            var rc = RC.OK;

            Debug.Assert(MutexEx.Held(bt.Mutex));

#if !OMIT_AUTOVACUUM
            // Try to find the next page in the overflow list using the autovacuum pointer-map pages. Guess that the next page in 
            // the overflow list is page number (ovfl+1). If that guess turns out to be wrong, fall back to loading the data of page 
            // number ovfl to determine the next page number.
            if (bt.AutoVacuum)
            {
                Pid guess = ovfl + 1;

                while (PTRMAP_ISPAGE(bt, guess) || guess == PENDING_BYTE_PAGE(bt))
                    guess++;
                if (guess <= btreePagecount(bt))
                {
                    Pid id = 0;
                    PTRMAP type = 0;
                    rc = ptrmapGet(bt, guess, ref type, ref id);
                    if (rc == RC.OK && type == PTRMAP.OVERFLOW2 && id == ovfl)
                    {
                        next = guess;
                        rc = RC.DONE;
                    }
                }
            }
#endif

            Debug.Assert(next == 0 || rc == RC.DONE);
            if (rc == RC.OK)
            {
                rc = btreeGetPage(bt, ovfl, ref page, false);
                Debug.Assert(rc == RC.OK || page == null);
                if (rc == RC.OK)
                    next = ConvertEx.Get4(page.Data);
            }

            idNextOut = next;
            if (pageOut != null)
                pageOut = page;
            else
                releasePage(page);
            return (rc == RC.DONE ? RC.OK : rc);
        }
Beispiel #33
0
 static MemPage btreePageLookup(BtShared bt, Pid id)
 {
     Debug.Assert(MutexEx.Held(bt.Mutex));
     var dbPage = bt.Pager.Lookup(id);
     return (dbPage != null ? btreePageFromDbPage(dbPage, id, bt) : null);
 }
Beispiel #34
0
 static void invalidateAllOverflowCache(BtShared bt) { }
Beispiel #35
0
 static Pid btreePagecount(BtShared bt)
 {
     return bt.Pages;
 }
Beispiel #36
0
 static bool btreeGetHasContent(BtShared bt, Pid id)
 {
     var p = bt.HasContent;
     return (p != null && (id > p.Length || p.Get(id)));
 }
Beispiel #37
0
        static RC getAndInitPage(BtShared bt, Pid id, ref MemPage page)
        {
            Debug.Assert(MutexEx.Held(bt.Mutex));

            RC rc;
            if (id > btreePagecount(bt))
                rc = SysEx.CORRUPT_BKPT();
            else
            {
                rc = btreeGetPage(bt, id, ref page, false);
                if (rc == RC.OK)
                {
                    rc = btreeInitPage(page);
                    if (rc != RC.OK)
                        releasePage(page);
                }
            }

            Debug.Assert(id != 0 || rc == RC.CORRUPT);
            return rc;
        }
Beispiel #38
0
        static RC allocateBtreePage(BtShared bt, ref MemPage page, ref Pid id, Pid nearby, BTALLOC mode)
        {
            Debug.Assert(MutexEx.Held(bt.Mutex));
            Debug.Assert(mode == BTALLOC.ANY || (nearby > 0 && IFAUTOVACUUM(bt.AutoVacuum)));
            MemPage page1 = bt.Page1;
            Pid maxPage = btreePagecount(bt); // Total size of the database file
            uint n = ConvertEx.Get4(page1.Data, 36); // Number of pages on the freelist
            ASSERTCOVERAGE(n == maxPage - 1);
            if (n >= maxPage)
                return SysEx.CORRUPT_BKPT();
            RC rc;
            MemPage trunk = null;
            MemPage prevTrunk = null;
            if (n > 0)
            {
                // There are pages on the freelist.  Reuse one of those pages.
                bool searchList = false; // If the free-list must be searched for

                // If eMode==BTALLOC_EXACT 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 !OMIT_AUTOVACUUM
                if (mode == BTALLOC.EXACT)
                {
                    if (nearby <= maxPage)
                    {
                        Debug.Assert(nearby > 0);
                        Debug.Assert(bt.AutoVacuum);
                        PTRMAP type = 0;
                        Pid dummy = 0;
                        rc = ptrmapGet(bt, nearby, ref type, ref dummy);
                        if (rc != RC.OK) return rc;
                        if (type == PTRMAP.FREEPAGE)
                            searchList = true;
                    }
                }
                else if (mode == BTALLOC.LE)
                    searchList = true;
#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(page1.DBPage);
                if (rc != RC.OK) return rc;
                ConvertEx.Put4(page1.Data, (uint)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 (eMode==BTALLOC_EXACT) or until a page less than 'nearby' is located (eMode==BTALLOC_LT)
                Pid trunkID;
                do
                {
                    prevTrunk = trunk;
                    if (prevTrunk != null)
                        trunkID = ConvertEx.Get4(prevTrunk.Data, 0);
                    else
                        trunkID = ConvertEx.Get4(page1.Data, 32);

                    ASSERTCOVERAGE(trunkID == maxPage);
                    if (trunkID > maxPage)
                        rc = SysEx.CORRUPT_BKPT();
                    else
                        rc = btreeGetPage(bt, trunkID, ref trunk, false);
                    if (rc != RC.OK)
                    {
                        trunk = null;
                        goto end_allocate_page;
                    }
                    Debug.Assert(trunk != null);
                    Debug.Assert(trunk.Data != null);

                    uint k = ConvertEx.Get4(trunk.Data, 4); // # of leaves on this trunk page, Number of leaves on the trunk of the freelist
                    if (k == 0 && !searchList)
                    {
                        // 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(prevTrunk == null);
                        rc = Pager.Write(trunk.DBPage);
                        if (rc != RC.OK)
                            goto end_allocate_page;
                        id = trunkID;
                        Buffer.BlockCopy(trunk.Data, 0, page1.Data, 32, 4);
                        page = trunk;
                        trunk = null;
                        TRACE("ALLOCATE: %d trunk - %d free pages left\n", id, n - 1);
                    }
                    else if (k > (uint)(bt.UsableSize / 4 - 2))
                    {
                        // Value of k is out of range.  Database corruption
                        rc = SysEx.CORRUPT_BKPT();
                        goto end_allocate_page;
#if !OMIT_AUTOVACUUM
                    }
                    else if (searchList && (nearby == trunkID || (trunkID < nearby && mode == BTALLOC.LE)))
                    {
                        // The list is being searched and this trunk page is the page to allocate, regardless of whether it has leaves.
                        id = trunkID;
                        page = trunk;
                        searchList = false;
                        rc = Pager.Write(trunk.DBPage);
                        if (rc != RC.OK)
                            goto end_allocate_page;
                        if (k == 0)
                        {
                            if (prevTrunk == null)
                            {
                                //memcpy(page1.Data[32], trunk.Data[0], 4);
                                page1.Data[32 + 0] = trunk.Data[0 + 0];
                                page1.Data[32 + 1] = trunk.Data[0 + 1];
                                page1.Data[32 + 2] = trunk.Data[0 + 2];
                                page1.Data[32 + 3] = trunk.Data[0 + 3];
                            }
                            else
                            {
                                rc = Pager.Write(prevTrunk.DBPage);
                                if (rc != RC.OK)
                                    goto end_allocate_page;
                                //memcpy(prevTrunk.Data[0], trunk.Data[0], 4);
                                prevTrunk.Data[0 + 0] = trunk.Data[0 + 0];
                                prevTrunk.Data[0 + 1] = trunk.Data[0 + 1];
                                prevTrunk.Data[0 + 2] = trunk.Data[0 + 2];
                                prevTrunk.Data[0 + 3] = trunk.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.
                            Pid newTrunkID = ConvertEx.Get4(trunk.Data, 8);
                            if (newTrunkID > maxPage)
                            {
                                rc = SysEx.CORRUPT_BKPT();
                                goto end_allocate_page;
                            }
                            ASSERTCOVERAGE(newTrunkID == maxPage);
                            var newTrunk = new MemPage();
                            rc = btreeGetPage(bt, newTrunkID, ref newTrunk, false);
                            if (rc != RC.OK)
                                goto end_allocate_page;
                            rc = Pager.Write(newTrunk.DBPage);
                            if (rc != RC.OK)
                            {
                                releasePage(newTrunk);
                                goto end_allocate_page;
                            }
                            //memcpy(newTrunk.Data[0], trunk.Data[0], 4);
                            newTrunk.Data[0 + 0] = trunk.Data[0 + 0];
                            newTrunk.Data[0 + 1] = trunk.Data[0 + 1];
                            newTrunk.Data[0 + 2] = trunk.Data[0 + 2];
                            newTrunk.Data[0 + 3] = trunk.Data[0 + 3];
                            ConvertEx.Put4(newTrunk.Data, (uint)4, (uint)(k - 1));
                            Buffer.BlockCopy(trunk.Data, 12, newTrunk.Data, 8, (int)(k - 1) * 4);
                            releasePage(newTrunk);
                            if (prevTrunk == null)
                            {
                                Debug.Assert(Pager.Iswriteable(page1.DBPage));
                                ConvertEx.Put4(page1.Data, (uint)32, newTrunkID);
                            }
                            else
                            {
                                rc = Pager.Write(prevTrunk.DBPage);
                                if (rc != RC.OK)
                                    goto end_allocate_page;
                                ConvertEx.Put4(prevTrunk.Data, (uint)0, newTrunkID);
                            }
                        }
                        trunk = null;
                        TRACE("ALLOCATE: %d trunk - %d free pages left\n", id, n - 1);
#endif
                    }
                    else if (k > 0)
                    {
                        // Extract a leaf from the trunk
                        byte[] data = trunk.Data;
                        Pid pageID;
                        uint closest;
                        if (nearby > 0)
                        {
                            closest = 0;
                            if (mode == BTALLOC.LE)
                            {
                                for (var i = 0U; i < k; i++)
                                {
                                    pageID = ConvertEx.Get4(data, 8 + i * 4);
                                    if (pageID <= nearby)
                                    {
                                        closest = i;
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                int dist = Math.Abs((int)(ConvertEx.Get4(data, 8) - nearby));
                                for (var i = 1U; i < k; i++)
                                {
                                    int d2 = Math.Abs((int)(ConvertEx.Get4(data, 8 + i * 4) - nearby));
                                    if (d2 < dist)
                                    {
                                        closest = i;
                                        dist = d2;
                                    }
                                }
                            }
                        }
                        else
                            closest = 0;

                        pageID = ConvertEx.Get4(data, 8 + closest * 4);
                        ASSERTCOVERAGE(pageID == maxPage);
                        if (pageID > maxPage)
                        {
                            rc = SysEx.CORRUPT_BKPT();
                            goto end_allocate_page;
                        }
                        ASSERTCOVERAGE(pageID == maxPage);
                        if (!searchList || (pageID == nearby || (pageID < nearby && mode == BTALLOC.LE)))
                        {
                            id = pageID;
                            TRACE("ALLOCATE: %d was leaf %d of %d on trunk %d: %d more free pages\n", id, closest + 1, k, trunk.ID, n - 1);
                            rc = Pager.Write(trunk.DBPage);
                            if (rc != RC.OK) goto end_allocate_page;
                            if (closest < k - 1)
                                Buffer.BlockCopy(data, (int)(4 + k * 4), data, 8 + (int)closest * 4, 4);//memcpy( aData[8 + closest * 4], ref aData[4 + k * 4], 4 );
                            ConvertEx.Put4(data, (uint)4, (k - 1));
                            bool noContent = !btreeGetHasContent(bt, id);
                            rc = btreeGetPage(bt, id, ref page, noContent);
                            if (rc == RC.OK)
                            {
                                rc = Pager.Write((page).DBPage);
                                if (rc != RC.OK)
                                    releasePage(page);
                            }
                            searchList = false;
                        }
                    }
                    releasePage(prevTrunk);
                    prevTrunk = null;
                } while (searchList);
            }
            else
            {
                // Normally, new pages allocated by this block can be requested from the pager layer with the 'no-content' flag set. This prevents the pager
                // from trying to read the pages content from disk. However, if the current transaction has already run one or more incremental-vacuum
                // steps, then the page we are about to allocate may contain content that is required in the event of a rollback. In this case, do
                // not set the no-content flag. This causes the pager to load and journal the current page content before overwriting it.
                //
                // Note that the pager will not actually attempt to load or journal content for any page that really does lie past the end of the database
                // file on disk. So the effects of disabling the no-content optimization here are confined to those pages that lie between the end of the
                // database image and the end of the database file.
                bool noContent = !IFAUTOVACUUM(bt.DoTruncate);

                // There are no pages on the freelist, so append a new page to the database image.
                rc = Pager.Write(bt.Page1.DBPage);
                if (rc != RC.OK) return rc;
                bt.Pages++;
                if (bt.Pages == PENDING_BYTE_PAGE(bt)) bt.Pages++;

#if !OMIT_AUTOVACUUM
                if (bt.AutoVacuum && PTRMAP_ISPAGE(bt, bt.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.
                    TRACE("ALLOCATE: %d from end of file (pointer-map page)\n", bt.Pages);
                    Debug.Assert(bt.Pages != PENDING_BYTE_PAGE(bt));
                    MemPage pg = null;
                    rc = btreeGetPage(bt, bt.Pages, ref pg, noContent);
                    if (rc == RC.OK)
                    {
                        rc = Pager.Write(pg.DBPage);
                        releasePage(pg);
                    }
                    if (rc != RC.OK) return rc;
                    bt.Pages++;
                    if (bt.Pages == PENDING_BYTE_PAGE(bt)) bt.Pages++;
                }
#endif
                ConvertEx.Put4(bt.Page1.Data, (uint)28, bt.Pages);
                id = bt.Pages;

                Debug.Assert(id != PENDING_BYTE_PAGE(bt));
                rc = btreeGetPage(bt, id, ref page, noContent);
                if (rc != RC.OK) return rc;
                rc = Pager.Write((page).DBPage);
                if (rc != RC.OK)
                    releasePage(page);
                TRACE("ALLOCATE: %d from end of file\n", id);
            }

            Debug.Assert(id != PENDING_BYTE_PAGE(bt));

        end_allocate_page:
            releasePage(trunk);
            releasePage(prevTrunk);
            if (rc == RC.OK)
            {
                if (Pager.get_PageRefs((page).DBPage) > 1)
                {
                    releasePage(page);
                    return SysEx.CORRUPT_BKPT();
                }
                (page).IsInit = false;
            }
            else
                page = null;
            Debug.Assert(rc != RC.OK || Pager.Iswriteable((page).DBPage));
            return rc;
        }
Beispiel #39
0
        public static RC Open(VSystem vfs, string filename, BContext ctx, ref Btree btree, OPEN flags, VSystem.OPEN vfsFlags)
        {
            // True if opening an ephemeral, temporary database
            bool tempDB = string.IsNullOrEmpty(filename);

            // Set the variable isMemdb to true for an in-memory database, or false for a file-based database.
            bool memoryDB = (filename == ":memory:") ||
                (tempDB && ctx.TempInMemory()) ||
                (vfsFlags & VSystem.OPEN.MEMORY) != 0;

            Debug.Assert(ctx != null);
            Debug.Assert(vfs != null);
            Debug.Assert(MutexEx.Held(ctx.Mutex));
            Debug.Assert(((uint)flags & 0xff) == (uint)flags); // flags fit in 8 bits

            // Only a BTREE_SINGLE database can be BTREE_UNORDERED
            Debug.Assert((flags & OPEN.UNORDERED) == 0 || (flags & OPEN.SINGLE) != 0);

            // A BTREE_SINGLE database is always a temporary and/or ephemeral
            Debug.Assert((flags & OPEN.SINGLE) == 0 || tempDB);

            if (memoryDB)
                flags |= OPEN.MEMORY;
            if ((vfsFlags & VSystem.OPEN.MAIN_DB) != 0 && (memoryDB || tempDB))
                vfsFlags = (vfsFlags & ~VSystem.OPEN.MAIN_DB) | VSystem.OPEN.TEMP_DB;
            var p = new Btree(); // Handle to return
            if (p == null)
                return RC.NOMEM;
            p.InTrans = TRANS.NONE;
            p.Ctx = ctx;
#if !OMIT_SHARED_CACHE
            p.Lock.Btree = p;
            p.Lock.Table = 1;
#endif

            RC rc = RC.OK; // Result code from this function
            BtShared bt = null; // Shared part of btree structure
            MutexEx mutexOpen = null;
#if !OMIT_SHARED_CACHE && !OMIT_DISKIO
            // If this Btree is a candidate for shared cache, try to find an existing BtShared object that we can share with
            if (!tempDB && (!memoryDB || (vfsFlags & VSystem.OPEN.URI) != 0))
                if ((vfsFlags & VSystem.OPEN.SHAREDCACHE) != 0)
                {
                    string fullPathname;
                    p.Sharable_ = true;
                    if (memoryDB)
                        fullPathname = filename;
                    else
                        vfs.FullPathname(filename, out fullPathname);
                    MutexEx mutexShared;
#if THREADSAFE
                    mutexOpen = MutexEx.Alloc(MutexEx.MUTEX.STATIC_OPEN); // Prevents a race condition. Ticket #3537
                    MutexEx.Enter(mutexOpen);
                    mutexShared = MutexEx.Alloc(MutexEx.MUTEX.STATIC_MASTER);
                    MutexEx.Enter(mutexShared);
#endif
                    for (bt = _sharedCacheList; bt != null; bt = bt.Next)
                    {
                        Debug.Assert(bt.Refs > 0);
                        if (fullPathname == bt.Pager.get_Filename(false) && bt.Pager.get_Vfs() == vfs)
                        {
                            for (var i = ctx.DBs.length - 1; i >= 0; i--)
                            {
                                var existing = ctx.DBs[i].Bt;
                                if (existing != null && existing.Bt == bt)
                                {
                                    MutexEx.Leave(mutexShared);
                                    MutexEx.Leave(mutexOpen);
                                    fullPathname = null;
                                    p = null;
                                    return RC.CONSTRAINT;
                                }
                            }
                            p.Bt = bt;
                            bt.Refs++;
                            break;
                        }
                    }
                    MutexEx.Leave(mutexShared);
                    fullPathname = null;
                }
#if DEBUG
                else
                    // In debug mode, we mark all persistent databases as sharable even when they are not.  This exercises the locking code and
                    // gives more opportunity for asserts(sqlite3_mutex_held()) statements to find locking problems.
                    p.Sharable_ = true;
#endif
#endif

            byte reserves; // Byte of unused space on each page
            var dbHeader = new byte[100]; // Database header content
            if (bt == null)
            {
                // The following asserts make sure that structures used by the btree are the right size.  This is to guard against size changes that result
                // when compiling on a different architecture.
                Debug.Assert(sizeof(long) == 8 || sizeof(long) == 4);
                Debug.Assert(sizeof(ulong) == 8 || sizeof(ulong) == 4);
                Debug.Assert(sizeof(uint) == 4);
                Debug.Assert(sizeof(ushort) == 2);
                Debug.Assert(sizeof(Pid) == 4);

                bt = new BtShared();
                if (bt == null)
                {
                    rc = RC.NOMEM;
                    goto btree_open_out;
                }
                rc = Pager.Open(vfs, out bt.Pager, filename, EXTRA_SIZE, (IPager.PAGEROPEN)flags, vfsFlags, pageReinit, null);
                if (rc == RC.OK)
                    rc = bt.Pager.ReadFileHeader(dbHeader.Length, dbHeader);
                if (rc != RC.OK)
                    goto btree_open_out;
                bt.OpenFlags = flags;
                bt.Ctx = ctx;
                bt.Pager.SetBusyHandler(btreeInvokeBusyHandler, bt);
                p.Bt = bt;

                bt.Cursor = null;
                bt.Page1 = null;
                if (bt.Pager.get_Readonly()) bt.BtsFlags |= BTS.READ_ONLY;
#if SECURE_DELETE
                bt.BtsFlags |= BTS.SECURE_DELETE;
#endif
                bt.PageSize = (Pid)((dbHeader[16] << 8) | (dbHeader[17] << 16));
                if (bt.PageSize < 512 || bt.PageSize > Pager.MAX_PAGE_SIZE || ((bt.PageSize - 1) & bt.PageSize) != 0)
                {
                    bt.PageSize = 0;
#if !OMIT_AUTOVACUUM
                    // If the magic name ":memory:" will create an in-memory database, then leave the autoVacuum mode at 0 (do not auto-vacuum), even if
                    // SQLITE_DEFAULT_AUTOVACUUM is true. On the other hand, if SQLITE_OMIT_MEMORYDB has been defined, then ":memory:" is just a
                    // regular file-name. In this case the auto-vacuum applies as per normal.
                    if (filename != null && !memoryDB)
                    {
                        bt.AutoVacuum = (DEFAULT_AUTOVACUUM != 0);
                        bt.IncrVacuum = (DEFAULT_AUTOVACUUM == AUTOVACUUM.INCR);
                    }
#endif
                    reserves = 0;
                }
                else
                {
                    reserves = dbHeader[20];
                    bt.BtsFlags |= BTS.PAGESIZE_FIXED;
#if !OMIT_AUTOVACUUM
                    bt.AutoVacuum = (ConvertEx.Get4(dbHeader, 36 + 4 * 4) != 0);
                    bt.IncrVacuum = (ConvertEx.Get4(dbHeader, 36 + 7 * 4) != 0);
#endif
                }
                rc = bt.Pager.SetPageSize(ref bt.PageSize, reserves);
                if (rc != RC.OK) goto btree_open_out;
                bt.UsableSize = (ushort)(bt.PageSize - reserves);
                Debug.Assert((bt.PageSize & 7) == 0); // 8-byte alignment of pageSize

#if !SHARED_CACHE && !OMIT_DISKIO
                // Add the new BtShared object to the linked list sharable BtShareds.
                if (p.Sharable_)
                {
                    bt.Refs = 1;
                    MutexEx mutexShared;
#if THREADSAFE
                    mutexShared = MutexEx.Alloc(MutexEx.MUTEX.STATIC_MASTER);
                    bt.Mutex = MutexEx.Alloc(MutexEx.MUTEX.FAST);
#endif
                    MutexEx.Enter(mutexShared);
                    bt.Next = _sharedCacheList;
                    _sharedCacheList = bt;
                    MutexEx.Leave(mutexShared);
                }
#endif
            }

#if !OMIT_SHARED_CACHE && !OMIT_DISKIO
            // If the new Btree uses a sharable pBtShared, then link the new Btree into the list of all sharable Btrees for the same connection.
            // The list is kept in ascending order by pBt address.
            if (p.Sharable_)
            {
                Btree sib;
                for (var i = 0; i < ctx.DBs.length; i++)
                    if ((sib = ctx.DBs[i].Bt) != null && sib.Sharable_)
                    {
                        while (sib.Prev != null) { sib = sib.Prev; }
                        if (p.Bt.AutoID < sib.Bt.AutoID)
                        {
                            p.Next = sib;
                            p.Prev = null;
                            sib.Prev = p;
                        }
                        else
                        {
                            while (sib.Next != null && sib.Next.Bt.AutoID < p.Bt.AutoID)
                                sib = sib.Next;
                            p.Next = sib.Next;
                            p.Prev = sib;
                            if (p.Next != null)
                                p.Next.Prev = p;
                            sib.Next = p;
                        }
                        break;
                    }
            }
#endif
            btree = p;

        btree_open_out:
            if (rc != RC.OK)
            {
                if (bt != null && bt.Pager != null)
                    bt.Pager.Close();
                bt = null;
                p = null;
                btree = null;
            }
            else
                // If the B-Tree was successfully opened, set the pager-cache size to the default value. Except, when opening on an existing shared pager-cache,
                // do not change the pager-cache size.
                if (p.Schema(0, null) == null)
                    p.Bt.Pager.SetCacheSize(DEFAULT_CACHE_SIZE);
#if THREADSAFE
            Debug.Assert(MutexEx.Held(mutexOpen));
            MutexEx.Leave(mutexOpen);
#endif
            return rc;
        }
Beispiel #40
0
        static RC freePage2(BtShared bt, MemPage memPage, Pid pageID)
        {
            Debug.Assert(MutexEx.Held(bt.Mutex));
            Debug.Assert(pageID > 1);
            Debug.Assert(memPage == null || memPage.ID == pageID);

            MemPage page; // Page being freed. May be NULL.
            MemPage trunk = null; // Free-list trunk page
            if (memPage != null)
            {
                page = memPage;
                Pager.get_PageRefs(page.DBPage);
            }
            else
                page = btreePageLookup(bt, pageID);

            // Increment the free page count on pPage1
            MemPage page1 = bt.Page1; // Local reference to page 1
            RC rc = Pager.Write(page1.DBPage);
            if (rc != RC.OK) goto freepage_out;
            int frees = (int)ConvertEx.Get4(page1.Data, 36); // Initial number of pages on free-list
            ConvertEx.Put4(page1.Data, 36, frees + 1);

            if ((bt.BtsFlags & BTS.SECURE_DELETE) != 0)
            {
                // If the secure_delete option is enabled, then always fully overwrite deleted information with zeros.
                if ((page == null && (rc = btreeGetPage(bt, pageID, ref page, false)) != RC.OK) || (rc = Pager.Write(page.DBPage)) != RC.OK)
                    goto freepage_out;
                Array.Clear(page.Data, 0, (int)page.Bt.PageSize);
            }

            // If the database supports auto-vacuum, write an entry in the pointer-map to indicate that the page is free.
#if !OMIT_AUTOVACUUM
            if (bt.AutoVacuum)
            {
                ptrmapPut(bt, pageID, PTRMAP.FREEPAGE, 0, ref rc);
                if (rc != RC.OK) goto freepage_out;
            }
#endif

            // Now manipulate the actual database free-list structure. There are two possibilities. If the free-list is currently empty, or if the first
            // trunk page in the free-list is full, then this page will become a new free-list trunk page. Otherwise, it will become a leaf of the
            // first trunk page in the current free-list. This block tests if it is possible to add the page as a new free-list leaf.
            Pid trunkID = 0; // Page number of free-list trunk page
            if (frees != 0)
            {
                trunkID = ConvertEx.Get4(page1.Data, 32);
                rc = btreeGetPage(bt, trunkID, ref trunk, false);
                if (rc != RC.OK)
                    goto freepage_out;

                uint leafs = ConvertEx.Get4(trunk.Data, 4); // Initial number of leaf cells on trunk page
                Debug.Assert(bt.UsableSize > 32);
                if (leafs > (uint)bt.UsableSize / 4 - 2)
                {
                    rc = SysEx.CORRUPT_BKPT();
                    goto freepage_out;
                }
                if (leafs < (uint)bt.UsableSize / 4 - 8)
                {
                    // In this case there is room on the trunk page to insert the page being freed as a new leaf.
                    //
                    // Note that the trunk page is not really full until it contains usableSize/4 - 2 entries, not usableSize/4 - 8 entries as we have
                    // coded.  But due to a coding error in versions of SQLite prior to 3.6.0, databases with freelist trunk pages holding more than
                    // usableSize/4 - 8 entries will be reported as corrupt.  In order to maintain backwards compatibility with older versions of SQLite,
                    // we will continue to restrict the number of entries to usableSize/4 - 8 for now.  At some point in the future (once everyone has upgraded
                    // to 3.6.0 or later) we should consider fixing the conditional above to read "usableSize/4-2" instead of "usableSize/4-8".
                    rc = Pager.Write(trunk.DBPage);
                    if (rc == RC.OK)
                    {
                        ConvertEx.Put4(trunk.Data, (uint)4, leafs + 1);
                        ConvertEx.Put4(trunk.Data, (uint)8 + leafs * 4, pageID);
                        if (page != null && (bt.BtsFlags & BTS.SECURE_DELETE) == 0)
                            Pager.DontWrite(page.DBPage);
                        rc = btreeSetHasContent(bt, pageID);
                    }
                    TRACE("FREE-PAGE: %d leaf on trunk page %d\n", pageID, trunk.ID);
                    goto freepage_out;
                }
            }

            // If control flows to this point, then it was not possible to add the the page being freed as a leaf page of the first trunk in the free-list.
            // Possibly because the free-list is empty, or possibly because the first trunk in the free-list is full. Either way, the page being freed
            // will become the new first trunk page in the free-list.
            if (page == null && (rc = btreeGetPage(bt, pageID, ref page, false)) != RC.OK)
                goto freepage_out;
            rc = Pager.Write(page.DBPage);
            if (rc != RC.OK)
                goto freepage_out;
            ConvertEx.Put4(page.Data, trunkID);
            ConvertEx.Put4(page.Data, 4, 0);
            ConvertEx.Put4(page1.Data, (uint)32, pageID);
            TRACE("FREE-PAGE: %d new trunk page replacing %d\n", page.ID, trunkID);

        freepage_out:
            if (page != null)
                page.IsInit = false;
            releasePage(page);
            releasePage(trunk);
            return rc;
        }
Beispiel #41
0
        static bool removeFromSharingList(BtShared bt)
        {
#if !OMIT_SHARED_CACHE
            Debug.Assert(MutexEx.Held(bt.Mutex));
#if THREADSAFE
            var master = MutexEx.Alloc(MutexEx.MUTEX.STATIC_MASTER);
#endif
            var removed = false;
            MutexEx.Enter(master);
            bt.Refs--;
            if (bt.Refs <= 0)
            {
                if (_sharedCacheList == bt)
                    _sharedCacheList = bt.Next;
                else
                {
                    var list = _sharedCacheList;
                    while (C._ALWAYS(list != null) && list.Next != bt)
                        list = list.Next;
                    if (C._ALWAYS(list != null))
                        list.Next = bt.Next;
                }
#if THREADSAFE
                MutexEx.Free(bt.Mutex);
#endif
                removed = true;
            }
            MutexEx.Leave(master);
            return removed;
#else
            return true;
#endif
        }
Beispiel #42
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);
        }
Beispiel #43
0
 static void allocateTempSpace(BtShared bt)
 {
     if (bt.TmpSpace == null)
         bt.TmpSpace = PCache.PageAlloc2((int)bt.PageSize);
 }
Beispiel #44
0
        static RC clearDatabasePage(BtShared bt, Pid id, bool freePageFlag, ref int changes)
        {
            Debug.Assert(MutexEx.Held(bt.Mutex));
            if (id > btreePagecount(bt))
                return SysEx.CORRUPT_BKPT();

            MemPage page = new MemPage();
            var rc = getAndInitPage(bt, id, ref page);
            if (rc != RC.OK) return rc;
            for (uint i = 0U; i < page.Cells; i++)
            {
                uint cell_ = findCell(page, i);
                if (!page.Leaf)
                {
                    rc = clearDatabasePage(bt, ConvertEx.Get4(page.Data, cell_), true, ref changes);
                    if (rc != RC.OK) goto cleardatabasepage_out;
                }
                rc = clearCell(page, cell_);
                if (rc != RC.OK) goto cleardatabasepage_out;
            }
            if (!page.Leaf)
            {
                rc = clearDatabasePage(bt, ConvertEx.Get4(page.Data, 8), true, ref changes);
                if (rc != RC.OK) goto cleardatabasepage_out;
            }
            else
            {
                Debug.Assert(page.IntKey);
                changes += page.Cells;
            }
            if (freePageFlag)
                freePage(page, ref rc);
            else if ((rc = Pager.Write(page.DBPage)) == 0)
                zeroPage(page, page.Data[0] | PTF_LEAF);

        cleardatabasepage_out:
            releasePage(page);
            return rc;
        }
Beispiel #45
0
 /*
 ** The database page the PENDING_BYTE occupies. This page is never used.
 */
 //# define PENDING_BYTE_PAGE(pBt) PAGER_MJ_PGNO(pBt)
 // TODO -- Convert PENDING_BYTE_PAGE to inline
 private static uint PENDING_BYTE_PAGE(BtShared pBt)
 {
     return(PAGER_MJ_PGNO(pBt.pPager));
 }