Example #1
0
        public bool MovePrev()
        {
            while (true)
            {
                _currentPage.LastSearchPosition--;
                if (_currentPage.LastSearchPosition >= 0)
                {
                    // run out of entries, need to select the next page...
                    while (_currentPage.IsBranch)
                    {
                        _cursor.Push(_currentPage);
                        var node = _currentPage.GetNode(_currentPage.LastSearchPosition);
                        _currentPage = _tx.GetReadOnlyPage(node->PageNumber);
                        _currentPage.LastSearchPosition = _currentPage.NumberOfEntries - 1;
                    }
                    var current = _currentPage.GetNode(_currentPage.LastSearchPosition);
                    if (this.ValidateCurrentKey(current, _currentPage) == false)
                    {
                        return(false);
                    }

                    _currentPage.SetNodeKey(current, ref _currentInternalKey);
                    _currentKey = _currentInternalKey.ToSlice();
                    return(true);                   // there is another entry in this page
                }
                if (_cursor.PageCount == 0)
                {
                    break;
                }
                _currentPage = _cursor.Pop();
            }
            _currentPage = null;
            return(false);
        }
Example #2
0
        private void RemoveBranchWithOneEntry(Page page, Page parentPage)
        {
            Debug.Assert(page.NumberOfEntries == 1);

            var pageRefNumber = page.GetNode(0)->PageNumber;

            NodeHeader *nodeHeader = null;

            for (int i = 0; i < parentPage.NumberOfEntries; i++)
            {
                nodeHeader = parentPage.GetNode(i);

                if (nodeHeader->PageNumber == page.PageNumber)
                {
                    break;
                }
            }

            Debug.Assert(nodeHeader->PageNumber == page.PageNumber, string.Format("Node page number: {0}, page number: {1}", nodeHeader->PageNumber, page.PageNumber));

            nodeHeader->PageNumber = pageRefNumber;

            if (_cursor.CurrentPage.PageNumber == page.PageNumber)
            {
                _cursor.Pop();
                _cursor.Push(_tx.GetReadOnlyPage(pageRefNumber));
            }

            _tree.FreePage(page);
        }
Example #3
0
        public bool Seek(Slice key)
        {
            _currentPage = _tree.FindPageFor(_tx, key, _cursor);
            _cursor.Pop();
            var node = _currentPage.Search(key, _cmp);

            if (node == null)
            {
                return(false);
            }
            _currentKey.Set(node);
            return(this.ValidateCurrentKey(Current, _cmp));
        }
Example #4
0
 public PageSplitter(Transaction tx,
     Tree tree,
     SliceComparer cmp,
     Slice newKey,
     int len,
     long pageNumber,
     NodeFlags nodeType,
     ushort nodeVersion,
     Cursor cursor,
     TreeMutableState treeState)
 {
     _tx = tx;
     _tree = tree;
     _cmp = cmp;
     _newKey = newKey;
     _len = len;
     _pageNumber = pageNumber;
     _nodeType = nodeType;
     _nodeVersion = nodeVersion;
     _cursor = cursor;
     _treeState = treeState;
     Page page = _cursor.Pages.First.Value;
     _page = tx.ModifyPage(page.PageNumber, page);
     _cursor.Pop();
 }
        public PageSplitter(Transaction tx,
                            Tree tree,
                            SliceComparer cmp,
                            Slice newKey,
                            int len,
                            long pageNumber,
                            NodeFlags nodeType,
                            ushort nodeVersion,
                            Cursor cursor,
                            TreeMutableState treeState)
        {
            _tx          = tx;
            _tree        = tree;
            _cmp         = cmp;
            _newKey      = newKey;
            _len         = len;
            _pageNumber  = pageNumber;
            _nodeType    = nodeType;
            _nodeVersion = nodeVersion;
            _cursor      = cursor;
            _treeState   = treeState;
            Page page = _cursor.Pages.First.Value;

            _page = tx.ModifyPage(page.PageNumber, page);
            _cursor.Pop();
        }
Example #6
0
        private void RebalanceRoot(Cursor cursor, Page page)
        {
            if (page.NumberOfEntries == 0)
            {
                return; // nothing to do
            }
            if (!page.IsBranch || page.NumberOfEntries > 1)
            {
                return; // cannot do anything here
            }
            // in this case, we have a root pointer with just one pointer, we can just swap it out

            var node = page.GetNode(0);

            Debug.Assert(node->Flags == (NodeFlags.PageRef));

            _tree.State.LeafPages   = 1;
            _tree.State.BranchPages = 0;
            _tree.State.Depth       = 1;
            _tree.State.PageCount   = 1;

            var rootPage = _tx.ModifyPage(node->PageNumber, null);

            _tree.State.RootPageNumber = rootPage.PageNumber;

            Debug.Assert(rootPage.Dirty);

            cursor.Pop();
            cursor.Push(rootPage);

            _tx.FreePage(page.PageNumber);
        }
Example #7
0
        public bool Seek(Slice key)
        {
            if (_disposed)
            {
                throw new ObjectDisposedException("TreeIterator " + _tree.Name);
            }

            Lazy <Cursor> lazy;
            NodeHeader *  node;

            _currentPage = _tree.FindPageFor(key, out node, out lazy);
            _cursor      = lazy.Value;
            _cursor.Pop();

            if (node != null)
            {
                _currentPage.SetNodeKey(node, ref _currentInternalKey);
                _currentKey = _currentInternalKey.ToSlice();

                return(this.ValidateCurrentKey(Current, _currentPage));
            }

            // The key is not found in the db, but we are Seek()ing for equals or starts with.
            // We know that the exact value isn't there, but it is possible that the next page has values
            // that is actually greater than the key, so we need to check it as well.

            _currentPage.LastSearchPosition = _currentPage.NumberOfEntries; // force next MoveNext to move to the next _page_.
            return(MoveNext());
        }
Example #8
0
        public bool MovePrev()
        {
            if (_disposed)
            {
                throw new ObjectDisposedException("TreeIterator " + _tree.Name);
            }
            while (true)
            {
                _currentPage.LastSearchPosition--;
                if (_currentPage.LastSearchPosition >= 0)
                {
                    // run out of entries, need to select the next page...
                    while (_currentPage.IsBranch)
                    {
                        _cursor.Push(_currentPage);
                        var node = _currentPage.GetNode(_currentPage.LastSearchPosition);

                        _currentPage = _tx.GetReadOnlyPage(node->PageNumber);
                        _currentPage.LastSearchPosition = _currentPage.NumberOfEntries - 1;

                        if (_prefetch && _currentPage.IsLeaf)
                        {
                            MaybePrefetchOverflowPages(_currentPage);
                        }
                    }
                    var current = _currentPage.GetNode(_currentPage.LastSearchPosition);
                    if (this.ValidateCurrentKey(current, _currentPage) == false)
                    {
                        return(false);
                    }

                    _currentPage.SetNodeKey(current, ref _currentInternalKey);
                    _currentKey = _currentInternalKey.ToSlice();
                    return(true);// there is another entry in this page
                }
                if (_cursor.PageCount == 0)
                {
                    break;
                }
                _currentPage = _cursor.Pop();
            }
            _currentPage = null;
            return(false);
        }
Example #9
0
 public PageSplitter(Transaction tx, SliceComparer cmp, Slice newKey, int len, long pageNumber, Cursor cursor, TreeDataInTransaction txInfo)
 {
     _tx         = tx;
     _cmp        = cmp;
     _newKey     = newKey;
     _len        = len;
     _pageNumber = pageNumber;
     _cursor     = cursor;
     _txInfo     = txInfo;
     _page       = _cursor.Pop();
 }
Example #10
0
		public bool Seek(Slice key)
		{
			Lazy<Cursor> lazy;
			_currentPage = _tree.FindPageFor(_tx, key, out lazy);
			_cursor = lazy.Value;
			_cursor.Pop();
			var node = _currentPage.Search(key, _cmp);
			if (node == null)
			{
				return false;
			}
			_currentKey.Set(node);
			return this.ValidateCurrentKey(Current, _cmp);
		}
Example #11
0
		public bool Seek(Slice key)
		{
			Lazy<Cursor> lazy;
			_currentPage = _tree.FindPageFor(key, out lazy);
			_cursor = lazy.Value;
			_cursor.Pop();
			var node = _currentPage.Search(key);
		    if (node != null)
		    {
                _currentKey.Set(node);
                return this.ValidateCurrentKey(Current);
		    }
		    
            // The key is not found in the db, but we are Seek()ing for equals or starts with.
		    // We know that the exact value isn't there, but it is possible that the next page has values 
		    // that is actually greater than the key, so we need to check it as well.

		    _currentPage.LastSearchPosition = _currentPage.NumberOfEntries; // force next MoveNext to move to the next _page_.
		    return MoveNext();
		}
Example #12
0
        public PageSplitter(Transaction tx,
                            Tree tree,
                            MemorySlice newKey,
                            int len,
                            long pageNumber,
                            NodeFlags nodeType,
                            ushort nodeVersion,
                            Cursor cursor)
        {
            _tx          = tx;
            _tree        = tree;
            _newKey      = newKey;
            _len         = len;
            _pageNumber  = pageNumber;
            _nodeType    = nodeType;
            _nodeVersion = nodeVersion;
            _cursor      = cursor;
            Page page = _cursor.Pages.First.Value;

            _page = tx.ModifyPage(page.PageNumber, _tree, page);
            _cursor.Pop();
        }
Example #13
0
        public bool Seek(Slice key)
        {
            Lazy <Cursor> lazy;

            _currentPage = _tree.FindPageFor(key, out lazy);
            _cursor      = lazy.Value;
            _cursor.Pop();
            var node = _currentPage.Search(key);

            if (node != null)
            {
                _currentKey.Set(node);
                return(this.ValidateCurrentKey(Current));
            }

            // The key is not found in the db, but we are Seek()ing for equals or starts with.
            // We know that the exact value isn't there, but it is possible that the next page has values
            // that is actually greater than the key, so we need to check it as well.

            _currentPage.LastSearchPosition = _currentPage.NumberOfEntries;         // force next MoveNext to move to the next _page_.
            return(MoveNext());
        }
Example #14
0
        public Page Execute(Cursor cursor, Page page)
        {
            _tx.ClearRecentFoundPages(_tree);
            if (cursor.PageCount <= 1) // the root page
            {
                RebalanceRoot(cursor, page);
                return null;
            }

			var parentPage = _tx.ModifyPage(cursor.ParentPage.PageNumber, cursor.ParentPage);
			cursor.Update(cursor.Pages.First.Next, parentPage);

            if (page.NumberOfEntries == 0) // empty page, just delete it and fixup parent
            {
				// need to delete the implicit left page, shift right 
                if (parentPage.LastSearchPosition == 0 && parentPage.NumberOfEntries > 2)
                {
					var newImplicit = parentPage.GetNode(1)->PageNumber;
                    parentPage.RemoveNode(0);
                    parentPage.RemoveNode(0);
                    parentPage.AddPageRefNode(0, Slice.Empty, newImplicit);
                }
                else // will be set to rights by the next rebalance call
                {
                    parentPage.RemoveNode(parentPage.LastSearchPositionOrLastEntry);
                }
				
				_tx.FreePage(page.PageNumber);
                cursor.Pop();

                return parentPage;
            }

            var minKeys = page.IsBranch ? 2 : 1;
            if ((page.UseMoreSizeThan(_tx.DataPager.PageMinSpace)) &&
                page.NumberOfEntries >= minKeys)
                return null; // above space/keys thresholds

            Debug.Assert(parentPage.NumberOfEntries >= 2); // if we have less than 2 entries in the parent, the tree is invalid

            var sibling = SetupMoveOrMerge(cursor, page, parentPage);
            Debug.Assert(sibling.PageNumber != page.PageNumber);

            minKeys = sibling.IsBranch ? 2 : 1; // branch must have at least 2 keys
            if (sibling.UseMoreSizeThan(_tx.DataPager.PageMinSpace) &&
                sibling.NumberOfEntries > minKeys)
            {	         
                // neighbor is over the min size and has enough key, can move just one key to  the current page
	            if (page.IsBranch)
	            {
		            MoveBranchNode(parentPage, sibling, page);
	            }
	            else
		            MoveLeafNode(parentPage, sibling, page);
	            cursor.Pop();

                return parentPage;
            }

			if (page.LastSearchPosition == 0) // this is the right page, merge left
            {
				if (!HasEnoughSpaceToCopyNodes(sibling, page))
					return null;
				MergePages(parentPage, sibling, page);
            }
            else // this is the left page, merge right
            {
				if (!HasEnoughSpaceToCopyNodes(page, sibling))
					return null;
				MergePages(parentPage, page, sibling);
            }
            cursor.Pop();			

            return parentPage;
        }
Example #15
0
        public byte *AddSeparator(MemorySlice separator, long pageRefNumber, int?nodePos = null)
        {
            var originalLastSearchPositionOfParent = _parentPage.LastSearchPosition;

            if (nodePos == null)
            {
                nodePos = _parentPage.NodePositionFor(separator); // select the appropriate place for this
            }
            var separatorKeyToInsert = _parentPage.PrepareKeyToInsert(separator, nodePos.Value);

            if (_parentPage.HasSpaceFor(_tx, SizeOf.BranchEntry(separatorKeyToInsert) + Constants.NodeOffsetSize + SizeOf.NewPrefix(separatorKeyToInsert)) == false)
            {
                var pageSplitter = new PageSplitter(_tx, _tree, separator, -1, pageRefNumber, NodeFlags.PageRef, 0, _cursor, _tree.State);

                var posToInsert = pageSplitter.Execute();

                ParentOfAddedPageRef = _cursor.CurrentPage;

                var adjustParentPageOnCursor = true;

                for (int i = 0; i < _cursor.CurrentPage.NumberOfEntries; i++)
                {
                    if (_cursor.CurrentPage.GetNode(i)->PageNumber == _currentPage.PageNumber)
                    {
                        adjustParentPageOnCursor = false;
                        _cursor.CurrentPage.LastSearchPosition = i;
                        break;
                    }
                }

                if (adjustParentPageOnCursor)
                {
                    // the above page split has modified the cursor that its first page points to the parent of the leaf where 'separatorKey' was inserted
                    // and it doesn't have the reference to _page, we need to ensure that the actual parent is first at the cursor

                    _cursor.Pop();
                    _cursor.Push(_parentPage);

                    EnsureValidLastSearchPosition(_parentPage, _currentPage.PageNumber, originalLastSearchPositionOfParent);
                }
#if VALIDATE
                Debug.Assert(_cursor.CurrentPage.GetNode(_cursor.CurrentPage.LastSearchPosition)->PageNumber == _currentPage.PageNumber,
                             "The parent page is not referencing a page which is being split");

                var parentToValidate = ParentOfAddedPageRef;
                Debug.Assert(Enumerable.Range(0, parentToValidate.NumberOfEntries).Any(i => parentToValidate.GetNode(i)->PageNumber == pageRefNumber),
                             "The parent page of a page reference isn't referencing it");
#endif


                return(posToInsert);
            }

            ParentOfAddedPageRef = _parentPage;

            var pos = _parentPage.AddPageRefNode(nodePos.Value, separatorKeyToInsert, pageRefNumber);

            EnsureValidLastSearchPosition(_parentPage, _currentPage.PageNumber, originalLastSearchPositionOfParent);

            return(pos);
        }
        public Page Execute(Cursor cursor, Page page)
        {
            if (cursor.PageCount <= 1) // the root page
            {
                RebalanceRoot(cursor, _txInfo, page);
                return(null);
            }

            var parentPage = cursor.ParentPage;

            if (page.NumberOfEntries == 0) // empty page, just delete it and fixup parent
            {
                // need to delete the implicit left page, shift right
                if (parentPage.LastSearchPosition == 0 && parentPage.NumberOfEntries > 2)
                {
                    var newImplicit = parentPage.GetNode(1)->PageNumber;
                    parentPage.AddNode(0, Slice.Empty, -1, newImplicit);
                    parentPage.RemoveNode(1);
                    parentPage.RemoveNode(1);
                }
                else // will be set to rights by the next rebalance call
                {
                    parentPage.RemoveNode(parentPage.LastSearchPosition);
                }
                cursor.Pop();
                return(parentPage);
            }

            var minKeys = page.IsBranch ? 2 : 1;

            if (page.SizeUsed >= _tx.Pager.PageMinSpace &&
                page.NumberOfEntries >= minKeys)
            {
                return(null);                              // above space/keys thresholds
            }
            Debug.Assert(parentPage.NumberOfEntries >= 2); // if we have less than 2 entries in the parent, the tree is invalid

            var sibling = SetupMoveOrMerge(cursor, page, parentPage);

            Debug.Assert(sibling.PageNumber != page.PageNumber);

            minKeys = sibling.IsBranch ? 2 : 1; // branch must have at least 2 keys
            if (sibling.SizeUsed > _tx.Pager.PageMinSpace &&
                sibling.NumberOfEntries > minKeys)
            {
                // neighbor is over the min size and has enough key, can move just one key to  the current page
                if (page.IsBranch)
                {
                    MoveBranchNode(parentPage, sibling, page);
                }
                else
                {
                    MoveLeafNode(parentPage, sibling, page);
                }
                cursor.Pop();
                return(parentPage);
            }

            if (page.LastSearchPosition == 0) // this is the right page, merge left
            {
                MergePages(parentPage, sibling, page);
            }
            else // this is the left page, merge right
            {
                MergePages(parentPage, page, sibling);
            }
            cursor.Pop();
            return(parentPage);
        }
Example #17
0
        private byte *SplitPageInHalf(Page rightPage)
        {
            bool toRight;

            var currentIndex = _page.LastSearchPosition;
            var splitIndex   = _page.NumberOfEntries / 2;

            if (currentIndex <= splitIndex)
            {
                toRight = false;
            }
            else
            {
                toRight = true;

                var leftPageEntryCount  = splitIndex;
                var rightPageEntryCount = _page.NumberOfEntries - leftPageEntryCount + 1;

                if (rightPageEntryCount > leftPageEntryCount)
                {
                    splitIndex++;

                    Debug.Assert(splitIndex < _page.NumberOfEntries);
                }
            }

            PrefixNode[] prefixes = null;

            if (_tree.KeysPrefixing && _page.HasPrefixes)
            {
                prefixes = _page.GetPrefixes();
            }

            if (_page.IsLeaf || prefixes != null)
            {
                splitIndex = AdjustSplitPosition(currentIndex, splitIndex, prefixes, ref toRight);
            }

            var         currentKey = _page.GetNodeKey(splitIndex);
            MemorySlice seperatorKey;

            if (toRight && splitIndex == currentIndex)
            {
                seperatorKey = currentKey.Compare(_newKey) < 0 ? currentKey : _newKey;
            }
            else
            {
                seperatorKey = currentKey;
            }

            Page parentOfRight;

            AddSeparatorToParentPage(rightPage.PageNumber, seperatorKey, out parentOfRight);

            var parentOfPage = _cursor.CurrentPage;

            MemorySlice instance = _page.CreateNewEmptyKey();

            if (prefixes != null)
            {
                for (int i = 0; i < prefixes.Length; i++)
                {
                    var prefix = prefixes[i];

                    rightPage.WritePrefix(new Slice(prefix.ValuePtr, prefix.PrefixLength), i);
                }
            }

            bool addedAsImplicitRef = false;

            if (_page.IsBranch && toRight && seperatorKey == _newKey)
            {
                // _newKey needs to be inserted as first key (BeforeAllKeys) to the right page, so we need to add it before we move entries from the current page
                AddNodeToPage(rightPage, 0, _tree.KeysPrefixing ? (MemorySlice)PrefixedSlice.BeforeAllKeys : Slice.BeforeAllKeys);
                addedAsImplicitRef = true;
            }

            // move the actual entries from page to right page
            ushort nKeys = _page.NumberOfEntries;

            for (int i = splitIndex; i < nKeys; i++)
            {
                NodeHeader *node = _page.GetNode(i);
                if (_page.IsBranch && rightPage.NumberOfEntries == 0)
                {
                    rightPage.CopyNodeDataToEndOfPage(node, _tree.KeysPrefixing ? (MemorySlice)PrefixedSlice.BeforeAllKeys : Slice.BeforeAllKeys);
                }
                else
                {
                    _page.SetNodeKey(node, ref instance);
                    var key = rightPage.PrepareKeyToInsert(instance, rightPage.NumberOfEntries);

                    rightPage.CopyNodeDataToEndOfPage(node, key);
                }
            }

            _page.Truncate(_tx, splitIndex);

            byte *pos;

            if (addedAsImplicitRef == false)
            {
                try
                {
                    if (toRight && _cursor.CurrentPage.PageNumber != parentOfRight.PageNumber)
                    {
                        // modify the cursor if we are going to insert to the right page
                        _cursor.Pop();
                        _cursor.Push(parentOfRight);
                    }

                    // actually insert the new key
                    pos = toRight ? InsertNewKey(rightPage) : InsertNewKey(_page);
                }
                catch (InvalidOperationException e)
                {
                    if (e.Message.StartsWith("The page is full and cannot add an entry") == false)
                    {
                        throw;
                    }

                    throw new InvalidOperationException(GatherDetailedDebugInfo(rightPage, currentKey, seperatorKey, currentIndex, splitIndex, toRight), e);
                }
            }
            else
            {
                pos = null;
                _cursor.Push(rightPage);
            }

            if (_page.IsBranch) // remove a branch that has only one entry, the page ref needs to be added to the parent of the current page
            {
                Debug.Assert(_page.NumberOfEntries > 0);
                Debug.Assert(rightPage.NumberOfEntries > 0);

                if (_page.NumberOfEntries == 1)
                {
                    RemoveBranchWithOneEntry(_page, parentOfPage);
                }

                if (rightPage.NumberOfEntries == 1)
                {
                    RemoveBranchWithOneEntry(rightPage, parentOfRight);
                }
            }

            return(pos);
        }
Example #18
0
        public Page Execute(Page page)
        {
            _tree.ClearRecentFoundPages();
            if (_cursor.PageCount <= 1) // the root page
            {
                RebalanceRoot(page);
                return(null);
            }

            var parentPage = _tx.ModifyPage(_cursor.ParentPage.PageNumber, _tree, _cursor.ParentPage);

            _cursor.Update(_cursor.Pages.First.Next, parentPage);

            if (page.NumberOfEntries == 0) // empty page, just delete it and fixup parent
            {
                // need to change the implicit left page
                if (parentPage.LastSearchPosition == 0 && parentPage.NumberOfEntries > 2)
                {
                    var newImplicit = parentPage.GetNode(1)->PageNumber;
                    parentPage.RemoveNode(0);
                    parentPage.ChangeImplicitRefPageNode(newImplicit);
                }
                else // will be set to rights by the next rebalance call
                {
                    parentPage.RemoveNode(parentPage.LastSearchPositionOrLastEntry);
                }

                _tree.FreePage(page);
                _cursor.Pop();

                return(parentPage);
            }

            if (page.IsBranch && page.NumberOfEntries == 1)
            {
                RemoveBranchWithOneEntry(page, parentPage);
                _cursor.Pop();

                return(parentPage);
            }

            var minKeys = page.IsBranch ? 2 : 1;

            if ((page.UseMoreSizeThan(_tx.DataPager.PageMinSpace)) &&
                page.NumberOfEntries >= minKeys)
            {
                return(null);                              // above space/keys thresholds
            }
            Debug.Assert(parentPage.NumberOfEntries >= 2); // if we have less than 2 entries in the parent, the tree is invalid

            var sibling = SetupMoveOrMerge(page, parentPage);

            Debug.Assert(sibling.PageNumber != page.PageNumber);

            if (page.Flags != sibling.Flags)
            {
                return(null);
            }

            minKeys = sibling.IsBranch ? 2 : 1; // branch must have at least 2 keys
            if (sibling.UseMoreSizeThan(_tx.DataPager.PageMinSpace) &&
                sibling.NumberOfEntries > minKeys)
            {
                _cursor.Pop();

                // neighbor is over the min size and has enough key, can move just one key to  the current page
                if (page.IsBranch)
                {
                    MoveBranchNode(parentPage, sibling, page);
                }
                else
                {
                    MoveLeafNode(parentPage, sibling, page);
                }

                return(parentPage);
            }

            if (page.LastSearchPosition == 0)             // this is the right page, merge left
            {
                if (TryMergePages(parentPage, sibling, page) == false)
                {
                    return(null);
                }
            }
            else // this is the left page, merge right
            {
                if (TryMergePages(parentPage, page, sibling) == false)
                {
                    return(null);
                }
            }

            _cursor.Pop();

            return(parentPage);
        }
Example #19
0
        private void RebalanceRoot(Cursor cursor, Page page)
        {
            if (page.NumberOfEntries == 0)
                return; // nothing to do 
            if (!page.IsBranch || page.NumberOfEntries > 1)
            {
                return; // cannot do anything here
            }
            // in this case, we have a root pointer with just one pointer, we can just swap it out

            var node = page.GetNode(0);
            Debug.Assert(node->Flags == (NodeFlags.PageRef));

            _tree.State.LeafPages = 1;
			_tree.State.BranchPages = 0;
			_tree.State.Depth = 1;
			_tree.State.PageCount = 1;

			var rootPage = _tx.ModifyPage(node->PageNumber, null);
			_tree.State.RootPageNumber = rootPage.PageNumber;

            Debug.Assert(rootPage.Dirty);

            cursor.Pop();
            cursor.Push(rootPage);

            _tx.FreePage(page.PageNumber);
        }