public static void DumpHumanReadable(Transaction tx, string path, Page start) { using (var writer = File.CreateText(path)) { var stack = new Stack<Page>(); stack.Push(start); writer.WriteLine("Root page #{0}",start.PageNumber); while (stack.Count > 0) { var currentPage = stack.Pop(); if (currentPage.IsLeaf) { writer.WriteLine(); writer.WriteLine("Page #{0}, NumberOfEntries = {1}, Flags = {2} (Leaf), Used: {3} : {4}", currentPage.PageNumber,currentPage.NumberOfEntries,currentPage.Flags, currentPage.SizeUsed, currentPage.CalcSizeUsed()); if(currentPage.NumberOfEntries <= 0) writer.WriteLine("Empty page (tree corrupted?)"); for (int nodeIndex = 0; nodeIndex < currentPage.NumberOfEntries;nodeIndex++) { var node = currentPage.GetNode(nodeIndex); var key = currentPage.GetNodeKey(node); writer.WriteLine("Node #{0}, Flags = {1}, {4} = {2}, Key = {3}, Entry Size: {5}", nodeIndex, node->Flags, node->DataSize, MaxString(key.ToString(), 25), node->Flags == NodeFlags.Data ? "Size" : "Page", SizeOf.NodeEntry(node)); } writer.WriteLine(); } else if(currentPage.IsBranch) { writer.WriteLine(); writer.WriteLine("Page #{0}, NumberOfEntries = {1}, Flags = {2} (Branch), Used: {3} : {4}", currentPage.PageNumber, currentPage.NumberOfEntries, currentPage.Flags, currentPage.SizeUsed, currentPage.SizeUsed); var key = new Slice(SliceOptions.Key); for (int nodeIndex = 0; nodeIndex < currentPage.NumberOfEntries; nodeIndex++) { var node = currentPage.GetNode(nodeIndex); writer.WriteLine("Node #{2}, {0} / to page #{1}, Entry Size: {3}", GetBranchNodeString(nodeIndex, key, currentPage, node), node->PageNumber, nodeIndex, SizeOf.NodeEntry(node)); } for (int nodeIndex = 0; nodeIndex < currentPage.NumberOfEntries; nodeIndex++) { var node = currentPage.GetNode(nodeIndex); if (node->PageNumber < 0 || node->PageNumber > tx.State.NextPageNumber) { writer.Write("Found invalid reference to page #{0}", currentPage.PageNumber); stack.Clear(); break; } var child = tx.GetReadOnlyPage(node->PageNumber); stack.Push(child); } writer.WriteLine(); } } } }
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 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); }
public override int Write(Page page, long? pageNumber) { var toWrite = page.IsOverflow ? GetNumberOfOverflowPages(page.OverflowSize) : 1; var requestedPageNumber = pageNumber ?? page.PageNumber; return WriteDirect(page, requestedPageNumber, toWrite); }
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 FoundPage(long number, Page page, MemorySlice firstKey, MemorySlice lastKey, long[] cursorPath) { Number = number; Page = page; FirstKey = firstKey; LastKey = lastKey; CursorPath = cursorPath; }
public ParentPageAction(Page parentPage, Page currentPage, Tree tree, Cursor cursor, Transaction tx) { _parentPage = parentPage; _currentPage = currentPage; _tree = tree; _cursor = cursor; _tx = tx; }
public override int Write(Page page, long? pageNumber) { long startPage = pageNumber ?? page.PageNumber; //note: GetNumberOfOverflowPages and WriteDirect can throw ObjectDisposedException if the pager is already disposed int toWrite = page.IsOverflow ? GetNumberOfOverflowPages(page.OverflowSize) : 1; return WriteDirect(page, startPage, toWrite); }
public static unsafe bool HasDuplicateBranchReferences(Transaction tx, Page start,out long pageNumberWithDuplicates) { var stack = new Stack<Page>(); var existingTreeReferences = new ConcurrentDictionary<long, List<long>>(); stack.Push(start); while (stack.Count > 0) { var currentPage = stack.Pop(); if (currentPage.IsBranch) { for (int nodeIndex = 0; nodeIndex < currentPage.NumberOfEntries; nodeIndex++) { var node = currentPage.GetNode(nodeIndex); existingTreeReferences.AddOrUpdate(currentPage.PageNumber, new List<long> { node->PageNumber }, (branchPageNumber, pageNumberReferences) => { pageNumberReferences.Add(node->PageNumber); return pageNumberReferences; }); } for (int nodeIndex = 0; nodeIndex < currentPage.NumberOfEntries; nodeIndex++) { var node = currentPage.GetNode(nodeIndex); if (node->PageNumber < 0 || node->PageNumber > tx.State.NextPageNumber) { throw new InvalidDataException("found invalid reference on branch - tree is corrupted"); } var child = tx.GetReadOnlyPage(node->PageNumber); stack.Push(child); } } } Func<long, HashSet<long>> relevantPageReferences = branchPageNumber => new HashSet<long>(existingTreeReferences .Where(kvp => kvp.Key != branchPageNumber) .SelectMany(kvp => kvp.Value)); // ReSharper disable once LoopCanBeConvertedToQuery foreach (var branchReferences in existingTreeReferences) { if ( branchReferences.Value.Any( referencePageNumber => relevantPageReferences(branchReferences.Key).Contains(referencePageNumber))) { pageNumberWithDuplicates = branchReferences.Key; return true; } } pageNumberWithDuplicates = -1; return false; }
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); }
public void RecordNewPage(Page p, int num) { PageCount += num; if (p.IsBranch) { BranchPages++; } else if (p.IsLeaf) { LeafPages++; } else if (p.IsOverflow) { OverflowPages += num; } }
public void RecordNewPage(Page p, int num) { PageCount++; var flags = p.Flags; if ((flags & PageFlags.Branch) == PageFlags.Branch) { BranchPages++; } else if ((flags & PageFlags.Leaf) == PageFlags.Leaf) { LeafPages++; } else if ((flags & PageFlags.Overflow) == PageFlags.Overflow) { OverflowPages += num; } }
public void Update(LinkedListNode<Page> node, Page newVal) { var oldPageNumber = node.Value.PageNumber; var newPageNumber = newVal.PageNumber; if (oldPageNumber == newPageNumber) { _pagesByNum[oldPageNumber] = newVal; node.Value = newVal; return; } _anyOverrides = true; _pagesByNum[oldPageNumber] = newVal; _pagesByNum.Add(newPageNumber, newVal); node.Value = newVal; }
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(); }
public void RecordFreedPage(Page p, int num) { PageCount -= num; Debug.Assert(PageCount >= 0); if (p.IsBranch) { BranchPages--; Debug.Assert(BranchPages >= 0); } else if (p.IsLeaf) { LeafPages--; Debug.Assert(LeafPages >= 0); } else if (p.IsOverflow) { OverflowPages -= num; Debug.Assert(OverflowPages >= 0); } }
public abstract int Write(Page page, long? pageNumber);
public abstract int WriteDirect(Page start, long pagePosition, int pagesToWrite);
public IIterator MultiRead(Slice key) { Lazy<Cursor> lazy; var page = FindPageFor(key, out lazy); if (page == null || page.LastMatch != 0) { return new EmptyIterator(); } var item = page.Search(key); var fetchedNodeKey = new Slice(item); if (fetchedNodeKey.Compare(key) != 0) { throw new InvalidDataException("Was unable to retrieve the correct node. Data corruption possible"); } if (item->Flags == NodeFlags.MultiValuePageRef) { var tree = OpenOrCreateMultiValueTree(_tx, key, item); return tree.Iterate(); } var nestedPage = new Page(NodeHeader.DirectAccess(_tx, item), "multi tree", (ushort)NodeHeader.GetDataSize(_tx, item)); return new PageIterator(nestedPage); }
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 void MultiAddOnNewValue(Transaction tx, Slice key, Slice value, ushort? version, int maxNodeSize) { var requiredPageSize = Constants.PageHeaderSize + SizeOf.LeafEntry(-1, value, 0) + Constants.NodeOffsetSize; if (requiredPageSize > maxNodeSize) { // no choice, very big value, we might as well just put it in its own tree from the get go... // otherwise, we would have to put this in overflow page, and that won't save us any space anyway var tree = Create(tx, TreeFlags.MultiValue); tree.DirectAdd(value, 0); tx.AddMultiValueTree(this, key, tree); DirectAdd(key, sizeof (TreeRootHeader), NodeFlags.MultiValuePageRef); return; } var actualPageSize = (ushort) Math.Min(Utils.NearestPowerOfTwo(requiredPageSize), maxNodeSize); var ptr = DirectAdd(key, actualPageSize); var nestedPage = new Page(ptr, "multi tree", actualPageSize) { PageNumber = -1L,// hint that this is an inner page Lower = (ushort) Constants.PageHeaderSize, Upper = actualPageSize, Flags = PageFlags.Leaf, }; CheckConcurrency(key, value, version, 0, TreeActionType.Add); nestedPage.AddDataNode(0, value, 0, 0); }
public void MultiDelete(Slice key, Slice value, ushort? version = null) { State.IsModified = true; Lazy<Cursor> lazy; var page = FindPageFor(key, out lazy); if (page == null || page.LastMatch != 0) { return; //nothing to delete - key not found } page = _tx.ModifyPage(page.PageNumber, page); var item = page.GetNode(page.LastSearchPosition); if (item->Flags == NodeFlags.MultiValuePageRef) //multi-value tree exists { var tree = OpenOrCreateMultiValueTree(_tx, 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.EntriesCount != 0) return; _tx.TryRemoveMultiValueTree(this, key); _tx.FreePage(tree.State.RootPageNumber); Delete(key); } else // we use a nested page here { var nestedPage = new Page(NodeHeader.DirectAccess(_tx, item), "multi tree", (ushort)NodeHeader.GetDataSize(_tx, item)); var nestedItem = nestedPage.Search(value); if (nestedItem == null) // value not found 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); } nestedPage = new Page(nestedPagePtr, "multi tree", (ushort)NodeHeader.GetDataSize(_tx, item)) { LastSearchPosition = nestedPage.LastSearchPosition }; CheckConcurrency(key, value, version, nestedItem->Version, TreeActionType.Delete); nestedPage.RemoveNode(nestedPage.LastSearchPosition); if (nestedPage.NumberOfEntries == 0) Delete(key); } }
private void ExpandMultiTreeNestedPageSize(Transaction tx, Slice key, Slice value, byte* nestedPagePtr, ushort newSize, int currentSize) { Debug.Assert(newSize > currentSize); TemporaryPage tmp; using (tx.Environment.GetTemporaryPage(tx, out tmp)) { var tempPagePointer = tmp.TempPagePointer; NativeMethods.memcpy(tempPagePointer, nestedPagePtr, currentSize); Delete(key); // release our current page Page nestedPage = new Page(tempPagePointer, "multi tree", (ushort)currentSize); var ptr = DirectAdd(key, newSize); var newNestedPage = new Page(ptr, "multi tree", newSize) { Lower = (ushort)Constants.PageHeaderSize, Upper = newSize, Flags = PageFlags.Leaf, PageNumber = -1L // mark as invalid page number }; Slice nodeKey = new Slice(SliceOptions.Key); for (int i = 0; i < nestedPage.NumberOfEntries; i++) { var nodeHeader = nestedPage.GetNode(i); nodeKey.Set(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 } newNestedPage.Search(value); newNestedPage.AddDataNode(newNestedPage.LastSearchPosition, value, 0, 0); } }
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); }
public override int Write(Page page, long? pageNumber) { long startPage = pageNumber ?? page.PageNumber; int toWrite = page.IsOverflow ? GetNumberOfOverflowPages(page.OverflowSize) : 1; return WriteDirect(page, startPage, toWrite); }
private void WritePageDirect(Page page, int numberOfPagesIncludingOverflow) { var pageFromScratchBuffer = _env.ScratchBufferPool.Allocate(this, numberOfPagesIncludingOverflow); var dest = _env.ScratchBufferPool.AcquirePagePointer(pageFromScratchBuffer.ScratchFileNumber, pageFromScratchBuffer.PositionInScratchBuffer); Memory.Copy(dest, page.Base, numberOfPagesIncludingOverflow * AbstractPager.PageSize); _allocatedPagesInTransaction++; _dirtyPages.Add(page.PageNumber); page.Dirty = true; if (numberOfPagesIncludingOverflow > 1) _dirtyOverflowPages.Add(page.PageNumber + 1, numberOfPagesIncludingOverflow - 1); _scratchPagesTable[page.PageNumber] = pageFromScratchBuffer; _transactionPages.Add(pageFromScratchBuffer); }
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) == 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; }
internal void WriteDirect(Page[] pages, long nextPageNumber) { for (int i = 0; i < pages.Length; i++) { int numberOfPages = 1; var page = pages[i]; if (page.IsOverflow) { numberOfPages = (page.OverflowSize / AbstractPager.PageSize) + (page.OverflowSize % AbstractPager.PageSize == 0 ? 0 : 1); i += numberOfPages; _overflowPagesInTransaction += (numberOfPages - 1); } WritePageDirect(page, numberOfPages); } }
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)); _tree.State.LeafPages = 1; _tree.State.BranchPages = 0; _tree.State.Depth = 1; _tree.State.PageCount = 1; var rootPage = _tx.ModifyPage(node->PageNumber, _tree, null); _tree.State.RootPageNumber = rootPage.PageNumber; Debug.Assert(rootPage.Dirty); _cursor.Pop(); _cursor.Push(rootPage); _tx.FreePage(page.PageNumber); }
internal Page ModifyPage(long num, Tree tree, Page page) { _env.AssertFlushingNotFailed(); page = page ?? GetReadOnlyPage(num); if (page.Dirty) return page; if (_dirtyPages.Contains(num)) { page.Dirty = true; return page; } var newPage = AllocatePage(1, PageFlags.None, num); // allocate new page in a log file but with the same number Memory.Copy(newPage.Base, page.Base, AbstractPager.PageSize); newPage.LastSearchPosition = page.LastSearchPosition; newPage.LastMatch = page.LastMatch; tree.RecentlyFoundPages.Reset(num); return newPage; }
public void Push(Page p) { Pages.AddFirst(p); _pagesByNum.Add(p.PageNumber, p); }
public override int WriteDirect(Page start, long pagePosition, int pagesToWrite) { ThrowObjectDisposedIfNeeded(); int toCopy = pagesToWrite*PageSize; StdLib.memcpy(PagerState.MapBase + pagePosition*PageSize, start.Base, toCopy); return toCopy; }
public void MultiDelete(Slice key, Slice value, ushort?version = null) { State.IsModified = true; Lazy <Cursor> lazy; var page = FindPageFor(key, out lazy); if (page == null || page.LastMatch != 0) { return; //nothing to delete - key not found } page = _tx.ModifyPage(page.PageNumber, page); var item = page.GetNode(page.LastSearchPosition); if (item->Flags == NodeFlags.MultiValuePageRef) //multi-value tree exists { var tree = OpenOrCreateMultiValueTree(_tx, 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.EntriesCount != 0) { return; } _tx.TryRemoveMultiValueTree(this, key); _tx.FreePage(tree.State.RootPageNumber); Delete(key); } else // we use a nested page here { var nestedPage = new Page(NodeHeader.DirectAccess(_tx, item), "multi tree", (ushort)NodeHeader.GetDataSize(_tx, item)); var nestedItem = nestedPage.Search(value); if (nestedItem == null) // value not found { 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); } nestedPage = new Page(nestedPagePtr, "multi tree", (ushort)NodeHeader.GetDataSize(_tx, item)) { LastSearchPosition = nestedPage.LastSearchPosition }; CheckConcurrency(key, value, version, nestedItem->Version, TreeActionType.Delete); nestedPage.RemoveNode(nestedPage.LastSearchPosition); if (nestedPage.NumberOfEntries == 0) { Delete(key); } } }