Example #1
0
        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());
        }
Example #2
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, (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);
            }
        }
Example #4
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);
        }
Example #5
0
        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();
        }
Example #6
0
        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);
                }
            }
        }