Beispiel #1
0
        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);
        }
Beispiel #2
0
        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);
        }
Beispiel #3
0
        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);
        }
Beispiel #4
0
        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));
        }