Example #1
0
        static void sqlite3MemFreeBtCursor(ref BtCursor pPrior)
        {
            if (pPrior == null)
            {
                return;
            }
#if FALSE && DEBUG
            for (int i = mem0.msBtCursor.next - 1; i >= 0; i--)
            {
                if (mem0.aBtCursor[i] != null && mem0.aBtCursor[i] == pPrior)
                {
                    Debugger.Break();
                }
            }
#endif
            mem0.msBtCursor.dealloc++;
            if (mem0.msBtCursor.next < mem0.aBtCursor.Length - 1)
            {
                mem0.aBtCursor[++mem0.msBtCursor.next] = pPrior;
                if (mem0.msBtCursor.next > mem0.msBtCursor.max)
                {
                    mem0.msBtCursor.max = mem0.msBtCursor.next;
                }
            }
            //Array.Resize( ref mem0.aBtCursor, (int)(mem0.hw_BtCursor * 1.5 + 1 ));
            //mem0.aBtCursor[mem0.hw_BtCursor] = pPrior;
            //mem0.hw_BtCursor = mem0.aBtCursor.Length;
            pPrior = null;
            return;
        }
Example #2
0
        /*
        ** Usage:   btree_cursor ID TABLENUM WRITEABLE
        **
        ** Create a new cursor.  Return the ID for the cursor.
        */
        static int btree_cursor(
            object NotUsed,
            Tcl_Interp interp, /* The TCL interpreter that invoked this command */
            int argc,          /* Number of arguments */
            TclObject[] argv   /* Text of each argument */
            )
        {
            Btree         pBt;
            int           iTable = 0;
            BtCursor      pCur;
            int           rc     = SQLITE_OK;
            int           wrFlag = 0;
            StringBuilder zBuf   = new StringBuilder(30);

            if (argc != 4)
            {
                TCL.Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0].ToString(),
                                     " ID TABLENUM WRITEABLE\"");
                return(TCL.TCL_ERROR);
            }
            pBt = (Btree)sqlite3TestTextToPtr(interp, argv[1].ToString());
            if (TCL.Tcl_GetInt(interp, argv[2], out iTable))
            {
                return(TCL.TCL_ERROR);
            }
            if (TCL.Tcl_GetBoolean(interp, argv[3], out wrFlag))
            {
                return(TCL.TCL_ERROR);
            }
            //pCur = (BtCursor )ckalloc(sqlite3BtreeCursorSize());
            pCur = new BtCursor();// memset( pCur, 0, sqlite3BtreeCursorSize() );
            sqlite3BtreeEnter(pBt);
#if !SQLITE_OMIT_SHARED_CACHE
            rc = sqlite3BtreeLockTable(pBt, iTable, wrFlag);
#endif
            if (rc == SQLITE_OK)
            {
                rc = sqlite3BtreeCursor(pBt, iTable, wrFlag, null, pCur);
            }
            sqlite3BtreeLeave(pBt);
            if (rc != 0)
            {
                pCur = null;// ckfree( pCur );
                TCL.Tcl_AppendResult(interp, errorName(rc), null);
                ;
                return(TCL.TCL_ERROR);
            }
            sqlite3_snprintf(30, zBuf, "->%p", pCur);
            if (TCL.Tcl_CreateCommandPointer(interp, zBuf, pCur))
            {
                return(TCL.TCL_ERROR);
            }
            else
            {
                TCL.Tcl_AppendResult(interp, zBuf);
            }
            return(SQLITE_OK);
        }
Example #3
0
        static BtCursor sqlite3MemMallocBtCursor(BtCursor dummy)
        {
            BtCursor pBtCursor;

            mem0.msBtCursor.alloc++;
            if (mem0.msBtCursor.next > 0 && mem0.aBtCursor[mem0.msBtCursor.next] != null)
            {
                pBtCursor = mem0.aBtCursor[mem0.msBtCursor.next];
                mem0.aBtCursor[mem0.msBtCursor.next] = null;
                mem0.msBtCursor.cached++;
                mem0.msBtCursor.next--;
            }
            else
            {
                pBtCursor = new BtCursor();
            }
            return(pBtCursor);
        }
Example #4
0
        static BtCursor sqlite3MemMallocBtCursor(BtCursor dummy)
        {
            BtCursor pBtCursor;

            mem0.msBtCursor.alloc++;
            if (mem0.msBtCursor.next > 0 && mem0.aBtCursor[mem0.msBtCursor.next] != null)
            {
                pBtCursor = mem0.aBtCursor[mem0.msBtCursor.next];
                Debug.Assert(pBtCursor.pNext == null && pBtCursor.pPrev == null && pBtCursor.wrFlag == 0);
                mem0.aBtCursor[mem0.msBtCursor.next] = null;
                mem0.msBtCursor.cached++;
                mem0.msBtCursor.next--;
            }
            else
            {
                pBtCursor = new BtCursor();
            }
            return(pBtCursor);
        }
Example #5
0
            internal MemPage[] Pages = new MemPage[BTCURSOR_MAX_DEPTH]; // Pages from root to current page
            internal void memset()
            {
                Next        = Prev = null;
                KeyInfo     = null;
                RootID      = 0;
                CachedRowID = 0;
                Info        = new CellInfo();
                WrFlag      = false;
                AtLast      = 0;
                ValidNKey   = false;
                State       = 0;
                KeyLength   = 0;
                Key         = null;
                SkipNext    = 0;
#if !OMIT_INCRBLOB
                IsIncrblobHandle = false;
                Overflows        = null;
#endif
                ID = 0;
            }
            public MemPage[] apPage = new MemPage[BTCURSOR_MAX_DEPTH]; /* Pages from root to current page */

            public void Clear()
            {
                pNext       = null;
                pPrev       = null;
                pKeyInfo    = null;
                pgnoRoot    = 0;
                cachedRowid = 0;
                info        = new CellInfo();
                wrFlag      = 0;
                atLast      = 0;
                validNKey   = false;
                eState      = 0;
                pKey        = null;
                nKey        = 0;
                skipNext    = 0;
#if NOT_SQLITE_OMIT_INCRBLOB //#if !SQLITE_OMIT_INCRBLOB
                isIncrblobHandle = false;
                aOverflow        = null;
#endif
                iPage = 0;
            }
Example #7
0
        public bool isIncrblobHandle; // True if this cursor is an incr. io handle

        #endif

        #region Methods

        public void Clear()
        {
            pNext = null;
            pPrev = null;
            pKeyInfo = null;
            pgnoRoot = 0;
            cachedRowid = 0;
            info = new CellInfo();
            wrFlag = 0;
            atLast = 0;
            validNKey = false;
            eState = 0;
            pKey = null;
            nKey = 0;
            skipNext = 0;
            #if !SQLITE_OMIT_INCRBLOB
            isIncrblobHandle = false;
            aOverflow = null;
            #endif
            iPage = 0;
        }
Example #8
0
 public static RC CloseCursor(BtCursor cur)
 {
     var btree = cur.Btree;
     if (btree != null)
     {
         var bt = cur.Bt;
         btree.Enter();
         ClearCursor(cur);
         if (cur.Prev != null)
             cur.Prev.Next = cur.Next;
         else
             bt.Cursor = cur.Next;
         if (cur.Next != null)
             cur.Next.Prev = cur.Prev;
         for (var i = 0; i <= cur.ID; i++)
             releasePage(cur.Pages[i]);
         unlockBtreeIfUnused(bt);
         invalidateOverflowCache(cur);
         btree.Leave();
     }
     return RC.OK;
 }
Example #9
0
 public static RC CursorHasMoved(BtCursor cur, out bool hasMoved)
 {
     var rc = restoreCursorPosition(cur);
     if (rc != RC.OK)
     {
         hasMoved = true;
         return rc;
     }
     hasMoved = (cur.State != CURSOR.VALID || cur.SkipNext != 0);
     return RC.OK;
 }
Example #10
0
 // was:invalidateOverflowCache
 internal static void invalidateOverflowCache(BtCursor pCur)
 {
 }
Example #11
0
 static RC restoreCursorPosition(BtCursor cur)
 {
     return (cur.State >= CURSOR.REQUIRESEEK ? btreeRestoreCursorPosition(cur) : RC.OK);
 }
Example #12
0
 public static void CursorHints(BtCursor cur, uint mask)
 {
     Debug.Assert(mask == BTREE_BULKLOAD || mask == 0);
     cur.Hints = (byte)mask;
 }
Example #13
0
 //# define sqlite3BtreeEnterCursor(X)
 static void sqlite3BtreeEnterCursor(BtCursor X)
 {
 }
Example #14
0
        public static RC Delete(BtCursor cur)
        {
            Btree p = cur.Btree;
            BtShared bt = p.Bt;
            Debug.Assert(cursorHoldsMutex(cur));
            Debug.Assert(bt.InTransaction == TRANS.WRITE);
            Debug.Assert((bt.BtsFlags & BTS.READ_ONLY) == 0);
            Debug.Assert(cur.WrFlag);
            Debug.Assert(hasSharedCacheTableLock(p, cur.RootID, cur.KeyInfo != null, LOCK.WRITE));
            Debug.Assert(!hasReadConflicts(p, cur.RootID));

            if (C._NEVER(cur.Idxs[cur.ID] >= cur.Pages[cur.ID].Cells) || C._NEVER(cur.State != CURSOR.VALID))
                return RC.ERROR; // Something has gone awry.

            int cellDepth = cur.ID; // Depth of node containing pCell
            uint cellIdx = cur.Idxs[cellDepth]; // Index of cell to delete
            MemPage page = cur.Pages[cellDepth]; // Page to delete cell from
            uint cell_ = findCell(page, cellIdx); // Pointer to cell to delete

            // If the page containing the entry to delete is not a leaf page, move the cursor to the largest entry in the tree that is smaller than
            // the entry being deleted. This cell will replace the cell being deleted from the internal node. The 'previous' entry is used for this instead
            // of the 'next' entry, as the previous entry is always a part of the sub-tree headed by the child page of the cell being deleted. This makes
            // balancing the tree following the delete operation easier.
            RC rc;
            if (!page.Leaf)
            {
                int notUsed = 0;
                rc = Previous(cur, ref notUsed);
                if (rc != RC.OK) return rc;
            }

            // Save the positions of any other cursors open on this table before making any modifications. Make the page containing the entry to be 
            // deleted writable. Then free any overflow pages associated with the entry and finally remove the cell itself from within the page.  
            rc = saveAllCursors(bt, cur.RootID, cur);
            if (rc != RC.OK) return rc;

            // If this is a delete operation to remove a row from a table b-tree, invalidate any incrblob cursors open on the row being deleted.
            if (cur.KeyInfo == null)
                invalidateIncrblobCursors(p, cur.Info.Key, false);

            rc = Pager.Write(page.DBPage);
            if (rc != RC.OK) return rc;
            rc = clearCell(page, cell_);
            dropCell(page, cellIdx, cellSizePtr(page, cell_), ref rc);
            if (rc != RC.OK) return rc;

            // If the cell deleted was not located on a leaf page, then the cursor is currently pointing to the largest entry in the sub-tree headed
            // by the child-page of the cell that was just deleted from an internal node. The cell from the leaf node needs to be moved to the internal
            // node to replace the deleted cell.
            if (!page.Leaf)
            {
                MemPage leaf = cur.Pages[cur.ID];
                Pid n = cur.Pages[cellDepth + 1].ID;

                cell_ = findCell(leaf, (uint)leaf.Cells - 1);
                ushort sizeCell = cellSizePtr(leaf, cell_);
                Debug.Assert(MX_CELL_SIZE(bt) >= sizeCell);

                //allocateTempSpace(bt);
                //byte[] tmp = Bt.TmpSpace;

                rc = Pager.Write(leaf.DBPage);
                { byte[] cell_4 = new byte[sizeCell + 4]; Buffer.BlockCopy(leaf.Data, (int)cell_ - 4, cell_4, 0, (int)sizeCell + 4); insertCell(page, cellIdx, cell_4, (ushort)(sizeCell + 4), null, n, ref rc); }
                dropCell(leaf, (uint)(leaf.Cells - 1), sizeCell, ref rc);
                if (rc != RC.OK) return rc;
            }

            // Balance the tree. If the entry deleted was located on a leaf page, then the cursor still points to that page. In this case the first
            // call to balance() repairs the tree, and the if(...) condition is never true.
            //
            // Otherwise, if the entry deleted was on an internal node page, then pCur is pointing to the leaf page from which a cell was removed to
            // replace the cell deleted from the internal node. This is slightly tricky as the leaf node may be underfull, and the internal node may
            // be either under or overfull. In this case run the balancing algorithm on the leaf node first. If the balance proceeds far enough up the
            // tree that we can be sure that any problem in the internal node has been corrected, so be it. Otherwise, after balancing the leaf node,
            // walk the cursor up the tree to the internal node and balance it as well.
            rc = balance(cur);
            if (rc == RC.OK && cur.ID > cellDepth)
            {
                while (cur.ID > cellDepth)
                    releasePage(cur.Pages[cur.ID--]);
                rc = balance(cur);
            }

            if (rc == RC.OK)
                moveToRoot(cur);
            return rc;
        }
Example #15
0
        public static RC PutData(BtCursor cur, uint offset, uint amount, byte[] z)
        {
            Debug.Assert(cursorHoldsMutex(cur));
            Debug.Assert(MutexEx.Held(cur.Btree.Ctx.Mutex));
            Debug.Assert(cur.IsIncrblobHandle);

            RC rc = restoreCursorPosition(cur);
            if (rc != RC.OK)
                return rc;
            Debug.Assert(cur.State != CURSOR.REQUIRESEEK);
            if (cur.State != CURSOR.VALID)
                return RC.ABORT;

            // Check some assumptions: 
            //   (a) the cursor is open for writing,
            //   (b) there is a read/write transaction open,
            //   (c) the connection holds a write-lock on the table (if required),
            //   (d) there are no conflicting read-locks, and
            //   (e) the cursor points at a valid row of an intKey table.
            if (!cur.WrFlag)
                return RC.READONLY;
            Debug.Assert((cur.Bt.BtsFlags & BTS.READ_ONLY) == 0 && cur.Bt.InTransaction == TRANS.WRITE);
            Debug.Assert(hasSharedCacheTableLock(cur.Btree, cur.RootID, false, LOCK.WRITE));
            Debug.Assert(!hasReadConflicts(cur.Btree, cur.RootID));
            Debug.Assert(cur.Pages[cur.ID].IntKey);

            return accessPayload(cur, offset, amount, z, 1);
        }
Example #16
0
        public static RC Next_(BtCursor cur, ref int res)
        {
            Debug.Assert(cursorHoldsMutex(cur));
            var rc = restoreCursorPosition(cur);
            if (rc != RC.OK)
                return rc;
            if (cur.State == CURSOR.INVALID)
            {
                res = 1;
                return RC.OK;
            }
            if (cur.SkipNext > 0)
            {
                cur.SkipNext = 0;
                res = 0;
                return RC.OK;
            }
            cur.SkipNext = 0;

            MemPage page = cur.Pages[cur.ID];
            uint idx = ++cur.Idxs[cur.ID];
            Debug.Assert(page.IsInit);

            cur.Info.Size = 0;
            cur.ValidNKey = false;
            if (idx >= page.Cells)
            {
                if (!page.Leaf)
                {
                    rc = moveToChild(cur, ConvertEx.Get4(page.Data, page.HdrOffset + 8));
                    if (rc != RC.OK) return rc;
                    rc = moveToLeftmost(cur);
                    res = 0;
                    return rc;
                }
                do
                {
                    if (cur.ID == 0)
                    {
                        res = 1;
                        cur.State = CURSOR.INVALID;
                        return RC.OK;
                    }
                    moveToParent(cur);
                    page = cur.Pages[cur.ID];
                } while (cur.Idxs[cur.ID] >= page.Cells);
                res = 0;
                if (page.IntKey)
                    rc = Next_(cur, ref res);
                else
                    rc = RC.OK;
                return rc;
            }
            res = 0;
            if (page.Leaf)
                return RC.OK;
            rc = moveToLeftmost(cur);
            return rc;
        }
Example #17
0
            public u16[] aiIdx = new u16[BTCURSOR_MAX_DEPTH];          /* Current index in apPage[i] */

            public BtCursor Copy()
            {
                BtCursor cp = (BtCursor)MemberwiseClone();

                return(cp);
            }
Example #18
0
 static void btreeReleaseAllCursorPages(BtCursor cur)
 {
     for (int i = 0; i <= cur.ID; i++)
     {
         releasePage(cur.Pages[i]);
         cur.Pages[i] = null;
     }
     cur.ID = -1;
 }
Example #19
0
 public static bool Eof(BtCursor cur)
 {
     // TODO: What if the cursor is in CURSOR_REQUIRESEEK but all table entries have been deleted? This API will need to change to return an error code
     // as well as the boolean result value.
     return (cur.State != CURSOR.VALID);
 }
Example #20
0
        public static RC MovetoUnpacked(BtCursor cur, UnpackedRecord idxKey, long intKey, int biasRight, out int res)
        {
            Debug.Assert(cursorHoldsMutex(cur));
            Debug.Assert(MutexEx.Held(cur.Btree.Ctx.Mutex));
            Debug.Assert((idxKey == null) == (cur.KeyInfo == null));

            // If the cursor is already positioned at the point we are trying to move to, then just return without doing any work
            if (cur.State == CURSOR.VALID && cur.ValidNKey && cur.Pages[0].IntKey)
            {
                if (cur.Info.Key == intKey)
                {
                    res = 0;
                    return RC.OK;
                }
                if (cur.AtLast != 0 && cur.Info.Key < intKey)
                {
                    res = -1;
                    return RC.OK;
                }
            }

            res = -1;
            var rc = moveToRoot(cur);
            if (rc != RC.OK)
                return rc;
            Debug.Assert(cur.RootID == 0 || cur.Pages[cur.ID] != null);
            Debug.Assert(cur.RootID == 0 || cur.Pages[cur.ID].IsInit);
            Debug.Assert(cur.State == CURSOR.INVALID || cur.Pages[cur.ID].Cells > 0);
            if (cur.State == CURSOR.INVALID)
            {
                res = -1;
                Debug.Assert(cur.RootID == 0 || cur.Pages[cur.ID].Cells == 0);
                return RC.OK;
            }
            Debug.Assert(cur.Pages[0].IntKey || idxKey != null);
            int c;
            for (; ; )
            {
                // pPage->nCell must be greater than zero. If this is the root-page the cursor would have been INVALID above and this for(;;) loop
                // not run. If this is not the root-page, then the moveToChild() routine would have already detected db corruption. Similarly, pPage must
                // be the right kind (index or table) of b-tree page. Otherwise a moveToChild() or moveToRoot() call would have detected corruption.
                MemPage page = cur.Pages[cur.ID];
                Debug.Assert(page.Cells > 0);
                Debug.Assert(page.IntKey == (idxKey == null));
                uint idx;
                uint lwr = 0;
                uint upr = (uint)(page.Cells - 1);
                if (biasRight != 0)
                    cur.Idxs[cur.ID] = (ushort)(idx = upr);
                else
                    cur.Idxs[cur.ID] = (ushort)(idx = (upr + lwr) / 2);
                for (; ; )
                {
                    Debug.Assert(idx == cur.Idxs[cur.ID]);
                    cur.Info.Size = 0;
                    uint cell_ = findCell(page, idx) + page.ChildPtrSize; // Pointer to current cell in pPage
                    if (page.IntKey)
                    {
                        if (page.HasData)
                        {
                            uint dummy = 0;
                            cell_ += ConvertEx.GetVarint32(page.Data, cell_, out dummy);
                        }
                        long cellKeyLength = 0;
                        ConvertEx.GetVarint(page.Data, cell_, out cellKeyLength);
                        if (cellKeyLength == intKey)
                            c = 0;
                        else if (cellKeyLength < intKey)
                            c = -1;
                        else
                            c = +1;
                        cur.ValidNKey = true;
                        cur.Info.Key = cellKeyLength;
                    }
                    else
                    {
                        // The maximum supported page-size is 65536 bytes. This means that the maximum number of record bytes stored on an index B-Tree
                        // page is less than 16384 bytes and may be stored as a 2-byte varint. This information is used to attempt to avoid parsing 
                        // the entire cell by checking for the cases where the record is stored entirely within the b-tree page by inspecting the first 
                        // 2 bytes of the cell.
                        int cellLength = page.Data[cell_ + 0];
                        if (cellLength <= page.Max1bytePayload)
                        {
                            // This branch runs if the record-size field of the cell is a single byte varint and the record fits entirely on the main b-tree page.
                            c = _vdbe.RecordCompare(cellLength, page.Data, cell_ + 1, idxKey);
                        }
                        else if ((page.Data[cell_ + 1] & 0x80) == 0 && (cellLength = ((cellLength & 0x7f) << 7) + page.Data[cell_ + 1]) <= page.MaxLocal)
                        {
                            // The record-size field is a 2 byte varint and the record fits entirely on the main b-tree page.
                            c = _vdbe.RecordCompare(cellLength, page.Data, cell_ + 2, idxKey);
                        }
                        else
                        {
                            // The record flows over onto one or more overflow pages. In this case the whole cell needs to be parsed, a buffer allocated
                            // and accessPayload() used to retrieve the record into the buffer before VdbeRecordCompare() can be called.
                            var cellBody = new byte[page.Data.Length - cell_ + page.ChildPtrSize];
                            Buffer.BlockCopy(page.Data, (int)cell_ - page.ChildPtrSize, cellBody, 0, cellBody.Length);
                            btreeParseCellPtr(page, cellBody, ref cur.Info);
                            cellLength = (int)cur.Info.Key;
                            var cellKey = C._alloc(cellLength);
                            rc = accessPayload(cur, 0, (uint)cellLength, cellKey, 0);
                            if (rc != RC.OK)
                            {
                                cellKey = null;
                                goto moveto_finish;
                            }
                            c = _vdbe.RecordCompare(cellLength, cellKey, 0, idxKey);
                            cellKey = null;
                        }
                    }
                    if (c == 0)
                    {
                        if (page.IntKey && !page.Leaf)
                        {
                            lwr = idx;
                            upr = lwr - 1;
                            break;
                        }
                        else
                        {
                            res = 0;
                            rc = RC.OK;
                            goto moveto_finish;
                        }
                    }
                    if (c < 0)
                        lwr = idx + 1;
                    else
                        upr = idx - 1;
                    if (lwr > upr)
                        break;
                    cur.Idxs[cur.ID] = (ushort)(idx = (lwr + upr) / 2);
                }
                Debug.Assert(lwr == upr + 1 || (page.IntKey && !page.Leaf));
                Debug.Assert(page.IsInit);
                Pid childID;
                if (page.Leaf)
                    childID = 0;
                else if (lwr >= page.Cells)
                    childID = ConvertEx.Get4(page.Data, page.HdrOffset + 8);
                else
                    childID = ConvertEx.Get4(page.Data, findCell(page, lwr));
                if (childID == 0)
                {
                    Debug.Assert(cur.Idxs[cur.ID] < cur.Pages[cur.ID].Cells);
                    res = (int)c;
                    rc = RC.OK;
                    goto moveto_finish;
                }
                cur.Idxs[cur.ID] = (ushort)lwr;
                cur.Info.Size = 0;
                cur.ValidNKey = false;
                rc = moveToChild(cur, childID);
                if (rc != RC.OK) goto moveto_finish;
            }
        moveto_finish:
            return rc;
        }
Example #21
0
        public static RC Last(BtCursor cur, ref int res)
        {
            Debug.Assert(cursorHoldsMutex(cur));
            Debug.Assert(MutexEx.Held(cur.Btree.Ctx.Mutex));

            // If the cursor already points to the last entry, this is a no-op.
            if (cur.State == CURSOR.VALID && cur.AtLast != 0)
            {
#if DEBUG
                // This block serves to Debug.Assert() that the cursor really does point to the last entry in the b-tree.
                for (var ii = 0; ii < cur.ID; ii++)
                    Debug.Assert(cur.Idxs[ii] == cur.Pages[ii].Cells);
                Debug.Assert(cur.Idxs[cur.ID] == cur.Pages[cur.ID].Cells - 1);
                Debug.Assert(cur.Pages[cur.ID].Leaf);
#endif
                return RC.OK;
            }

            var rc = moveToRoot(cur);
            if (rc == RC.OK)
            {
                if (cur.State == CURSOR.INVALID)
                {
                    Debug.Assert(cur.Pages[cur.ID].Cells == 0);
                    res = 1;
                }
                else
                {
                    Debug.Assert(cur.State == CURSOR.VALID);
                    res = 0;
                    rc = moveToRightmost(cur);
                    cur.AtLast = (byte)(rc == RC.OK ? 1 : 0);
                }
            }
            return rc;
        }
Example #22
0
 public static RC First(BtCursor cur, ref int res)
 {
     Debug.Assert(cursorHoldsMutex(cur));
     Debug.Assert(MutexEx.Held(cur.Btree.Ctx.Mutex));
     var rc = moveToRoot(cur);
     if (rc == RC.OK)
     {
         if (cur.State == CURSOR.INVALID)
         {
             Debug.Assert(cur.Pages[cur.ID].Cells == 0);
             res = 1;
         }
         else
         {
             Debug.Assert(cur.Pages[cur.ID].Cells > 0);
             res = 0;
             rc = moveToLeftmost(cur);
         }
     }
     return rc;
 }
Example #23
0
        static RC balance(BtCursor cur)
        {
#if DEBUG
            int balance_quick_called = 0;
            int balance_deeper_called = 0;
#else
            int balance_quick_called = 0;
            int balance_deeper_called = 0;
#endif

            byte[] free = null;
            int min = (int)cur.Bt.UsableSize * 2 / 3;
            RC rc = RC.OK;
            do
            {
                int pageID = cur.ID;
                MemPage page = cur.Pages[pageID];

                if (pageID == 0)
                {
                    if (page.Overflows != 0)
                    {
                        // The root page of the b-tree is overfull. In this case call the balance_deeper() function to create a new child for the root-page
                        // and copy the current contents of the root-page to it. The next iteration of the do-loop will balance the child page.
                        Debug.Assert(balance_deeper_called++ == 0);
                        rc = balance_deeper(page, ref cur.Pages[1]);
                        if (rc == RC.OK)
                        {
                            cur.ID = 1;
                            cur.Idxs[0] = 0;
                            cur.Idxs[1] = 0;
                            Debug.Assert(cur.Pages[1].Overflows != 0);
                        }
                    }
                    else
                        break;
                }
                else if (page.Overflows == 0 && page.Frees <= min)
                    break;
                else
                {
                    MemPage parent = cur.Pages[pageID - 1];
                    ushort idx = cur.Idxs[pageID - 1];

                    rc = Pager.Write(parent.DBPage);
                    if (rc == RC.OK)
                    {
#if !OMIT_QUICKBALANCE
                        if (page.HasData && page.Overflows == 1 && page.OvflIdxs[0] == page.Cells && parent.ID != 1 && parent.Cells == idx)
                        {
                            // Call balance_quick() to create a new sibling of pPage on which to store the overflow cell. balance_quick() inserts a new cell
                            // into pParent, which may cause pParent overflow. If this happens, the next interation of the do-loop will balance pParent 
                            // use either balance_nonroot() or balance_deeper(). Until this happens, the overflow cell is stored in the aBalanceQuickSpace[] buffer. 
                            //
                            // The purpose of the following assert() is to check that only a single call to balance_quick() is made for each call to this
                            // function. If this were not verified, a subtle bug involving reuse of the aBalanceQuickSpace[] might sneak in.
                            Debug.Assert((balance_quick_called++) == 0);
                            rc = balance_quick(parent, page, _balanceQuickSpace);
                        }
                        else
#endif
                        {
                            // In this case, call balance_nonroot() to redistribute cells between pPage and up to 2 of its sibling pages. This involves
                            // modifying the contents of pParent, which may cause pParent to become overfull or underfull. The next iteration of the do-loop
                            // will balance the parent page to correct this.
                            // 
                            // If the parent page becomes overfull, the overflow cell or cells are stored in the pSpace buffer allocated immediately below. 
                            // A subsequent iteration of the do-loop will deal with this by calling balance_nonroot() (balance_deeper() may be called first,
                            // but it doesn't deal with overflow cells - just moves them to a different page). Once this subsequent call to balance_nonroot() 
                            // has completed, it is safe to release the pSpace buffer used by the previous call, as the overflow cell data will have been 
                            // copied either into the body of a database page or into the new pSpace buffer passed to the latter call to balance_nonroot().
                            byte[] space = null; //PCache.PageAlloc2((int)cur.Bt.PageSize);
                            rc = balance_nonroot(parent, idx, space, pageID == 1, cur.Hints != 0);
                            if (free != null)
                            {
                                // If pFree is not NULL, it points to the pSpace buffer used  by a previous call to balance_nonroot(). Its contents are
                                // now stored either on real database pages or within the new pSpace buffer, so it may be safely freed here.
                                PCache.PageFree2(ref free);
                            }

                            // The pSpace buffer will be freed after the next call to balance_nonroot(), or just before this function returns, whichever comes first.
                            free = space;
                        }
                    }

                    page.Overflows = 0;

                    // The next iteration of the do-loop balances the parent page.
                    releasePage(page);
                    cur.ID--;
                }
            } while (rc == RC.OK);

            if (free != null)
                PCache.PageFree2(ref free);
            return rc;
        }
Example #24
0
 private static void sqlite3MemFreeBtCursor(ref BtCursor pPrior)
 {
     pPrior = null;
 }
Example #25
0
        public static RC Insert(BtCursor cur, byte[] key, long keyLength, byte[] data, int dataLength, int zero, int appendBias, int seekResult)
        {
            if (cur.State == CURSOR.FAULT)
            {
                Debug.Assert(cur.SkipNext != 0);
                return (RC)cur.SkipNext;
            }

            Btree p = cur.Btree;
            BtShared bt = p.Bt;
            Debug.Assert(cursorHoldsMutex(cur));
            Debug.Assert(cur.WrFlag && bt.InTransaction == TRANS.WRITE && (bt.BtsFlags & BTS.READ_ONLY) == 0);
            Debug.Assert(hasSharedCacheTableLock(p, cur.RootID, cur.KeyInfo != null, LOCK.WRITE));

            // Assert that the caller has been consistent. If this cursor was opened expecting an index b-tree, then the caller should be inserting blob
            // keys with no associated data. If the cursor was opened expecting an intkey table, the caller should be inserting integer keys with a
            // blob of associated data.
            Debug.Assert((key == null) == (cur.KeyInfo == null));

            // Save the positions of any other cursors open on this table.
            //
            // In some cases, the call to btreeMoveto() below is a no-op. For example, when inserting data into a table with auto-generated integer
            // keys, the VDBE layer invokes sqlite3BtreeLast() to figure out the integer key to use. It then calls this function to actually insert the 
            // data into the intkey B-Tree. In this case btreeMoveto() recognizes that the cursor is already where it needs to be and returns without
            // doing any work. To avoid thwarting these optimizations, it is important not to clear the cursor here.
            RC rc = saveAllCursors(bt, cur.RootID, cur);
            if (rc != RC.OK) return rc;

            // If this is an insert into a table b-tree, invalidate any incrblob cursors open on the row being replaced (assuming this is a replace
            // operation - if it is not, the following is a no-op).
            if (cur.KeyInfo == null)
                invalidateIncrblobCursors(p, keyLength, false);

            int loc = seekResult; // -1: before desired location  +1: after
            if (loc == 0)
            {
                rc = btreeMoveto(cur, key, keyLength, appendBias, ref loc);
                if (rc != RC.OK) return rc;
            }
            Debug.Assert(cur.State == CURSOR.VALID || (cur.State == CURSOR.INVALID && loc != 0));

            MemPage page = cur.Pages[cur.ID];
            Debug.Assert(page.IntKey || keyLength >= 0);
            Debug.Assert(page.Leaf || !page.IntKey);

            TRACE("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n", cur.RootID, keyLength, dataLength, page.ID, loc == 0 ? "overwrite" : "new entry");
            Debug.Assert(page.IsInit);
            allocateTempSpace(bt);
            byte[] newCell = bt.TmpSpace;
            if (newCell == null) return RC.NOMEM;
            ushort sizeNew = 0;
            rc = fillInCell(page, newCell, key, keyLength, data, dataLength, zero, ref sizeNew);
            if (rc != RC.OK) goto end_insert;
            Debug.Assert(sizeNew == cellSizePtr(page, newCell));
            Debug.Assert(sizeNew <= MX_CELL_SIZE(bt));
            uint idx = cur.Idxs[cur.ID];
            if (loc == 0)
            {
                Debug.Assert(idx < page.Cells);
                rc = Pager.Write(page.DBPage);
                if (rc != RC.OK)
                    goto end_insert;
                uint oldCell_ = findCell(page, idx);
                if (!page.Leaf)
                {
                    //_memcpy(newCell, oldCell, 4);
                    newCell[0] = page.Data[oldCell_ + 0];
                    newCell[1] = page.Data[oldCell_ + 1];
                    newCell[2] = page.Data[oldCell_ + 2];
                    newCell[3] = page.Data[oldCell_ + 3];
                }
                ushort sizeOld = cellSizePtr(page, oldCell_);
                rc = clearCell(page, oldCell_);
                dropCell(page, idx, sizeOld, ref rc);
                if (rc != RC.OK) goto end_insert;
            }
            else if (loc < 0 && page.Cells > 0)
            {
                Debug.Assert(page.Leaf);
                idx = ++cur.Idxs[cur.ID];
            }
            else
                Debug.Assert(page.Leaf);
            insertCell(page, idx, newCell, sizeNew, null, 0, ref rc);
            Debug.Assert(rc != RC.OK || page.Cells > 0 || page.Overflows > 0);

            // If no error has occurred and pPage has an overflow cell, call balance() to redistribute the cells within the tree. Since balance() may move
            // the cursor, zero the BtCursor.info.nSize and BtCursor.validNKey variables.
            //
            // Previous versions of SQLite called moveToRoot() to move the cursor back to the root page as balance() used to invalidate the contents
            // of BtCursor.apPage[] and BtCursor.aiIdx[]. Instead of doing that, set the cursor state to "invalid". This makes common insert operations
            // slightly faster.
            //
            // There is a subtle but important optimization here too. When inserting multiple records into an intkey b-tree using a single cursor (as can
            // happen while processing an "INSERT INTO ... SELECT" statement), it is advantageous to leave the cursor pointing to the last entry in
            // the b-tree if possible. If the cursor is left pointing to the last entry in the table, and the next row inserted has an integer key
            // larger than the largest existing key, it is possible to insert the row without seeking the cursor. This can be a big performance boost.
            cur.Info.Size = 0;
            cur.ValidNKey = false;
            if (rc == RC.OK && page.Overflows != 0)
            {
                rc = balance(cur);

                // Must make sure nOverflow is reset to zero even if the balance() fails. Internal data structure corruption will result otherwise. 
                // Also, set the cursor state to invalid. This stops saveCursorPosition() from trying to save the current position of the cursor.
                cur.Pages[cur.ID].Overflows = 0;
                cur.State = CURSOR.INVALID;
            }
            Debug.Assert(cur.Pages[cur.ID].Overflows == 0);

        end_insert:
            return rc;
        }
Example #26
0
void sqlite3BtreeLeaveCursor(BtCursor *pCur){
  sqlite3BtreeLeave(pCur->pBtree);
}
Example #27
0
        public static RC Count(BtCursor cur, ref long entrysRef)
        {
            if (cur.RootID == 0)
            {
                entrysRef = 0;
                return RC.OK;
            }
            var rc = moveToRoot(cur);

            // Unless an error occurs, the following loop runs one iteration for each page in the B-Tree structure (not including overflow pages).
            long entrys = 0; // Value to return in pnEntry
            while (rc == RC.OK)
            {
                // If this is a leaf page or the tree is not an int-key tree, then this page contains countable entries. Increment the entry counter accordingly.
                MemPage page = cur.Pages[cur.ID]; // Current page of the b-tree
                if (page.Leaf || !page.IntKey)
                    entrys += page.Cells;

                // pPage is a leaf node. This loop navigates the cursor so that it points to the first interior cell that it points to the parent of
                // the next page in the tree that has not yet been visited. The pCur->aiIdx[pCur->iPage] value is set to the index of the parent cell
                // of the page, or to the number of cells in the page if the next page to visit is the right-child of its parent.
                //
                // If all pages in the tree have been visited, return SQLITE_OK to the caller.
                if (page.Leaf)
                {
                    do
                    {
                        if (cur.ID == 0)
                        {
                            // All pages of the b-tree have been visited. Return successfully.
                            entrysRef = entrys;
                            return RC.OK;
                        }
                        moveToParent(cur);
                    } while (cur.Idxs[cur.ID] >= cur.Pages[cur.ID].Cells);

                    cur.Idxs[cur.ID]++;
                    page = cur.Pages[cur.ID];
                }

                // Descend to the child node of the cell that the cursor currently points at. This is the right-child if (iIdx==pPage->nCell).
                uint idx = cur.Idxs[cur.ID]; // Index of child node in parent
                if (idx == page.Cells)
                    rc = moveToChild(cur, ConvertEx.Get4(page.Data, page.HdrOffset + 8));
                else
                    rc = moveToChild(cur, ConvertEx.Get4(page.Data, findCell(page, idx)));
            }

            // An error has occurred. Return an error code.
            return rc;
        }
Example #28
0
		/*
		** Move data out of a btree key or data field and into a Mem structure.
		** The data or key is taken from the entry that pCur is currently pointing
		** to.  offset and amt determine what portion of the data or key to retrieve.
		** key is true to get the key or false to get data.  The result is written
		** into the pMem element.
		**
		** The pMem structure is assumed to be uninitialized.  Any prior content
		** is overwritten without being freed.
		**
		** If this routine fails for any reason (malloc returns NULL or unable
		** to read from the disk) then the pMem is left in an inconsistent state.
		*/

		private static int sqlite3VdbeMemFromBtree(
		BtCursor pCur,    /* Cursor pointing at record to retrieve. */
		int offset,       /* Offset from the start of data to return bytes from. */
		int amt,          /* Number of bytes to return. */
		bool key,         /* If true, retrieve from the btree key, not data. */
		Mem pMem          /* OUT: Return data in this Mem structure. */
		)
		{
			byte[] zData;       /* Data from the btree layer */
			int available = 0; /* Number of bytes available on the local btree page */
			int rc = SQLITE_OK; /* Return code */

			Debug.Assert(sqlite3BtreeCursorIsValid(pCur));

			/* Note: the calls to BtreeKeyFetch() and DataFetch() below assert()
			** that both the BtShared and database handle mutexes are held. */
			Debug.Assert((pMem.flags & MEM_RowSet) == 0);
			int outOffset = -1;
			if (key)
			{
				zData = sqlite3BtreeKeyFetch(pCur, ref available, ref outOffset);
			}
			else
			{
				zData = sqlite3BtreeDataFetch(pCur, ref available, ref outOffset);
			}
			Debug.Assert(zData != null);

			if (offset + amt <= available && (pMem.flags & MEM_Dyn) == 0)
			{
				sqlite3VdbeMemRelease(pMem);
				pMem.zBLOB = sqlite3Malloc(amt);
				Buffer.BlockCopy(zData, offset, pMem.zBLOB, 0, amt);//pMem.z = &zData[offset];
				pMem.flags = MEM_Blob | MEM_Ephem;
			}
			else if (SQLITE_OK == (rc = sqlite3VdbeMemGrow(pMem, amt + 2, 0)))
			{
				pMem.enc = 0;
				pMem.type = SQLITE_BLOB;
				pMem.z = null;
				pMem.zBLOB = sqlite3Malloc(amt);
				pMem.flags = MEM_Blob | MEM_Dyn | MEM_Term;
				if (key)
				{
					rc = sqlite3BtreeKey(pCur, (u32)offset, (u32)amt, pMem.zBLOB);
				}
				else
				{
					rc = sqlite3BtreeData(pCur, (u32)offset, (u32)amt, pMem.zBLOB);//pMem.z =  pMem_z ;
				}
				//pMem.z[amt] = 0;
				//pMem.z[amt+1] = 0;
				if (rc != SQLITE_OK)
				{
					sqlite3VdbeMemRelease(pMem);
				}
			}
			pMem.n = amt;
			sqlite3_free(ref zData);

			return rc;
		}
Example #29
0
 public static void CacheOverflow(BtCursor cur)
 {
     Debug.Assert(cursorHoldsMutex(cur));
     Debug.Assert(MutexEx.Held(cur.Btree.Ctx.Mutex));
     invalidateOverflowCache(cur);
     cur.IsIncrblobHandle = true;
 }
Example #30
0
 //# define sqlite3BtreeLeaveCursor(X)
 static void sqlite3BtreeLeaveCursor( BtCursor X )
 {
 }
Example #31
0
        static RC saveCursorPosition(BtCursor cur)
        {
            Debug.Assert(cur.State == CURSOR.VALID);
            Debug.Assert(cur.Key == null);
            Debug.Assert(cursorHoldsMutex(cur));

            var rc = KeySize(cur, ref cur.KeyLength);
            Debug.Assert(rc == RC.OK);  // KeySize() cannot fail

            // If this is an intKey table, then the above call to BtreeKeySize() stores the integer key in pCur.nKey. In this case this value is
            // all that is required. Otherwise, if pCur is not open on an intKey table, then malloc space for and store the pCur.nKey bytes of key data.
            if (!cur.Pages[0].IntKey)
            {
                var key = C._alloc((int)cur.KeyLength);
                rc = Key(cur, 0, (uint)cur.KeyLength, key);
                if (rc == RC.OK)
                    cur.Key = key;
            }
            Debug.Assert(!cur.Pages[0].IntKey || cur.Key == null);

            if (rc == RC.OK)
            {
                btreeReleaseAllCursorPages(cur);
                cur.State = CURSOR.REQUIRESEEK;
            }

            invalidateOverflowCache(cur);
            return rc;
        }
Example #32
0
 //# define sqlite3BtreeLeaveCursor(X)
 static void sqlite3BtreeLeaveCursor(BtCursor X)
 {
 }
Example #33
0
 private static BtCursor sqlite3MemMallocBtCursor(BtCursor dummy)
 {
     return(new BtCursor());
 }
Example #34
0
        public static RC Previous(BtCursor cur, ref int res)
        {
            Debug.Assert(cursorHoldsMutex(cur));
            var rc = restoreCursorPosition(cur);
            if (rc != RC.OK)
                return rc;
            cur.AtLast = 0;
            if (cur.State == CURSOR.INVALID)
            {
                res = 1;
                return RC.OK;
            }
            if (cur.SkipNext < 0)
            {
                cur.SkipNext = 0;
                res = 0;
                return RC.OK;
            }
            cur.SkipNext = 0;

            MemPage page = cur.Pages[cur.ID];
            Debug.Assert(page.IsInit);
            if (!page.Leaf)
            {
                uint idx = cur.Idxs[cur.ID];
                rc = moveToChild(cur, ConvertEx.Get4(page.Data, findCell(page, idx)));
                if (rc != RC.OK)
                    return rc;
                rc = moveToRightmost(cur);
            }
            else
            {
                while (cur.Idxs[cur.ID] == 0)
                {
                    if (cur.ID == 0)
                    {
                        cur.State = CURSOR.INVALID;
                        res = 1;
                        return RC.OK;
                    }
                    moveToParent(cur);
                }
                cur.Info.Size = 0;
                cur.ValidNKey = false;

                cur.Idxs[cur.ID]--;
                page = cur.Pages[cur.ID];
                if (page.IntKey && !page.Leaf)
                    rc = Previous(cur, ref res);
                else
                    rc = RC.OK;
            }
            res = 0;
            return rc;
        }
Example #35
0
/*
** Enter and leave a mutex on a Btree given a cursor owned by that
** Btree.  These entry points are used by incremental I/O and can be
** omitted if that module is not used.
*/
void sqlite3BtreeEnterCursor(BtCursor *pCur){
  sqlite3BtreeEnter(pCur->pBtree);
}
Example #36
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;
 }
Example #37
0
    /*
    ** Usage:   btree_cursor ID TABLENUM WRITEABLE
    **
    ** Create a new cursor.  Return the ID for the cursor.
    */
    static int btree_cursor(
    object NotUsed,
    Tcl_Interp interp,    /* The TCL interpreter that invoked this command */
    int argc,              /* Number of arguments */
    TclObject[] argv      /* Text of each argument */
    )
    {
      Btree pBt;
      int iTable = 0;
      BtCursor pCur;
      int rc = SQLITE_OK;
      int wrFlag = 0;
      StringBuilder zBuf = new StringBuilder( 30 );

      if ( argc != 4 )
      {
        TCL.Tcl_AppendResult( interp, "wrong # args: should be \"", argv[0].ToString(),
        " ID TABLENUM WRITEABLE\"" );
        return TCL.TCL_ERROR;
      }
      pBt = (Btree)sqlite3TestTextToPtr( interp, argv[1].ToString() );
      if ( TCL.Tcl_GetInt( interp, argv[2], out iTable ) )
        return TCL.TCL_ERROR;
      if ( TCL.Tcl_GetBoolean( interp, argv[3], out wrFlag ) )
        return TCL.TCL_ERROR;
      //pCur = (BtCursor )ckalloc(sqlite3BtreeCursorSize());
      pCur = new BtCursor();// memset( pCur, 0, sqlite3BtreeCursorSize() );
      sqlite3BtreeEnter( pBt );
#if !SQLITE_OMIT_SHARED_CACHE
      rc = sqlite3BtreeLockTable( pBt, iTable, wrFlag );
#endif
      if ( rc == SQLITE_OK )
      {
        rc = sqlite3BtreeCursor( pBt, iTable, wrFlag, null, pCur );
      }
      sqlite3BtreeLeave( pBt );
      if ( rc != 0 )
      {
        pCur = null;// ckfree( pCur );
        TCL.Tcl_AppendResult( interp, errorName( rc ), null );
        ;
        return TCL.TCL_ERROR;
      }
      sqlite3_snprintf( 30, zBuf, "->%p", pCur );
      if ( TCL.Tcl_CreateCommandPointer( interp, zBuf, pCur ) )
      {
        return TCL.TCL_ERROR;
      }
      else
        TCL.Tcl_AppendResult( interp, zBuf );
      return SQLITE_OK;
    }
Example #38
0
 public static void ClearCursor(BtCursor cur)
 {
     Debug.Assert(cursorHoldsMutex(cur));
     C._free(ref cur.Key);
     cur.State = CURSOR.INVALID;
 }
Example #39
0
 //# define sqlite3BtreeEnterCursor(X)
 static void sqlite3BtreeEnterCursor( BtCursor X )
 {
 }
Example #40
0
 static RC btreeMoveto(BtCursor cur, byte[] key, long keyLength, int bias, ref int res)
 {
     UnpackedRecord idxKey; // Unpacked index key
     object free;
     if (key != null)
     {
         Debug.Assert(keyLength == (long)(int)keyLength);
         idxKey = _vdbe.AllocUnpackedRecord(cur.KeyInfo, null, 0, out free);
         if (idxKey == null) return RC.NOMEM;
         _vdbe.RecordUnpack(cur.KeyInfo, (int)keyLength, key, idxKey);
     }
     else
         idxKey = null;
     var rc = MovetoUnpacked(cur, idxKey, keyLength, (bias != 0 ? 1 : 0), out res);
     return rc;
 }
Example #41
0
 // was:invalidateOverflowCache
 internal static void invalidateOverflowCache(BtCursor pCur) { }
Example #42
0
 static RC btreeRestoreCursorPosition(BtCursor cur)
 {
     Debug.Assert(cursorHoldsMutex(cur));
     Debug.Assert(cur.State >= CURSOR.REQUIRESEEK);
     if (cur.State == CURSOR.FAULT)
         return (RC)cur.SkipNext;
     cur.State = CURSOR.INVALID;
     var rc = btreeMoveto(cur, cur.Key, cur.KeyLength, 0, ref cur.SkipNext);
     if (rc == RC.OK)
     {
         cur.Key = null;
         Debug.Assert(cur.State == CURSOR.VALID || cur.State == CURSOR.INVALID);
     }
     return rc;
 }