Exemplo n.º 1
0
	    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();
					}
			    }
		    }
	    }
Exemplo n.º 2
0
 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();
 }
Exemplo n.º 3
0
        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);
        }
Exemplo n.º 4
0
        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);
        }
Exemplo n.º 5
0
        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;
        }
Exemplo n.º 6
0
 public FoundPage(long number, Page page, MemorySlice firstKey, MemorySlice lastKey, long[] cursorPath)
 {
     Number = number;
     Page = page;
     FirstKey = firstKey;
     LastKey = lastKey;
     CursorPath = cursorPath;
 }
Exemplo n.º 7
0
 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);
		}
Exemplo n.º 9
0
        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;
        }
Exemplo n.º 10
0
		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);
		}
Exemplo n.º 11
0
		public void RecordNewPage(Page p, int num)
		{
			PageCount += num;

			if (p.IsBranch)
			{
				BranchPages++;
			}
			else if (p.IsLeaf)
			{
				LeafPages++;
			}
			else if (p.IsOverflow)
			{
				OverflowPages += num;
			}
		}
Exemplo n.º 12
0
		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;
			}
		}
Exemplo n.º 13
0
        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;
        }
Exemplo n.º 14
0
		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();
		}
Exemplo n.º 15
0
        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);
            }
        }
Exemplo n.º 16
0
 public abstract int Write(Page page, long? pageNumber);
Exemplo n.º 17
0
 public abstract int WriteDirect(Page start, long pagePosition, int pagesToWrite);
Exemplo n.º 18
0
		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);
		}
Exemplo n.º 19
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);
		}
Exemplo n.º 20
0
		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);
		}
Exemplo n.º 21
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);
			}
		}
Exemplo n.º 22
0
		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);
			}
		}
Exemplo n.º 23
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);
        }
Exemplo n.º 24
0
		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);
		}
Exemplo n.º 25
0
		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);
		}
Exemplo n.º 26
0
		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;
		}
Exemplo n.º 27
0
		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);				
			}
		}
Exemplo n.º 28
0
        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);
        }
Exemplo n.º 29
0
		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;
		}
Exemplo n.º 30
0
 public void Push(Page p)
 {
     Pages.AddFirst(p);
     _pagesByNum.Add(p.PageNumber, p);
 }
Exemplo n.º 31
0
		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;
		}
Exemplo n.º 32
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);
                }
            }
        }