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); }
public byte *Execute() { var rightPage = Tree.NewPage(_tx, _page.Flags, 1); _txInfo.RecordNewPage(_page, 1); rightPage.Flags = _page.Flags; if (_cursor.PageCount == 0) // we need to do a root split { var newRootPage = Tree.NewPage(_tx, PageFlags.Branch, 1); _cursor.Push(newRootPage); _txInfo.RootPageNumber = newRootPage.PageNumber; _txInfo.State.Depth++; _txInfo.RecordNewPage(newRootPage, 1); // now add implicit left page newRootPage.AddNode(0, Slice.BeforeAllKeys, -1, _page.PageNumber); _parentPage = newRootPage; _parentPage.LastSearchPosition++; _parentPage.ItemCount = _page.ItemCount; } else { // we already popped the page, so the current one on the stack is what the parent of the page _parentPage = _cursor.CurrentPage; } if (_page.LastSearchPosition >= _page.NumberOfEntries) { // when we get a split at the end of the page, we take that as a hint that the user is doing // sequential inserts, at that point, we are going to keep the current page as is and create a new // page, this will allow us to do minimal amount of work to get the best density byte *pos; if (_page.IsBranch) { // here we steal the last entry from the current page so we maintain the implicit null left entry var node = _page.GetNode(_page.NumberOfEntries - 1); Debug.Assert(node->Flags == NodeFlags.PageRef); var itemsMoved = _tx.Pager.Get(_tx, node->PageNumber).ItemCount; rightPage.AddNode(0, Slice.Empty, -1, node->PageNumber); pos = rightPage.AddNode(1, _newKey, _len, _pageNumber); rightPage.ItemCount = itemsMoved; AddSeperatorToParentPage(rightPage, new Slice(node)); _page.RemoveNode(_page.NumberOfEntries - 1); _page.ItemCount -= itemsMoved; } else { AddSeperatorToParentPage(rightPage, _newKey); pos = rightPage.AddNode(0, _newKey, _len, _pageNumber); } _cursor.Push(rightPage); IncrementItemCountIfNecessary(); return(pos); } return(SplitPageInHalf(rightPage)); }
public Page FindPageFor(Transaction tx, Slice key, Cursor cursor) { var treeDataInTransaction = tx.GetTreeInformation(this); var p = tx.GetReadOnlyPage(treeDataInTransaction.RootPageNumber); cursor.Push(p); while (p.Flags == (PageFlags.Branch)) { int nodePos; if (key.Options == SliceOptions.BeforeAllKeys) { p.LastSearchPosition = nodePos = 0; } else if (key.Options == SliceOptions.AfterAllKeys) { p.LastSearchPosition = nodePos = (ushort)(p.NumberOfEntries - 1); } else { if (p.Search(key, _cmp) != null) { nodePos = p.LastSearchPosition; if (p.LastMatch != 0) { nodePos--; p.LastSearchPosition--; } } else { nodePos = (ushort)(p.LastSearchPosition - 1); } } var node = p.GetNode(nodePos); p = tx.GetReadOnlyPage(node->PageNumber); cursor.Push(p); } if (p.IsLeaf == false) { throw new DataException("Index points to a non leaf page"); } p.NodePositionFor(key, _cmp); // will set the LastSearchPosition return(p); }
public bool MoveNext() { while (true) { _currentPage.LastSearchPosition++; if (_currentPage.LastSearchPosition < _currentPage.NumberOfEntries) { // 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 = 0; } var current = _currentPage.GetNode(_currentPage.LastSearchPosition); if (this.ValidateCurrentKey(current, _cmp) == false) { return(false); } _currentKey.Set(current); return(true); // there is another entry in this page } if (_cursor.PageCount == 0) { break; } _currentPage = _cursor.Pop(); } _currentPage = null; return(false); }
private void RebalanceRoot(Cursor cursor, TreeDataInTransaction txInfo, 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)); _tx.ModifyCursor(txInfo, cursor); txInfo.State.LeafPages = 1; txInfo.State.BranchPages = 0; txInfo.State.Depth = 1; txInfo.State.PageCount = 1; var rootPage = _tx.ModifyPage(_txInfo.Tree, null, node->PageNumber, cursor); rootPage.ItemCount = 1; txInfo.RootPageNumber = rootPage.PageNumber; Debug.Assert(rootPage.Dirty); cursor.Pop(); cursor.Push(rootPage); _tx.FreePage(page.PageNumber); }
private void RebalanceRoot(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)); var rootPage = _tx.ModifyPage(node->PageNumber, _tree, null); _tree.State.RootPageNumber = rootPage.PageNumber; _tree.State.Depth--; Debug.Assert(rootPage.Dirty); _cursor.Pop(); _cursor.Push(rootPage); _tree.FreePage(page); }
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); }
private Page SearchForPage(MemorySlice key, out Lazy <Cursor> cursor, out NodeHeader *node) { var p = _tx.GetReadOnlyPage(State.RootPageNumber); var c = new Cursor(); c.Push(p); bool rightmostPage = true; bool leftmostPage = true; while ((p.Flags & PageFlags.Branch) == PageFlags.Branch) { int nodePos; if (key.Options == SliceOptions.BeforeAllKeys) { p.LastSearchPosition = nodePos = 0; rightmostPage = false; } else if (key.Options == SliceOptions.AfterAllKeys) { p.LastSearchPosition = nodePos = (ushort)(p.NumberOfEntries - 1); leftmostPage = false; } else { if (p.Search(key) != null) { nodePos = p.LastSearchPosition; if (p.LastMatch != 0) { nodePos--; p.LastSearchPosition--; } if (nodePos != 0) { leftmostPage = false; } rightmostPage = false; } else { nodePos = (ushort)(p.LastSearchPosition - 1); leftmostPage = false; } } var pageNode = p.GetNode(nodePos); p = _tx.GetReadOnlyPage(pageNode->PageNumber); Debug.Assert(pageNode->PageNumber == p.PageNumber, string.Format("Requested Page: #{0}. Got Page: #{1}", pageNode->PageNumber, p.PageNumber)); c.Push(p); } if (p.IsLeaf == false) { throw new DataException("Index points to a non leaf page"); } node = p.Search(key); // will set the LastSearchPosition AddToRecentlyFoundPages(c, p, leftmostPage, rightmostPage); cursor = new Lazy <Cursor>(() => c); return(p); }
private bool TryUseRecentTransactionPage(Transaction tx, Slice key, out Lazy<Cursor> cursor, out Page page) { page = null; cursor = null; var recentPages = tx.GetRecentlyFoundPages(this); if (recentPages == null) return false; var foundPage = recentPages.Find(key); if (foundPage == null) return false; var lastFoundPageNumber = foundPage.Number; page = tx.GetReadOnlyPage(lastFoundPageNumber); if (page.IsLeaf == false) throw new DataException("Index points to a non leaf page"); page.NodePositionFor(key, _cmp); // will set the LastSearchPosition var cursorPath = foundPage.CursorPath; var pageCopy = page; cursor = new Lazy<Cursor>(() => { var c = new Cursor(); foreach (var p in cursorPath) { if (p == lastFoundPageNumber) c.Push(pageCopy); else { var cursorPage = tx.GetReadOnlyPage(p); if (key.Options == SliceOptions.BeforeAllKeys) { cursorPage.LastSearchPosition = 0; } else if (key.Options == SliceOptions.AfterAllKeys) { cursorPage.LastSearchPosition = (ushort)(cursorPage.NumberOfEntries - 1); } else if (cursorPage.Search(key, _cmp) != null) { if (cursorPage.LastMatch != 0) { cursorPage.LastSearchPosition--; } } c.Push(cursorPage); } } return c; }); return true; }
private Page SearchForPage(Transaction tx, Slice key, ref Lazy<Cursor> cursor) { var p = tx.GetReadOnlyPage(State.RootPageNumber); var c = new Cursor(); c.Push(p); bool rightmostPage = true; bool leftmostPage = true; while (p.Flags == (PageFlags.Branch)) { int nodePos; if (key.Options == SliceOptions.BeforeAllKeys) { p.LastSearchPosition = nodePos = 0; rightmostPage = false; } else if (key.Options == SliceOptions.AfterAllKeys) { p.LastSearchPosition = nodePos = (ushort) (p.NumberOfEntries - 1); leftmostPage = false; } else { if (p.Search(key, _cmp) != null) { nodePos = p.LastSearchPosition; if (p.LastMatch != 0) { nodePos--; p.LastSearchPosition--; } if (nodePos != 0) leftmostPage = false; rightmostPage = false; } else { nodePos = (ushort) (p.LastSearchPosition - 1); leftmostPage = false; } } var node = p.GetNode(nodePos); p = tx.GetReadOnlyPage(node->PageNumber); Debug.Assert(node->PageNumber == p.PageNumber, string.Format("Requested Page: #{0}. Got Page: #{1}", node->PageNumber, p.PageNumber)); c.Push(p); } if (p.IsLeaf == false) throw new DataException("Index points to a non leaf page"); p.Search(key, _cmp); // will set the LastSearchPosition AddToRecentlyFoundPages(tx, c, p, leftmostPage, rightmostPage); cursor = new Lazy<Cursor>(() => c); return p; }
public byte *Execute() { using (_tree.IsFreeSpaceTree ? _tx.Environment.FreeSpaceHandling.Disable() : null) { Page rightPage = _tree.NewPage(_page.Flags, 1); if (_cursor.PageCount == 0) // we need to do a root split { Page newRootPage = _tree.NewPage(_tree.KeysPrefixing ? PageFlags.Branch | PageFlags.KeysPrefixed : PageFlags.Branch, 1); _cursor.Push(newRootPage); _tree.State.RootPageNumber = newRootPage.PageNumber; _tree.State.Depth++; // now add implicit left page newRootPage.AddPageRefNode(0, _tree.KeysPrefixing ? (MemorySlice)PrefixedSlice.BeforeAllKeys : Slice.BeforeAllKeys, _page.PageNumber); _parentPage = newRootPage; _parentPage.LastSearchPosition++; } else { // we already popped the page, so the current one on the stack is the parent of the page _parentPage = _tx.ModifyPage(_cursor.CurrentPage.PageNumber, _tree, _cursor.CurrentPage); _cursor.Update(_cursor.Pages.First, _parentPage); } if (_page.IsLeaf) { _tree.ClearRecentFoundPages(); } if (_page.LastSearchPosition >= _page.NumberOfEntries) { // when we get a split at the end of the page, we take that as a hint that the user is doing // sequential inserts, at that point, we are going to keep the current page as is and create a new // page, this will allow us to do minimal amount of work to get the best density Page branchOfSeparator; byte *pos; if (_page.IsBranch) { if (_page.NumberOfEntries > 2) { // here we steal the last entry from the current page so we maintain the implicit null left entry NodeHeader *node = _page.GetNode(_page.NumberOfEntries - 1); Debug.Assert(node->Flags == NodeFlags.PageRef); rightPage.AddPageRefNode(0, _tree.KeysPrefixing ? (MemorySlice)PrefixedSlice.BeforeAllKeys : Slice.BeforeAllKeys, node->PageNumber); pos = AddNodeToPage(rightPage, 1); var separatorKey = _page.GetNodeKey(node); AddSeparatorToParentPage(rightPage.PageNumber, separatorKey, out branchOfSeparator); _page.RemoveNode(_page.NumberOfEntries - 1); } else { _tree.FreePage(rightPage); // return the unnecessary right page pos = AddSeparatorToParentPage(_pageNumber, _newKey, out branchOfSeparator); if (_cursor.CurrentPage.PageNumber != branchOfSeparator.PageNumber) { _cursor.Push(branchOfSeparator); } return(pos); } } else { AddSeparatorToParentPage(rightPage.PageNumber, _newKey, out branchOfSeparator); pos = AddNodeToPage(rightPage, 0); } _cursor.Push(rightPage); return(pos); } return(SplitPageInHalf(rightPage)); } }
private bool TryUseRecentTransactionPage(Slice key, out Lazy <Cursor> cursor, out Page page) { page = null; cursor = null; var recentPages = _tx.GetRecentlyFoundPages(this); if (recentPages == null) { return(false); } var foundPage = recentPages.Find(key); if (foundPage == null) { return(false); } var lastFoundPageNumber = foundPage.Number; page = _tx.GetReadOnlyPage(lastFoundPageNumber); if (page.IsLeaf == false) { throw new DataException("Index points to a non leaf page"); } page.NodePositionFor(key); // will set the LastSearchPosition var cursorPath = foundPage.CursorPath; var pageCopy = page; cursor = new Lazy <Cursor>(() => { var c = new Cursor(); foreach (var p in cursorPath) { if (p == lastFoundPageNumber) { c.Push(pageCopy); } else { var cursorPage = _tx.GetReadOnlyPage(p); if (key.Options == SliceOptions.BeforeAllKeys) { cursorPage.LastSearchPosition = 0; } else if (key.Options == SliceOptions.AfterAllKeys) { cursorPage.LastSearchPosition = (ushort)(cursorPage.NumberOfEntries - 1); } else if (cursorPage.Search(key) != null) { if (cursorPage.LastMatch != 0) { cursorPage.LastSearchPosition--; } } c.Push(cursorPage); } } return(c); }); return(true); }
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); }
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); }
private bool TryUseRecentTransactionPage(MemorySlice key, out Lazy <Cursor> cursor, out Page page, out NodeHeader *node) { node = null; page = null; cursor = null; var recentPages = RecentlyFoundPages; if (recentPages == null) { return(false); } var foundPage = recentPages.Find(key); if (foundPage == null) { return(false); } var lastFoundPageNumber = foundPage.Number; if (foundPage.Page != null) { // we can't share the same instance, Page instance may be modified by // concurrently run iterators page = new Page(foundPage.Page.Base, foundPage.Page.Source, foundPage.Page.PageSize); } else { page = _tx.GetReadOnlyPage(lastFoundPageNumber); } if (page.IsLeaf == false) { throw new DataException("Index points to a non leaf page"); } node = page.Search(key); // will set the LastSearchPosition var cursorPath = foundPage.CursorPath; var pageCopy = page; cursor = new Lazy <Cursor>(() => { var c = new Cursor(); foreach (var p in cursorPath) { if (p == lastFoundPageNumber) { c.Push(pageCopy); } else { var cursorPage = _tx.GetReadOnlyPage(p); if (key.Options == SliceOptions.BeforeAllKeys) { cursorPage.LastSearchPosition = 0; } else if (key.Options == SliceOptions.AfterAllKeys) { cursorPage.LastSearchPosition = (ushort)(cursorPage.NumberOfEntries - 1); } else if (cursorPage.Search(key) != null) { if (cursorPage.LastMatch != 0) { cursorPage.LastSearchPosition--; } } c.Push(cursorPage); } } return(c); }); return(true); }
public byte *Execute() { Page rightPage = _tree.NewPage(_page.Flags, 1); if (_cursor.PageCount == 0) // we need to do a root split { Page newRootPage = _tree.NewPage(_tree.KeysPrefixing ? PageFlags.Branch | PageFlags.KeysPrefixed : PageFlags.Branch, 1); _cursor.Push(newRootPage); _treeState.RootPageNumber = newRootPage.PageNumber; _treeState.Depth++; // now add implicit left page newRootPage.AddPageRefNode(0, _tree.KeysPrefixing ? (MemorySlice)PrefixedSlice.BeforeAllKeys : Slice.BeforeAllKeys, _page.PageNumber); _parentPage = newRootPage; _parentPage.LastSearchPosition++; } else { // we already popped the page, so the current one on the stack is the parent of the page if (_tree.Name == Constants.FreeSpaceTreeName) { // a special case for FreeSpaceTree because the allocation of a new page called above // can cause a delete of a free space section resulting in a run of the tree rebalancer // and here the parent page that exists in cursor can be outdated _parentPage = _tx.ModifyPage(_cursor.CurrentPage.PageNumber, _tree, null); // pass _null_ to make sure we'll get the most updated parent page _parentPage.LastSearchPosition = _cursor.CurrentPage.LastSearchPosition; _parentPage.LastMatch = _cursor.CurrentPage.LastMatch; } else { _parentPage = _tx.ModifyPage(_cursor.CurrentPage.PageNumber, _tree, _cursor.CurrentPage); } _cursor.Update(_cursor.Pages.First, _parentPage); } if (_page.IsLeaf) { _tree.ClearRecentFoundPages(); } if (_tree.Name == Constants.FreeSpaceTreeName) { // we need to refresh the LastSearchPosition of the split page which is used by the free space handling // because the allocation of a new page called above could remove some sections // from the page that is being split _page.NodePositionFor(_newKey); } if (_page.LastSearchPosition >= _page.NumberOfEntries) { // when we get a split at the end of the page, we take that as a hint that the user is doing // sequential inserts, at that point, we are going to keep the current page as is and create a new // page, this will allow us to do minimal amount of work to get the best density Page branchOfSeparator; byte *pos; if (_page.IsBranch) { if (_page.NumberOfEntries > 2) { // here we steal the last entry from the current page so we maintain the implicit null left entry NodeHeader *node = _page.GetNode(_page.NumberOfEntries - 1); Debug.Assert(node->Flags == NodeFlags.PageRef); rightPage.AddPageRefNode(0, _tree.KeysPrefixing ? (MemorySlice)PrefixedSlice.BeforeAllKeys : Slice.BeforeAllKeys, node->PageNumber); pos = AddNodeToPage(rightPage, 1); var separatorKey = _page.GetNodeKey(node); AddSeparatorToParentPage(rightPage.PageNumber, separatorKey, out branchOfSeparator); _page.RemoveNode(_page.NumberOfEntries - 1); } else { _tree.FreePage(rightPage); // return the unnecessary right page pos = AddSeparatorToParentPage(_pageNumber, _newKey, out branchOfSeparator); if (_cursor.CurrentPage.PageNumber != branchOfSeparator.PageNumber) { _cursor.Push(branchOfSeparator); } return(pos); } } else { AddSeparatorToParentPage(rightPage.PageNumber, _newKey, out branchOfSeparator); pos = AddNodeToPage(rightPage, 0); } _cursor.Push(rightPage); return(pos); } return(SplitPageInHalf(rightPage)); }
public byte *Execute() { Page rightPage = Tree.NewPage(_tx, _page.Flags, 1); _treeState.RecordNewPage(_page, 1); rightPage.Flags = _page.Flags; if (_cursor.PageCount == 0) // we need to do a root split { Page newRootPage = Tree.NewPage(_tx, PageFlags.Branch, 1); _cursor.Push(newRootPage); _treeState.RootPageNumber = newRootPage.PageNumber; _treeState.Depth++; _treeState.RecordNewPage(newRootPage, 1); // now add implicit left page newRootPage.AddPageRefNode(0, Slice.BeforeAllKeys, _page.PageNumber); _parentPage = newRootPage; _parentPage.LastSearchPosition++; } else { // we already popped the page, so the current one on the stack is what the parent of the page _parentPage = _tx.ModifyPage(_cursor.CurrentPage.PageNumber, _cursor.CurrentPage); _cursor.Update(_cursor.Pages.First, _parentPage); } if (_page.IsLeaf) { _tx.ClearRecentFoundPages(_tree); } if (_page.LastSearchPosition >= _page.NumberOfEntries) { // when we get a split at the end of the page, we take that as a hint that the user is doing // sequential inserts, at that point, we are going to keep the current page as is and create a new // page, this will allow us to do minimal amount of work to get the best density byte *pos; if (_page.IsBranch) { // here we steal the last entry from the current page so we maintain the implicit null left entry NodeHeader *node = _page.GetNode(_page.NumberOfEntries - 1); Debug.Assert(node->Flags == NodeFlags.PageRef); rightPage.AddPageRefNode(0, Slice.Empty, node->PageNumber); pos = AddNodeToPage(rightPage, 1); AddSeparatorToParentPage(rightPage, new Slice(node)); _page.RemoveNode(_page.NumberOfEntries - 1); } else { AddSeparatorToParentPage(rightPage, _newKey); pos = AddNodeToPage(rightPage, 0); } _cursor.Push(rightPage); return(pos); } return(SplitPageInHalf(rightPage)); }