/// <summary> /// The recursive portion of the find key algorithm invoked by the FindKey method of the parent Index. /// </summary> public bool FindKey(Stream AKey, object ACompareContext, SearchPath ASearchPath, out int AEntryNumber) { ASearchPath.Add(this); if (NodeType == IndexNodeType.Routing) { // Perform a binary search among the keys in this node to determine which streamid to follow for the next node bool LResult = NodeSearch(AKey, ACompareContext, out AEntryNumber); // If the key was found, use the given entry number, otherwise, use the one before the given entry AEntryNumber = LResult ? AEntryNumber : (AEntryNumber - 1); return (new IndexNode ( FProcess, FIndex, IndexUtility.GetStreamID(Data(AEntryNumber), 0) ).FindKey ( AKey, ACompareContext, ASearchPath, out AEntryNumber )); } else { // Perform a binary search among the keys in this node to determine which entry, if any, is equal to the given key return(NodeSearch(AKey, ACompareContext, out AEntryNumber)); } }
private bool FindIndexKey(Stream AKey, object ACompareContext, out IndexNode AIndexNode, out int AEntryNumber) { SearchPath LSearchPath = new SearchPath(); try { bool LResult = FAccessPath.FindKey(FProcess, AKey, ACompareContext, LSearchPath, out AEntryNumber); AIndexNode = LSearchPath.DisownAt(LSearchPath.Count - 1); return(LResult); } finally { LSearchPath.Dispose(); } }
// TODO: Asynchronous collapsed node recovery /// <summary>Deletes the entry given by AKey. The streams are disposed through the DisposeKey event, so it is the responsibility of the index user to dispose references within the streams.</summary> public void Delete(ServerProcess AProcess, Stream AKey) { int LEntryNumber; using (SearchPath LSearchPath = new SearchPath()) { bool LResult = FindKey(AProcess, AKey, null, LSearchPath, out LEntryNumber); if (!LResult) { throw new IndexException(IndexException.Codes.KeyNotFound); } InternalDelete(AProcess, LSearchPath, LEntryNumber); } }
/// <summary> /// The given streams are copied into the index, so references within the streams /// are considered owned by the index after the insert. /// </summary> public void Insert(ServerProcess AProcess, Stream AKey, Stream AData) { int LEntryNumber; using (SearchPath LSearchPath = new SearchPath()) { bool LResult = FindKey(AProcess, AKey, null, LSearchPath, out LEntryNumber); if (LResult) { throw new IndexException(IndexException.Codes.DuplicateKey); } InternalInsert(AProcess, LSearchPath, LEntryNumber, AKey, AData); } }
public void Delete(ServerProcess AProcess, Row ARow) { // Delete the row from all indexes Row LClusteredKey = GetIndexData(AProcess, FClusteredIndex.KeyRowType, new Row[] { ARow }); try { using (SearchPath LSearchPath = new SearchPath()) { int LEntryNumber; bool LResult = FClusteredIndex.FindKey(AProcess, LClusteredKey.Stream, null, LSearchPath, out LEntryNumber); if (!LResult) { throw new IndexException(IndexException.Codes.KeyNotFound); } Row LClusteredData = AProcess.RowManager.RequestRow(AProcess, FClusteredIndex.DataRowType, LSearchPath.DataNode.Data(LEntryNumber)); try { foreach (TableBufferIndex LBufferIndex in Indexes) { if (LBufferIndex != FClusteredIndex) { Row LKey = GetIndexData(AProcess, LBufferIndex.KeyRowType, new Row[] { LClusteredKey, LClusteredData }); try { LBufferIndex.Delete(AProcess, LKey.Stream); } finally { AProcess.RowManager.ReleaseRow(LKey); } } } } finally { AProcess.RowManager.ReleaseRow(LClusteredData); } } FClusteredIndex.Delete(AProcess, LClusteredKey.Stream); } finally { AProcess.RowManager.ReleaseRow(LClusteredKey); } }
public bool HasRow(ServerProcess AProcess, Row ARow) { Row LKey = GetIndexData(AProcess, FClusteredIndex.KeyRowType, new Row[] { ARow }); try { using (SearchPath LSearchPath = new SearchPath()) { int LEntryNumber; return(FClusteredIndex.FindKey(AProcess, LKey.Stream, null, LSearchPath, out LEntryNumber)); } } finally { AProcess.RowManager.ReleaseRow(LKey); } }
/// <summary>Updates the entry given by AOldKey to the entry given by ANewKey and ANewData. If AOldKey == ANewKey, the data for the entry is updated in place, otherwise it is moved to the location given by ANewKey.</summary> public void Update(ServerProcess AProcess, Stream AOldKey, Stream ANewKey, Stream ANewData) { int LEntryNumber; using (SearchPath LSearchPath = new SearchPath()) { bool LResult = FindKey(AProcess, AOldKey, null, LSearchPath, out LEntryNumber); if (!LResult) { throw new IndexException(IndexException.Codes.KeyNotFound); } if (Compare(AProcess, AOldKey, null, ANewKey, null) == 0) { if (ANewData != null) { LSearchPath.DataNode.Update(ANewData, LEntryNumber); } } else { if (ANewData == null) { ANewData = new MemoryStream(DataLength); ANewData.SetLength(DataLength); CopyData(AProcess, LSearchPath.DataNode.Data(LEntryNumber), ANewData); } InternalDelete(AProcess, LSearchPath, LEntryNumber); LSearchPath.Dispose(); LResult = FindKey(AProcess, ANewKey, null, LSearchPath, out LEntryNumber); if (LResult) { throw new IndexException(IndexException.Codes.DuplicateKey); } InternalInsert(AProcess, LSearchPath, LEntryNumber, ANewKey, ANewData); } } }
public void Update(ServerProcess AProcess, Row AOldRow, Row ANewRow) { // AOldRow must have at least the columns of the clustered index key Row LOldClusteredKey = GetIndexData(AProcess, FClusteredIndex.KeyRowType, new Row[] { AOldRow }); try { bool LIsClusteredIndexKeyAffected = GetIsIndexAffected(FClusteredIndex.KeyRowType, ANewRow); bool LIsClusteredIndexDataAffected = GetIsIndexAffected(FClusteredIndex.DataRowType, ANewRow); Row LNewClusteredKey = null; Row LNewClusteredData = null; try { // Update the row in each index using (SearchPath LSearchPath = new SearchPath()) { int LEntryNumber; bool LResult = FClusteredIndex.FindKey(AProcess, LOldClusteredKey.Stream, null, LSearchPath, out LEntryNumber); if (!LResult) { throw new IndexException(IndexException.Codes.KeyNotFound); } Row LOldClusteredData = AProcess.RowManager.RequestRow(AProcess, FClusteredIndex.DataRowType, LSearchPath.DataNode.Data(LEntryNumber)); try { bool LIsIndexAffected; foreach (TableBufferIndex LBufferIndex in Indexes) { if (LBufferIndex != FClusteredIndex) { LIsIndexAffected = GetIsIndexAffected(LBufferIndex.KeyRowType, ANewRow); if (LIsClusteredIndexKeyAffected || LIsIndexAffected) { Row LOldIndexKey = GetIndexData(AProcess, LBufferIndex.KeyRowType, new Row[] { LOldClusteredKey, LOldClusteredData }); try { Row LNewIndexKey = null; Row LNewIndexData = null; try { if (LIsIndexAffected) { LNewIndexKey = GetIndexData(AProcess, LBufferIndex.KeyRowType, new Row[] { ANewRow, LOldClusteredKey, LOldClusteredData }); } if (LIsClusteredIndexKeyAffected) { LNewIndexData = GetIndexData(AProcess, LBufferIndex.DataRowType, new Row[] { ANewRow, LOldClusteredKey, LOldClusteredData }); } if (LIsIndexAffected && LIsClusteredIndexKeyAffected) { LBufferIndex.Update(AProcess, LOldIndexKey.Stream, LNewIndexKey.Stream, LNewIndexData.Stream); LNewIndexKey.ValuesOwned = false; LNewIndexData.ValuesOwned = false; } else if (LIsIndexAffected) { LBufferIndex.Update(AProcess, LOldIndexKey.Stream, LNewIndexKey.Stream); LNewIndexKey.ValuesOwned = false; } else if (LIsClusteredIndexKeyAffected) { LBufferIndex.Update(AProcess, LOldIndexKey.Stream, LOldIndexKey.Stream, LNewIndexData.Stream); LNewIndexData.ValuesOwned = false; } } finally { if (LNewIndexKey != null) { AProcess.RowManager.ReleaseRow(LNewIndexKey); } if (LNewIndexData != null) { AProcess.RowManager.ReleaseRow(LNewIndexData); } } } finally { AProcess.RowManager.ReleaseRow(LOldIndexKey); } } } } if (LIsClusteredIndexKeyAffected) { LNewClusteredKey = GetIndexData(AProcess, FClusteredIndex.KeyRowType, new Row[] { ANewRow, LOldClusteredKey, LOldClusteredData }); } if (LIsClusteredIndexDataAffected) { LNewClusteredData = GetIndexData(AProcess, FClusteredIndex.DataRowType, new Row[] { ANewRow, LOldClusteredData }); } } finally { AProcess.RowManager.ReleaseRow(LOldClusteredData); } } if (LIsClusteredIndexKeyAffected && LIsClusteredIndexDataAffected) { FClusteredIndex.Update(AProcess, LOldClusteredKey.Stream, LNewClusteredKey.Stream, LNewClusteredData.Stream); LNewClusteredKey.ValuesOwned = false; LNewClusteredData.ValuesOwned = false; } else if (LIsClusteredIndexKeyAffected) { FClusteredIndex.Update(AProcess, LOldClusteredKey.Stream, LNewClusteredKey.Stream); LNewClusteredKey.ValuesOwned = false; } else if (LIsClusteredIndexDataAffected) { FClusteredIndex.Update(AProcess, LOldClusteredKey.Stream, LOldClusteredKey.Stream, LNewClusteredData.Stream); LNewClusteredData.ValuesOwned = false; } } finally { if (LNewClusteredKey != null) { AProcess.RowManager.ReleaseRow(LNewClusteredKey); } if (LNewClusteredData != null) { AProcess.RowManager.ReleaseRow(LNewClusteredData); } } } finally { AProcess.RowManager.ReleaseRow(LOldClusteredKey); } }
public void GetRow(Row ARow) { CheckActive(); CheckNotCrack(); if ((FAccessPath.IsClustered) || IsSubset(ARow, FAccessPath)) { Row LRow = FProcess.RowManager.RequestRow(FProcess, FAccessPath.KeyRowType, FIndexNode.Key(FEntryNumber)); try { LRow.CopyTo(ARow); } finally { FProcess.RowManager.ReleaseRow(LRow); } LRow = FProcess.RowManager.RequestRow(FProcess, FAccessPath.DataRowType, FIndexNode.Data(FEntryNumber)); try { LRow.CopyTo(ARow); } finally { FProcess.RowManager.ReleaseRow(LRow); } } else { using (SearchPath LSearchPath = new SearchPath()) { int LEntryNumber; bool LResult = FTable.ClusteredIndex.FindKey(FProcess, FIndexNode.Data(FEntryNumber), null, LSearchPath, out LEntryNumber); if (LResult) { Row LRow = FProcess.RowManager.RequestRow(FProcess, FTable.ClusteredIndex.KeyRowType, LSearchPath.DataNode.Key(LEntryNumber)); try { LRow.CopyTo(ARow); } finally { FProcess.RowManager.ReleaseRow(LRow); } LRow = FProcess.RowManager.RequestRow(FProcess, FTable.ClusteredIndex.DataRowType, LSearchPath.DataNode.Data(LEntryNumber)); try { LRow.CopyTo(ARow); } finally { FProcess.RowManager.ReleaseRow(LRow); } } else { throw new ScanException(ScanException.Codes.ClusteredRowNotFound); } } } }
/// <summary> /// Searches for the given key within the index. ASearchPath and AEntryNumber together give the /// location of the key in the index. If the search is successful, the entry exists, otherwise /// the EntryNumber indicates where the entry should be placed for an insert. /// </summary> /// <param name="AKey">The key to be found.</param> /// <param name="ACompareContext">Context information which will passed to the compare handler. May be null.</param> /// <param name="ASearchPath">A <see cref="SearchPath"/> which will contain the set of nodes along the search path to the key.</param> /// <param name="AEntryNumber">The EntryNumber where the key either is, or should be, depending on the result of the find.</param> /// <returns>A boolean value indicating the success or failure of the find.</returns> public bool FindKey(ServerProcess AProcess, Stream AKey, object ACompareContext, SearchPath ASearchPath, out int AEntryNumber) { return(new IndexNode(AProcess, this, FRootID).FindKey(AKey, ACompareContext, ASearchPath, out AEntryNumber)); }
private void InternalDelete(ServerProcess AProcess, SearchPath ASearchPath, int AEntryNumber) { ASearchPath.DataNode.Delete(AEntryNumber); }
private unsafe void InternalInsert(ServerProcess AProcess, SearchPath ASearchPath, int AEntryNumber, Stream AKey, Stream AData) { // Walk back up the search path, inserting data and splitting pages as necessary IndexNode LNewIndexNode; for (int LIndex = ASearchPath.Count - 1; LIndex >= 0; LIndex--) { if (ASearchPath[LIndex].EntryCount >= ASearchPath[LIndex].MaxEntries) { // Allocate a new node using (LNewIndexNode = AllocateNode(AProcess, ASearchPath[LIndex].NodeType)) { // Thread it into the list of leaves, if necessary if (LNewIndexNode.NodeType == IndexNodeType.Data) { LNewIndexNode.PriorNode = ASearchPath[LIndex].StreamID; LNewIndexNode.NextNode = ASearchPath[LIndex].NextNode; ASearchPath[LIndex].NextNode = LNewIndexNode.StreamID; if (LNewIndexNode.NextNode == StreamID.Null) { FTailID = LNewIndexNode.StreamID; } else { using (IndexNode LNextIndexNode = new IndexNode(AProcess, this, LNewIndexNode.NextNode)) { LNextIndexNode.PriorNode = LNewIndexNode.StreamID; } } } // Insert the upper half of the entries from ASearchPath[LIndex] into the new index node int LEntryCount = ASearchPath[LIndex].EntryCount; int LEntryPivot = LEntryCount / 2; for (int LEntryIndex = LEntryPivot; LEntryIndex < LEntryCount; LEntryIndex++) { LNewIndexNode.InternalInsert(ASearchPath[LIndex].Key(LEntryIndex), ASearchPath[LIndex].Data(LEntryIndex), LEntryIndex - LEntryPivot); // The internal call prevents the RowsMoved event from being fired } // Remove the upper half of the entries from ASearchPath[LIndex] for (int LEntryIndex = LEntryCount - 1; LEntryIndex >= LEntryPivot; LEntryIndex--) { ASearchPath[LIndex].InternalDelete(LEntryIndex); // The internal call prevents the data inside from being passed to the DisposeXXX methods, and prevents the RowDeleted event from being fired } // Notify index clients of the data change RowsMoved(ASearchPath[LIndex].StreamID, LEntryPivot, LEntryCount - 1, LNewIndexNode.StreamID, -LEntryPivot); // Insert the new entry into the appropriate node if (AEntryNumber >= LEntryPivot) { LNewIndexNode.Insert(AKey, AData, AEntryNumber - LEntryPivot); } else { ASearchPath[LIndex].Insert(AKey, AData, AEntryNumber); } // Reset the AKey, AData and AEntryNumber variables for the next round // The key for the entry one level up is the first key for the newly allocated node AKey = new MemoryStream(KeyLength); AKey.SetLength(KeyLength); CopyKey(AProcess, LNewIndexNode.Key(0), AKey); // The data is the StreamID of the newly allocated node AData = new MemoryStream(sizeof(StreamID)); IndexUtility.SetStreamID(AData, 0, LNewIndexNode.StreamID); } if (LIndex == 0) { // Allocate a new root node and grow the height of the tree by 1 using (LNewIndexNode = AllocateNode(AProcess, IndexNodeType.Routing)) { LNewIndexNode.Insert(AKey, AData, 0); AKey = new MemoryStream(KeyLength); AKey.SetLength(KeyLength); AData = new MemoryStream(DataLength); AData.SetLength(KeyLength); IndexUtility.SetStreamID(AData, 0, ASearchPath[LIndex].StreamID); LNewIndexNode.Insert(AKey, AData, 0); FRootID = LNewIndexNode.StreamID; FHeight++; } } else { bool LResult = ASearchPath[LIndex - 1].NodeSearch(AKey, null, out AEntryNumber); // At this point we should be guaranteed to have a routing key which does not exist in the parent node if (LResult) { throw new IndexException(IndexException.Codes.DuplicateRoutingKey); } } } else { ASearchPath[LIndex].Insert(AKey, AData, AEntryNumber); break; } } }