private void AddToRecentlyFoundPages(Cursor c, Page p, bool?leftmostPage, bool?rightmostPage) { MemorySlice firstKey; if (leftmostPage == true) { if (p.KeysPrefixed) { firstKey = PrefixedSlice.BeforeAllKeys; } else { firstKey = Slice.BeforeAllKeys; } } else { firstKey = p.GetNodeKey(0); } MemorySlice lastKey; if (rightmostPage == true) { if (p.KeysPrefixed) { lastKey = PrefixedSlice.AfterAllKeys; } else { lastKey = Slice.AfterAllKeys; } } else { lastKey = p.GetNodeKey(p.NumberOfEntries - 1); } var cursorPath = new long[c.Pages.Count]; var cur = c.Pages.First; int pos = cursorPath.Length - 1; while (cur != null) { cursorPath[pos--] = cur.Value.PageNumber; cur = cur.Next; } var foundPage = new RecentlyFoundPages.FoundPage(p.PageNumber, p, firstKey, lastKey, cursorPath); RecentlyFoundPages.Add(foundPage); }
private void AddToRecentlyFoundPages(Cursor c, Page p, bool?leftmostPage, bool?rightmostPage) { var foundPage = new RecentlyFoundPages.FoundPage(c.Pages.Count) { Number = p.PageNumber }; if (leftmostPage == true) { if (p.KeysPrefixed) { foundPage.FirstKey = PrefixedSlice.BeforeAllKeys; } else { foundPage.FirstKey = Slice.BeforeAllKeys; } } else { foundPage.FirstKey = p.GetNodeKey(0); } if (rightmostPage == true) { if (p.KeysPrefixed) { foundPage.LastKey = PrefixedSlice.AfterAllKeys; } else { foundPage.LastKey = Slice.AfterAllKeys; } } else { foundPage.LastKey = p.GetNodeKey(p.NumberOfEntries - 1); } var cur = c.Pages.First; int pos = foundPage.CursorPath.Length - 1; while (cur != null) { foundPage.CursorPath[pos--] = cur.Value.PageNumber; cur = cur.Next; } _tx.AddRecentlyFoundPage(this, foundPage); }
private MemorySlice GetActualKey(Page page, int pos, out NodeHeader *node) { node = page.GetNode(pos); var key = page.GetNodeKey(node); while (key.KeyLength == 0) { Debug.Assert(page.IsBranch); page = _tx.GetReadOnlyPage(node->PageNumber); node = page.GetNode(0); key = page.GetNodeKey(node); } return(key); }
private string GatherDetailedDebugInfo(Page rightPage, MemorySlice currentKey, MemorySlice seperatorKey, int currentIndex, int splitIndex, bool toRight) { var debugInfo = new StringBuilder(); debugInfo.AppendFormat("\r\n_tree.Name: {0}\r\n", _tree.Name); debugInfo.AppendFormat("_newKey: {0}, _len: {1}, needed space: {2}\r\n", _newKey, _len, _page.GetRequiredSpace(_newKey, _len)); debugInfo.AppendFormat("key at LastSearchPosition: {0}, current key: {1}, seperatorKey: {2}\r\n", _page.GetNodeKey(_page.LastSearchPosition), currentKey, seperatorKey); debugInfo.AppendFormat("currentIndex: {0}\r\n", currentIndex); debugInfo.AppendFormat("splitIndex: {0}\r\n", splitIndex); debugInfo.AppendFormat("toRight: {0}\r\n", toRight); debugInfo.AppendFormat("_page info: flags - {0}, # of entries {1}, size left: {2}, calculated size left: {3}\r\n", _page.Flags, _page.NumberOfEntries, _page.SizeLeft, _page.CalcSizeLeft()); for (int i = 0; i < _page.NumberOfEntries; i++) { var node = _page.GetNode(i); var key = _page.GetNodeKey(node); debugInfo.AppendFormat("{0} - {2} {1}\r\n", key, node->DataSize, node->Flags == NodeFlags.Data ? "Size" : "Page"); } debugInfo.AppendFormat("rightPage info: flags - {0}, # of entries {1}, size left: {2}, calculated size left: {3}\r\n", rightPage.Flags, rightPage.NumberOfEntries, rightPage.SizeLeft, rightPage.CalcSizeLeft()); for (int i = 0; i < rightPage.NumberOfEntries; i++) { var node = rightPage.GetNode(i); var key = rightPage.GetNodeKey(node); debugInfo.AppendFormat("{0} - {2} {1}\r\n", key, node->DataSize, node->Flags == NodeFlags.Data ? "Size" : "Page"); } return(debugInfo.ToString()); }
private void AddToRecentlyFoundPages(Cursor c, Page p, bool?leftmostPage, bool?rightmostPage) { var foundPage = new RecentlyFoundPages.FoundPage(c.Pages.Count) { Number = p.PageNumber, FirstKey = leftmostPage == true ? Slice.BeforeAllKeys : p.GetNodeKey(0), LastKey = rightmostPage == true ? Slice.AfterAllKeys : p.GetNodeKey(p.NumberOfEntries - 1), }; var cur = c.Pages.First; int pos = foundPage.CursorPath.Length - 1; while (cur != null) { foundPage.CursorPath[pos--] = cur.Value.PageNumber; cur = cur.Next; } _tx.AddRecentlyFoundPage(this, foundPage); }
public unsafe static bool ValidateCurrentKey(this IIterator self, NodeHeader *node, Page page) { if (self.RequiredPrefix != null) { var currentKey = page.GetNodeKey(node); if (currentKey.StartsWith(self.RequiredPrefix) == false) { return(false); } } if (self.MaxKey != null) { var currentKey = page.GetNodeKey(node); if (currentKey.Compare(self.MaxKey) >= 0) { return(false); } } return(true); }
public void MultiAdd(Slice key, Slice value, ushort? version = null) { if (value == null) throw new ArgumentNullException("value"); int maxNodeSize = _tx.DataPager.MaxNodeSize; if (value.Size > maxNodeSize) throw new ArgumentException( "Cannot add a value to child tree that is over " + maxNodeSize + " bytes in size", "value"); if (value.Size == 0) throw new ArgumentException("Cannot add empty value to child tree"); State.IsModified = true; Lazy<Cursor> lazy; var page = FindPageFor(key, out lazy); if ((page == null || page.LastMatch != 0)) { MultiAddOnNewValue(_tx, key, value, version, maxNodeSize); return; } page = _tx.ModifyPage(page.PageNumber, page); var item = page.GetNode(page.LastSearchPosition); // already was turned into a multi tree, not much to do here if (item->Flags == NodeFlags.MultiValuePageRef) { var existingTree = OpenOrCreateMultiValueTree(_tx, key, item); existingTree.DirectAdd(value, 0, version: version); return; } byte* nestedPagePtr; if (item->Flags == NodeFlags.PageRef) { var overFlowPage = _tx.ModifyPage(item->PageNumber, null); nestedPagePtr = overFlowPage.Base + Constants.PageHeaderSize; } else { nestedPagePtr = NodeHeader.DirectAccess(_tx, item); } var nestedPage = new Page(nestedPagePtr, "multi tree", (ushort)NodeHeader.GetDataSize(_tx, item)); var existingItem = nestedPage.Search(value); if (nestedPage.LastMatch != 0) existingItem = null;// not an actual match, just greater than ushort previousNodeRevision = existingItem != null ? existingItem->Version : (ushort)0; CheckConcurrency(key, value, version, previousNodeRevision, TreeActionType.Add); if (existingItem != null) { // maybe same value added twice? var tmpKey = new Slice(item); if (tmpKey.Compare(value) == 0) return; // already there, turning into a no-op nestedPage.RemoveNode(nestedPage.LastSearchPosition); } if (nestedPage.HasSpaceFor(_tx, value, 0)) { // we are now working on top of the modified root page, we can just modify the memory directly nestedPage.AddDataNode(nestedPage.LastSearchPosition, value, 0, previousNodeRevision); return; } int pageSize = nestedPage.CalcSizeUsed() + Constants.PageHeaderSize; var newRequiredSize = pageSize + nestedPage.GetRequiredSpace(value, 0); if (newRequiredSize <= maxNodeSize) { // we can just expand the current value... no need to create a nested tree yet var actualPageSize = (ushort)Math.Min(Utils.NearestPowerOfTwo(newRequiredSize), maxNodeSize); ExpandMultiTreeNestedPageSize(_tx, key, value, nestedPagePtr, actualPageSize, item->DataSize); return; } // we now have to convert this into a tree instance, instead of just a nested page var tree = Create(_tx, TreeFlags.MultiValue); for (int i = 0; i < nestedPage.NumberOfEntries; i++) { var existingValue = nestedPage.GetNodeKey(i); tree.DirectAdd(existingValue, 0); } tree.DirectAdd(value, 0, version: version); _tx.AddMultiValueTree(this, key, tree); // we need to record that we switched to tree mode here, so the next call wouldn't also try to create the tree again DirectAdd(key, sizeof (TreeRootHeader), NodeFlags.MultiValuePageRef); }
private MemorySlice GetActualKey(Page page, int pos, out NodeHeader* node) { node = page.GetNode(pos); var key = page.GetNodeKey(node); while (key.KeyLength == 0) { Debug.Assert(page.IsBranch); page = _tx.GetReadOnlyPage(node->PageNumber); node = page.GetNode(0); key = page.GetNodeKey(node); } return key; }
public unsafe static bool ValidateCurrentKey(this IIterator self, NodeHeader* node, Page page) { if (self.RequiredPrefix != null) { var currentKey = page.GetNodeKey(node); if (currentKey.StartsWith(self.RequiredPrefix) == false) return false; } if (self.MaxKey != null) { var currentKey = page.GetNodeKey(node); if (currentKey.Compare(self.MaxKey) >= 0) return false; } return true; }
private void AddToRecentlyFoundPages(Transaction tx, Cursor c, Page p, bool? leftmostPage, bool? rightmostPage) { var foundPage = new RecentlyFoundPages.FoundPage(c.Pages.Count) { Number = p.PageNumber, FirstKey = leftmostPage == true ? Slice.BeforeAllKeys : p.GetNodeKey(0), LastKey = rightmostPage == true ? Slice.AfterAllKeys : p.GetNodeKey(p.NumberOfEntries - 1), }; var cur = c.Pages.First; int pos = foundPage.CursorPath.Length - 1; while (cur != null) { foundPage.CursorPath[pos--] = cur.Value.PageNumber; cur = cur.Next; } tx.AddRecentlyFoundPage(this, foundPage); }
private byte *SplitPageInHalf(Page rightPage) { int currentIndex = _page.LastSearchPosition; bool newPosition = true; int splitIndex = _page.NumberOfEntries / 2; if (currentIndex < splitIndex) { newPosition = false; } PrefixNode[] prefixes = null; if (_tree.KeysPrefixing && _page.HasPrefixes) { prefixes = _page.GetPrefixes(); } if (_page.IsLeaf || prefixes != null) { splitIndex = AdjustSplitPosition(currentIndex, splitIndex, prefixes, ref newPosition); } var currentKey = _page.GetNodeKey(splitIndex); // here the current key is the separator key and can go either way, so // use newPosition to decide if it stays on the left node or moves to the right MemorySlice seperatorKey; if (currentIndex == splitIndex && newPosition) { seperatorKey = currentKey.Compare(_newKey) < 0 ? currentKey : _newKey; } else { seperatorKey = currentKey; } AddSeparatorToParentPage(rightPage.PageNumber, seperatorKey); 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); } } // 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); // actually insert the new key try { return((currentIndex > splitIndex || newPosition && currentIndex == splitIndex) ? InsertNewKey(rightPage) : InsertNewKey(_page)); } catch (InvalidOperationException e) { if (e.Message.StartsWith("The page is full and cannot add an entry")) { var debugInfo = new StringBuilder(); debugInfo.AppendFormat("\r\n_tree.Name: {0}\r\n", _tree.Name); debugInfo.AppendFormat("_newKey: {0}, _len: {1}, needed space: {2}\r\n", _newKey, _len, _page.GetRequiredSpace(_newKey, _len)); debugInfo.AppendFormat("currentKey: {0}, seperatorKey: {1}\r\n", currentKey, seperatorKey); debugInfo.AppendFormat("currentIndex: {0}\r\n", currentIndex); debugInfo.AppendFormat("splitIndex: {0}\r\n", splitIndex); debugInfo.AppendFormat("newPosition: {0}\r\n", newPosition); debugInfo.AppendFormat("_page info: flags - {0}, # of entries {1}, size left: {2}, calculated size left: {3}\r\n", _page.Flags, _page.NumberOfEntries, _page.SizeLeft, _page.CalcSizeLeft()); for (int i = 0; i < _page.NumberOfEntries; i++) { var node = _page.GetNode(i); var key = _page.GetNodeKey(node); debugInfo.AppendFormat("{0} - {2} {1}\r\n", key, node->DataSize, node->Flags == NodeFlags.Data ? "Size" : "Page"); } debugInfo.AppendFormat("rightPage info: flags - {0}, # of entries {1}, size left: {2}, calculated size left: {3}\r\n", rightPage.Flags, rightPage.NumberOfEntries, rightPage.SizeLeft, rightPage.CalcSizeLeft()); for (int i = 0; i < rightPage.NumberOfEntries; i++) { var node = rightPage.GetNode(i); var key = rightPage.GetNodeKey(node); debugInfo.AppendFormat("{0} - {2} {1}\r\n", key, node->DataSize, node->Flags == NodeFlags.Data ? "Size" : "Page"); } throw new InvalidOperationException(debugInfo.ToString(), e); } throw; } }
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)); }
private byte* SplitPageInHalf(Page rightPage) { int currentIndex = _page.LastSearchPosition; bool newPosition = true; int splitIndex = _page.NumberOfEntries/2; if (currentIndex < splitIndex) newPosition = false; PrefixNode[] prefixes = null; if (_tree.KeysPrefixing && _page.HasPrefixes) { prefixes = _page.GetPrefixes(); } if (_page.IsLeaf || prefixes != null) { splitIndex = AdjustSplitPosition(currentIndex, splitIndex, prefixes, ref newPosition); } var currentKey = _page.GetNodeKey(splitIndex); // here the current key is the separator key and can go either way, so // use newPosition to decide if it stays on the left node or moves to the right MemorySlice seperatorKey; if (currentIndex == splitIndex && newPosition) { seperatorKey = currentKey.Compare(_newKey) < 0 ? currentKey : _newKey; } else { seperatorKey = currentKey; } AddSeparatorToParentPage(rightPage.PageNumber, seperatorKey); 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); } } // 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); // actually insert the new key try { return (currentIndex > splitIndex || newPosition && currentIndex == splitIndex) ? InsertNewKey(rightPage) : InsertNewKey(_page); } catch (InvalidOperationException e) { if (e.Message.StartsWith("The page is full and cannot add an entry")) { var debugInfo = new StringBuilder(); debugInfo.AppendFormat("\r\n_tree.Name: {0}\r\n", _tree.Name); debugInfo.AppendFormat("_newKey: {0}, _len: {1}, needed space: {2}\r\n", _newKey, _len, _page.GetRequiredSpace(_newKey, _len)); debugInfo.AppendFormat("currentKey: {0}, seperatorKey: {1}\r\n", currentKey, seperatorKey); debugInfo.AppendFormat("currentIndex: {0}\r\n", currentIndex); debugInfo.AppendFormat("splitIndex: {0}\r\n", splitIndex); debugInfo.AppendFormat("newPosition: {0}\r\n", newPosition); debugInfo.AppendFormat("_page info: flags - {0}, # of entries {1}, size left: {2}, calculated size left: {3}\r\n", _page.Flags, _page.NumberOfEntries, _page.SizeLeft, _page.CalcSizeLeft()); for (int i = 0; i < _page.NumberOfEntries; i++) { var node = _page.GetNode(i); var key = _page.GetNodeKey(node); debugInfo.AppendFormat("{0} - {2} {1}\r\n", key, node->DataSize, node->Flags == NodeFlags.Data ? "Size" : "Page"); } debugInfo.AppendFormat("rightPage info: flags - {0}, # of entries {1}, size left: {2}, calculated size left: {3}\r\n", rightPage.Flags, rightPage.NumberOfEntries, rightPage.SizeLeft, rightPage.CalcSizeLeft()); for (int i = 0; i < rightPage.NumberOfEntries; i++) { var node = rightPage.GetNode(i); var key = rightPage.GetNodeKey(node); debugInfo.AppendFormat("{0} - {2} {1}\r\n", key, node->DataSize, node->Flags == NodeFlags.Data ? "Size" : "Page"); } throw new InvalidOperationException(debugInfo.ToString(), e); } throw; } }
private string GatherDetailedDebugInfo(Page rightPage, MemorySlice currentKey, MemorySlice seperatorKey, int currentIndex, int splitIndex, bool toRight) { var debugInfo = new StringBuilder(); debugInfo.AppendFormat("\r\n_tree.Name: {0}\r\n", _tree.Name); debugInfo.AppendFormat("_newKey: {0}, _len: {1}, needed space: {2}\r\n", _newKey, _len, _page.GetRequiredSpace(_newKey, _len)); debugInfo.AppendFormat("key at LastSearchPosition: {0}, current key: {1}, seperatorKey: {2}\r\n", _page.GetNodeKey(_page.LastSearchPosition), currentKey, seperatorKey); debugInfo.AppendFormat("currentIndex: {0}\r\n", currentIndex); debugInfo.AppendFormat("splitIndex: {0}\r\n", splitIndex); debugInfo.AppendFormat("toRight: {0}\r\n", toRight); debugInfo.AppendFormat("_page info: flags - {0}, # of entries {1}, size left: {2}, calculated size left: {3}\r\n", _page.Flags, _page.NumberOfEntries, _page.SizeLeft, _page.CalcSizeLeft()); for (int i = 0; i < _page.NumberOfEntries; i++) { var node = _page.GetNode(i); var key = _page.GetNodeKey(node); debugInfo.AppendFormat("{0} - {2} {1}\r\n", key, node->DataSize, node->Flags == NodeFlags.Data ? "Size" : "Page"); } debugInfo.AppendFormat("rightPage info: flags - {0}, # of entries {1}, size left: {2}, calculated size left: {3}\r\n", rightPage.Flags, rightPage.NumberOfEntries, rightPage.SizeLeft, rightPage.CalcSizeLeft()); for (int i = 0; i < rightPage.NumberOfEntries; i++) { var node = rightPage.GetNode(i); var key = rightPage.GetNodeKey(node); debugInfo.AppendFormat("{0} - {2} {1}\r\n", key, node->DataSize, node->Flags == NodeFlags.Data ? "Size" : "Page"); } return debugInfo.ToString(); }
private void AddToRecentlyFoundPages(Cursor c, Page p, bool? leftmostPage, bool? rightmostPage) { var foundPage = new RecentlyFoundPages.FoundPage(c.Pages.Count) { Number = p.PageNumber }; if (leftmostPage == true) { if(p.KeysPrefixed) foundPage.FirstKey = PrefixedSlice.BeforeAllKeys; else foundPage.FirstKey = Slice.BeforeAllKeys; } else { foundPage.FirstKey = p.GetNodeKey(0); } if (rightmostPage == true) { if (p.KeysPrefixed) foundPage.LastKey = PrefixedSlice.AfterAllKeys; else foundPage.LastKey = Slice.AfterAllKeys; } else { foundPage.LastKey = p.GetNodeKey(p.NumberOfEntries - 1); } var cur = c.Pages.First; int pos = foundPage.CursorPath.Length - 1; while (cur != null) { foundPage.CursorPath[pos--] = cur.Value.PageNumber; cur = cur.Next; } _tx.AddRecentlyFoundPage(this, foundPage); }
private static string GetBranchNodeString(int i, MemorySlice key, Page p, NodeHeader* node) { string keyStr; if (i == 0 && key.KeyLength == 0) { key = p.GetNodeKey(1); keyStr = "(lt " + key + ")"; } else { key = p.GetNodeKey(node); keyStr = key.ToString(); } return MaxString(keyStr, 25); }
public void MultiAdd(Slice key, Slice value, ushort?version = null) { if (value == null) { throw new ArgumentNullException("value"); } int maxNodeSize = AbstractPager.NodeMaxSize; if (value.Size > maxNodeSize) { throw new ArgumentException( "Cannot add a value to child tree that is over " + maxNodeSize + " bytes in size", "value"); } if (value.Size == 0) { throw new ArgumentException("Cannot add empty value to child tree"); } State.IsModified = true; Lazy <Cursor> lazy; NodeHeader * node; var page = FindPageFor(key, out node, out lazy); if ((page == null || page.LastMatch != 0)) { MultiAddOnNewValue(_tx, key, value, version, maxNodeSize); return; } page = _tx.ModifyPage(page.PageNumber, page); var item = page.GetNode(page.LastSearchPosition); // already was turned into a multi tree, not much to do here if (item->Flags == NodeFlags.MultiValuePageRef) { var existingTree = OpenMultiValueTree(_tx, key, item); existingTree.DirectAdd(value, 0, version: version); return; } byte *nestedPagePtr; if (item->Flags == NodeFlags.PageRef) { var overFlowPage = _tx.ModifyPage(item->PageNumber, null); nestedPagePtr = overFlowPage.Base + Constants.PageHeaderSize; } else { nestedPagePtr = NodeHeader.DirectAccess(_tx, item); } var nestedPage = new Page(nestedPagePtr, "multi tree", (ushort)NodeHeader.GetDataSize(_tx, item)); var existingItem = nestedPage.Search(value); if (nestedPage.LastMatch != 0) { existingItem = null; // not an actual match, just greater than } ushort previousNodeRevision = existingItem != null ? existingItem->Version : (ushort)0; CheckConcurrency(key, value, version, previousNodeRevision, TreeActionType.Add); if (existingItem != null) { // maybe same value added twice? var tmpKey = page.GetNodeKey(item); if (tmpKey.Compare(value) == 0) { return; // already there, turning into a no-op } nestedPage.RemoveNode(nestedPage.LastSearchPosition); } var valueToInsert = nestedPage.PrepareKeyToInsert(value, nestedPage.LastSearchPosition); if (nestedPage.HasSpaceFor(_tx, valueToInsert, 0)) { // we are now working on top of the modified root page, we can just modify the memory directly nestedPage.AddDataNode(nestedPage.LastSearchPosition, valueToInsert, 0, previousNodeRevision); return; } int pageSize = nestedPage.CalcSizeUsed() + Constants.PageHeaderSize; var newRequiredSize = pageSize + nestedPage.GetRequiredSpace(valueToInsert, 0); if (newRequiredSize <= maxNodeSize) { // we can just expand the current value... no need to create a nested tree yet var actualPageSize = (ushort)Math.Min(Utils.NearestPowerOfTwo(newRequiredSize), maxNodeSize); var currentDataSize = NodeHeader.GetDataSize(_tx, item); ExpandMultiTreeNestedPageSize(_tx, key, value, nestedPagePtr, actualPageSize, currentDataSize); return; } // we now have to convert this into a tree instance, instead of just a nested page var tree = Create(_tx, KeysPrefixing, TreeFlags.MultiValue); for (int i = 0; i < nestedPage.NumberOfEntries; i++) { var existingValue = nestedPage.GetNodeKey(i); tree.DirectAdd(existingValue, 0); } tree.DirectAdd(value, 0, version: version); _tx.AddMultiValueTree(this, key, tree); // we need to record that we switched to tree mode here, so the next call wouldn't also try to create the tree again DirectAdd(key, sizeof(TreeRootHeader), NodeFlags.MultiValuePageRef); }
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)); } }