public byte *AddSeparator(Slice separator, long pageRefNumber, int?nodePos = null) { var originalLastSearchPositionOfParent = _parentPage.LastSearchPosition; if (nodePos == null) { nodePos = _parentPage.NodePositionFor(_tx, separator); // select the appropriate place for this } if (_parentPage.HasSpaceFor(_tx, TreeSizeOf.BranchEntry(separator) + Constants.NodeOffsetSize) == false) { var pageSplitter = new TreePageSplitter(_tx, _tree, separator, -1, pageRefNumber, TreeNodeFlags.PageRef, 0, _cursor); 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, separator, pageRefNumber); EnsureValidLastSearchPosition(_parentPage, _currentPage.PageNumber, originalLastSearchPositionOfParent); return(pos); }
private byte *InsertNewKey(TreePage p) { int pos = p.NodePositionFor(_tx, _newKey); var newKeyToInsert = _newKey; if (p.HasSpaceFor(_tx, p.GetRequiredSpace(newKeyToInsert, _len)) == false) { _cursor.Push(p); var pageSplitter = new TreePageSplitter(_tx, _tree, _newKey, _len, _pageNumber, _nodeType, _cursor); return(pageSplitter.Execute()); } byte *dataPos = AddNodeToPage(p, pos, newKeyToInsert); _cursor.Push(p); return(dataPos); }
public byte *DirectAdd(Slice key, int len, TreeNodeFlags nodeType = TreeNodeFlags.Data, ushort?version = null) { Debug.Assert(nodeType == TreeNodeFlags.Data || nodeType == TreeNodeFlags.MultiValuePageRef); if (State.InWriteTransaction) { State.IsModified = true; } if (_llt.Flags == (TransactionFlags.ReadWrite) == false) { throw new ArgumentException("Cannot add a value in a read only transaction"); } if (AbstractPager.IsKeySizeValid(key.Size) == false) { throw new ArgumentException($"Key size is too big, must be at most {AbstractPager.MaxKeySize} bytes, but was {(key.Size + AbstractPager.RequiredSpaceForNewNode)}", nameof(key)); } Func <TreeCursor> cursorConstructor; TreeNodeHeader * node; var foundPage = FindPageFor(key, out node, out cursorConstructor); var page = ModifyPage(foundPage); ushort nodeVersion = 0; bool? shouldGoToOverflowPage = null; if (page.LastMatch == 0) // this is an update operation { node = page.GetNode(page.LastSearchPosition); Debug.Assert(SliceComparer.EqualsInline(TreeNodeHeader.ToSlicePtr(_llt.Allocator, node), key)); shouldGoToOverflowPage = ShouldGoToOverflowPage(len); byte *pos; if (shouldGoToOverflowPage == false) { // optimization for Data and MultiValuePageRef - try to overwrite existing node space if (TryOverwriteDataOrMultiValuePageRefNode(node, key, len, nodeType, version, out pos)) { return(pos); } } else { // optimization for PageRef - try to overwrite existing overflows if (TryOverwriteOverflowPages(node, key, len, version, out pos)) { return(pos); } } RemoveLeafNode(page, out nodeVersion); } else // new item should be recorded { State.NumberOfEntries++; } CheckConcurrency(key, version, nodeVersion, TreeActionType.Add); var lastSearchPosition = page.LastSearchPosition; // searching for overflow pages might change this byte *overFlowPos = null; var pageNumber = -1L; if (shouldGoToOverflowPage ?? ShouldGoToOverflowPage(len)) { pageNumber = WriteToOverflowPages(len, out overFlowPos); len = -1; nodeType = TreeNodeFlags.PageRef; } byte *dataPos; if (page.HasSpaceFor(_llt, key, len) == false) { using (var cursor = cursorConstructor()) { cursor.Update(cursor.Pages.First, page); var pageSplitter = new TreePageSplitter(_llt, this, key, len, pageNumber, nodeType, nodeVersion, cursor); dataPos = pageSplitter.Execute(); } DebugValidateTree(State.RootPageNumber); } else { switch (nodeType) { case TreeNodeFlags.PageRef: dataPos = page.AddPageRefNode(lastSearchPosition, key, pageNumber); break; case TreeNodeFlags.Data: dataPos = page.AddDataNode(lastSearchPosition, key, len, nodeVersion); break; case TreeNodeFlags.MultiValuePageRef: dataPos = page.AddMultiValueNode(lastSearchPosition, key, len, nodeVersion); break; default: throw new NotSupportedException("Unknown node type for direct add operation: " + nodeType); } page.DebugValidate(_llt, State.RootPageNumber); } if (overFlowPos != null) { return(overFlowPos); } return(dataPos); }
public DirectAddScope DirectAdd(Slice key, int len, TreeNodeFlags nodeType, out byte *ptr) { if (_llt.Flags == TransactionFlags.ReadWrite) { State.IsModified = true; } else { ThreadCannotAddInReadTx(); } if (AbstractPager.IsKeySizeValid(key.Size) == false) { ThrowInvalidKeySize(key); } var foundPage = FindPageFor(key, node: out TreeNodeHeader * node, cursor: out TreeCursorConstructor cursorConstructor, allowCompressed: true); var page = ModifyPage(foundPage); bool?shouldGoToOverflowPage = null; if (page.LastMatch == 0) // this is an update operation { if ((nodeType & TreeNodeFlags.NewOnly) == TreeNodeFlags.NewOnly) { ThrowConcurrencyException(); } node = page.GetNode(page.LastSearchPosition); #if DEBUG using (TreeNodeHeader.ToSlicePtr(_llt.Allocator, node, out Slice nodeCheck)) { Debug.Assert(SliceComparer.EqualsInline(nodeCheck, key)); } #endif shouldGoToOverflowPage = ShouldGoToOverflowPage(len); byte *pos; if (shouldGoToOverflowPage == false) { // optimization for Data and MultiValuePageRef - try to overwrite existing node space if (TryOverwriteDataOrMultiValuePageRefNode(node, len, nodeType, out pos)) { ptr = pos; return(new DirectAddScope(this)); } } else { // optimization for PageRef - try to overwrite existing overflows if (TryOverwriteOverflowPages(node, len, out pos)) { ptr = pos; return(new DirectAddScope(this)); } } RemoveLeafNode(page); } else // new item should be recorded { State.NumberOfEntries++; } nodeType &= ~TreeNodeFlags.NewOnly; Debug.Assert(nodeType == TreeNodeFlags.Data || nodeType == TreeNodeFlags.MultiValuePageRef); var lastSearchPosition = page.LastSearchPosition; // searching for overflow pages might change this byte *overFlowPos = null; var pageNumber = -1L; if (shouldGoToOverflowPage ?? ShouldGoToOverflowPage(len)) { pageNumber = WriteToOverflowPages(len, out overFlowPos); len = -1; nodeType = TreeNodeFlags.PageRef; } byte *dataPos; if (page.HasSpaceFor(_llt, key, len) == false) { if (IsLeafCompressionSupported == false || TryCompressPageNodes(key, len, page) == false) { using (var cursor = cursorConstructor.Build(key)) { cursor.Update(cursor.Pages, page); var pageSplitter = new TreePageSplitter(_llt, this, key, len, pageNumber, nodeType, cursor); dataPos = pageSplitter.Execute(); } DebugValidateTree(State.RootPageNumber); ptr = overFlowPos == null ? dataPos : overFlowPos; return(new DirectAddScope(this)); } // existing values compressed and put at the end of the page, let's insert from Upper position lastSearchPosition = 0; } switch (nodeType) { case TreeNodeFlags.PageRef: dataPos = page.AddPageRefNode(lastSearchPosition, key, pageNumber); break; case TreeNodeFlags.Data: dataPos = page.AddDataNode(lastSearchPosition, key, len); break; case TreeNodeFlags.MultiValuePageRef: dataPos = page.AddMultiValueNode(lastSearchPosition, key, len); break; default: ThrowUnknownNodeTypeAddOperation(nodeType); dataPos = null; // never executed break; } page.DebugValidate(this, State.RootPageNumber); ptr = overFlowPos == null ? dataPos : overFlowPos; return(new DirectAddScope(this)); }