public bool Seek(Slice key) { if (_disposed) { throw new ObjectDisposedException("TreeIterator " + _tree.Name); } TreeNodeHeader * node; TreeCursorConstructor constructor; _currentPage = _tree.FindPageFor(key, node: out node, cursor: out constructor, allowCompressed: _tree.IsLeafCompressionSupported); if (_currentPage.IsCompressed) { DecompressedCurrentPage(); node = _currentPage.Search(_tx, key); } _cursor = constructor.Build(key); _cursor.Pop(); if (node != null) { _prevKeyScope.Dispose(); _prevKeyScope = TreeNodeHeader.ToSlicePtr(_tx.Allocator, node, out _currentInternalKey); _currentKey = _currentInternalKey; if (DoRequireValidation) { return(this.ValidateCurrentKey(_tx, Current)); } return(true); } // 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()); }
private void ExpandMultiTreeNestedPageSize(Slice key, Slice value, byte *nestedPagePtr, ushort newSize, int currentSize) { Debug.Assert(newSize > currentSize); TemporaryPage tmp; using (_llt.Environment.GetTemporaryPage(_llt, out tmp)) { var tempPagePointer = tmp.TempPagePointer; Memory.Copy(tempPagePointer, nestedPagePtr, currentSize); Delete(key); // release our current page TreePage nestedPage = new TreePage(tempPagePointer, (ushort)currentSize); byte *ptr; using (DirectAdd(key, newSize, out ptr)) { var newNestedPage = new TreePage(ptr, newSize) { Lower = (ushort)Constants.Tree.PageHeaderSize, Upper = newSize, TreeFlags = TreePageFlags.Leaf, PageNumber = -1L, // mark as invalid page number Flags = 0 }; ByteStringContext allocator = _llt.Allocator; for (int i = 0; i < nestedPage.NumberOfEntries; i++) { var nodeHeader = nestedPage.GetNode(i); Slice nodeKey; using (TreeNodeHeader.ToSlicePtr(allocator, nodeHeader, out nodeKey)) newNestedPage.AddDataNode(i, nodeKey, 0); } newNestedPage.Search(_llt, value); newNestedPage.AddDataNode(newNestedPage.LastSearchPosition, value, 0); } } }
private void ExpandMultiTreeNestedPageSize(Slice key, Slice value, byte *nestedPagePtr, ushort newSize, int currentSize) { Debug.Assert(newSize > currentSize); TemporaryPage tmp; using (_llt.Environment.GetTemporaryPage(_llt, out tmp)) { var tempPagePointer = tmp.TempPagePointer; Memory.Copy(tempPagePointer, nestedPagePtr, currentSize); Delete(key); // release our current page TreePage nestedPage = new TreePage(tempPagePointer, "multi tree", (ushort)currentSize); var ptr = DirectAdd(key, newSize); var newNestedPage = new TreePage(ptr, "multi tree", newSize) { Lower = (ushort)Constants.TreePageHeaderSize, Upper = newSize, TreeFlags = TreePageFlags.Leaf, PageNumber = -1L // mark as invalid page number }; ByteStringContext allocator = _llt.Allocator; for (int i = 0; i < nestedPage.NumberOfEntries; i++) { var nodeHeader = nestedPage.GetNode(i); Slice nodeKey = TreeNodeHeader.ToSlicePtr(allocator, nodeHeader); newNestedPage.AddDataNode(i, nodeKey, 0, (ushort)(nodeHeader->Version - 1)); // we dec by one because AdddataNode will inc by one, and we don't want to change those values nodeKey.Release(allocator); } newNestedPage.Search(_llt, value); newNestedPage.AddDataNode(newNestedPage.LastSearchPosition, value, 0, 0); } }
public bool Seek(Slice key) { if (_disposed) { throw new ObjectDisposedException("PageIterator"); } var current = _page.Search(_tx, key); if (current == null) { return(false); } _currentInternalKey = TreeNodeHeader.ToSlicePtr(_tx.Allocator, current); _currentKey = _currentInternalKey; if (DoRequireValidation) { return(this.ValidateCurrentKey(_tx, current)); } return(true); }
public void MultiAdd(Slice key, Slice value) { if (!value.HasValue) { throw new ArgumentNullException(nameof(value)); } int maxNodeSize = Llt.DataPager.NodeMaxSize; if (value.Size > maxNodeSize) { throw new ArgumentException("Cannot add a value to child tree that is over " + maxNodeSize + " bytes in size", nameof(value)); } if (value.Size == 0) { throw new ArgumentException("Cannot add empty value to child tree"); } State.IsModified = true; State.Flags |= TreeFlags.MultiValueTrees; TreeNodeHeader *node; var page = FindPageFor(key, out node); if (page == null || page.LastMatch != 0) { MultiAddOnNewValue(key, value, maxNodeSize); return; } page = ModifyPage(page); var item = page.GetNode(page.LastSearchPosition); byte *_; // already was turned into a multi tree, not much to do here if (item->Flags == TreeNodeFlags.MultiValuePageRef) { var existingTree = OpenMultiValueTree(key, item); existingTree.DirectAdd(value, 0, out _).Dispose(); return; } if (item->Flags == TreeNodeFlags.PageRef) { throw new InvalidOperationException("Multi trees don't use overflows"); } var nestedPagePtr = DirectAccessFromHeader(item); var nestedPage = new TreePage(nestedPagePtr, (ushort)GetDataSize(item)); var existingItem = nestedPage.Search(_llt, value); if (nestedPage.LastMatch != 0) { existingItem = null;// not an actual match, just greater than } if (existingItem != null) { // maybe same value added twice? Slice tmpKey; using (TreeNodeHeader.ToSlicePtr(_llt.Allocator, item, out tmpKey)) { if (SliceComparer.Equals(tmpKey, value)) { return; // already there, turning into a no-op } } nestedPage.RemoveNode(nestedPage.LastSearchPosition); } if (nestedPage.HasSpaceFor(_llt, 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); return; } if (page.HasSpaceFor(_llt, value, 0)) { // page has space for an additional node in nested page ... var requiredSpace = nestedPage.PageSize + // existing page nestedPage.GetRequiredSpace(value, 0); // new node if (requiredSpace + Constants.Tree.NodeHeaderSize <= maxNodeSize) { // ... and it won't require to create an overflow, so we can just expand the current value, no need to create a nested tree yet EnsureNestedPagePointer(page, item, ref nestedPage, ref nestedPagePtr); var newPageSize = (ushort)Math.Min(Bits.NextPowerOf2(requiredSpace), maxNodeSize - Constants.Tree.NodeHeaderSize); ExpandMultiTreeNestedPageSize(key, value, nestedPagePtr, newPageSize, nestedPage.PageSize); return; } } EnsureNestedPagePointer(page, item, ref nestedPage, ref nestedPagePtr); // we now have to convert this into a tree instance, instead of just a nested page var tree = Create(_llt, _tx, key, TreeFlags.MultiValue); for (int i = 0; i < nestedPage.NumberOfEntries; i++) { Slice existingValue; using (nestedPage.GetNodeKey(_llt, i, out existingValue)) { tree.DirectAdd(existingValue, 0, out _).Dispose(); } } tree.DirectAdd(value, 0, out _).Dispose(); _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), TreeNodeFlags.MultiValuePageRef, out _).Dispose(); }
public void MultiDelete(Slice key, Slice value) { State.IsModified = true; TreeNodeHeader *node; var page = FindPageFor(key, out node); if (page == null || page.LastMatch != 0) { return; //nothing to delete - key not found } page = ModifyPage(page); var item = page.GetNode(page.LastSearchPosition); if (item->Flags == TreeNodeFlags.MultiValuePageRef) //multi-value tree exists { var tree = OpenMultiValueTree(key, item); tree.Delete(value); // previously, we would convert back to a simple model if we dropped to a single entry // however, it doesn't really make sense, once you got enough values to go to an actual nested // tree, you are probably going to remain that way, or be removed completely. if (tree.State.NumberOfEntries != 0) { return; } _tx.TryRemoveMultiValueTree(this, key); if (_newPageAllocator != null) { if (IsIndexTree == false) { ThrowAttemptToFreePageToNewPageAllocator(Name, tree.State.RootPageNumber); } _newPageAllocator.FreePage(tree.State.RootPageNumber); } else { if (IsIndexTree) { ThrowAttemptToFreeIndexPageToFreeSpaceHandling(Name, tree.State.RootPageNumber); } _llt.FreePage(tree.State.RootPageNumber); } Delete(key); } else // we use a nested page here { var nestedPage = new TreePage(DirectAccessFromHeader(item), (ushort)GetDataSize(item)); nestedPage.Search(_llt, value); // need to search the value in the nested page if (nestedPage.LastMatch != 0) // value not found { return; } if (item->Flags == TreeNodeFlags.PageRef) { throw new InvalidOperationException("Multi trees don't use overflows"); } var nestedPagePtr = DirectAccessFromHeader(item); nestedPage = new TreePage(nestedPagePtr, (ushort)GetDataSize(item)) { LastSearchPosition = nestedPage.LastSearchPosition }; nestedPage.RemoveNode(nestedPage.LastSearchPosition); if (nestedPage.NumberOfEntries == 0) { Delete(key); } } }
public void MultiDelete(Slice key, Slice value, ushort?version = null) { State.IsModified = true; TreeNodeHeader *node; var page = FindPageFor(key, out node); if (page == null || page.LastMatch != 0) { return; //nothing to delete - key not found } page = ModifyPage(page); var item = page.GetNode(page.LastSearchPosition); if (item->Flags == TreeNodeFlags.MultiValuePageRef) //multi-value tree exists { var tree = OpenMultiValueTree(key, item); tree.Delete(value, version); // previously, we would convert back to a simple model if we dropped to a single entry // however, it doesn't really make sense, once you got enough values to go to an actual nested // tree, you are probably going to remain that way, or be removed completely. if (tree.State.NumberOfEntries != 0) { return; } _tx.TryRemoveMultiValueTree(this, key); _llt.FreePage(tree.State.RootPageNumber); Delete(key); } else // we use a nested page here { var nestedPage = new TreePage(TreeNodeHeader.DirectAccess(_llt, item), "multi tree", (ushort)TreeNodeHeader.GetDataSize(_llt, item)); var nestedItem = nestedPage.Search(_llt, value); if (nestedPage.LastMatch != 0) // value not found { return; } if (item->Flags == TreeNodeFlags.PageRef) { throw new InvalidOperationException("Multi trees don't use overflows"); } var nestedPagePtr = TreeNodeHeader.DirectAccess(_llt, item); nestedPage = new TreePage(nestedPagePtr, "multi tree", (ushort)TreeNodeHeader.GetDataSize(_llt, item)) { LastSearchPosition = nestedPage.LastSearchPosition }; CheckConcurrency(key, value, version, nestedItem->Version, TreeActionType.Delete); nestedPage.RemoveNode(nestedPage.LastSearchPosition); if (nestedPage.NumberOfEntries == 0) { Delete(key); } } }