Skip to content

Commit

Permalink
Constraint Enumerator could be not correctly iterated multiple times.
Browse files Browse the repository at this point in the history
  • Loading branch information
Bobris committed Jan 13, 2025
1 parent f9a25da commit be13eee
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 41 deletions.
81 changes: 40 additions & 41 deletions BTDB/ODBLayer/RelationEnumerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class RelationConstraintEnumerator<T> : IEnumerator<T>, IEnumerable<T> where T :
protected readonly RelationInfo.ItemLoaderInfo ItemLoader;
readonly ConstraintInfo[] _constraints;
protected readonly IKeyValueDBTransaction KeyValueTr;
protected IKeyValueDBCursor _cursor;
protected IKeyValueDBCursor? _cursor;

bool _seekNeeded;

Expand Down Expand Up @@ -123,7 +123,7 @@ unsafe bool FindNextKey(bool first = false)
_constraints[i].Constraint.WritePrefix(ref writer, _buffer);
goto case IConstraint.MatchType.NoPrefix;
case IConstraint.MatchType.NoPrefix:
if (!_cursor.FindFirstKey(writer.GetSpan())) return false;
if (!_cursor!.FindFirstKey(writer.GetSpan())) return false;
goto nextKeyTest;
default:
throw new InvalidOperationException();
Expand All @@ -132,7 +132,7 @@ unsafe bool FindNextKey(bool first = false)
i++;
}

if (_cursor.FindFirstKey(writer.GetSpan()))
if (_cursor!.FindFirstKey(writer.GetSpan()))
{
_cursor.GetKeyIntoMemWriter(ref writer);
return true;
Expand All @@ -142,7 +142,7 @@ unsafe bool FindNextKey(bool first = false)
}

goNextKey:
if (!_cursor.FindNextKey(new())) return false;
if (!_cursor!.FindNextKey(new())) return false;
nextKeyTest:
var key = _cursor.GetKeySpan(ref buf);
var commonUpToOffset = (uint)writer.GetSpan().CommonPrefixLength(key);
Expand Down Expand Up @@ -303,32 +303,32 @@ unsafe bool FindNextKey(bool first = false)
return false;
}

public virtual T Current
{
get => (T)ItemLoader.CreateInstance(_transaction, _cursor, _key.GetSpan());
}
public virtual T Current => (T)ItemLoader.CreateInstance(_transaction, _cursor!, _key.GetSpan());

public virtual T CurrentFromKeySpan(ReadOnlySpan<byte> key)
{
return (T)ItemLoader.CreateInstance(_transaction, _cursor, key);
return (T)ItemLoader.CreateInstance(_transaction, _cursor!, key);
}

object IEnumerator.Current => Current!;

public void Reset()
{
_cursor.Invalidate();
_cursor!.Invalidate();
_seekNeeded = true;
}

public void Dispose()
{
_cursor.Dispose();
_cursor!.Dispose();
_cursor = null!;
}

public IEnumerator<T> GetEnumerator()
{
_seekNeeded = true;
_cursor ??= KeyValueTr.CreateCursor();

return this;
}

Expand All @@ -339,7 +339,7 @@ IEnumerator IEnumerable.GetEnumerator()

public T CurrentByKeyIndex(ulong keyIndex)
{
_cursor.FindKeyIndex((long)keyIndex);
_cursor!.FindKeyIndex((long)keyIndex);
_cursor.GetKeyIntoMemWriter(ref _key);
return Current;
}
Expand All @@ -365,7 +365,7 @@ public unsafe ulong FastGather(long skip, long take, ICollection<T> target)
_constraints[i].Constraint.WritePrefix(ref writer, _buffer);
goto case IConstraint.MatchType.NoPrefix;
case IConstraint.MatchType.NoPrefix:
if (!_cursor.FindFirstKey(writer.GetSpan())) return 0;
if (!_cursor!.FindFirstKey(writer.GetSpan())) return 0;
goto startIteration;
default:
throw new InvalidOperationException();
Expand All @@ -376,7 +376,7 @@ public unsafe ulong FastGather(long skip, long take, ICollection<T> target)

startIteration:
var count = 0UL;
_cursor.FastIterate(ref buf, (index, key) =>
_cursor!.FastIterate(ref buf, (index, key) =>
{
if (!key.StartsWith(_key.GetSpan())) return true;
fixed (void* _ = key)
Expand Down Expand Up @@ -451,11 +451,11 @@ public RelationConstraintSecondaryKeyEnumerator(IInternalObjectDBTransaction tr,
}

public override T Current =>
(T)_manipulator.CreateInstanceFromSecondaryKey(ItemLoader, _secondaryKeyIndex, _key.GetSpan());
(T)_manipulator.CreateInstanceFromSecondaryKey(ItemLoader, _secondaryKeyIndex, _key.GetSpan())!;

public override T CurrentFromKeySpan(ReadOnlySpan<byte> key)
{
return (T)_manipulator.CreateInstanceFromSecondaryKey(ItemLoader, _secondaryKeyIndex, key);
return (T)_manipulator.CreateInstanceFromSecondaryKey(ItemLoader, _secondaryKeyIndex, key)!;
}
}

Expand All @@ -465,10 +465,10 @@ class RelationEnumerator<T> : IEnumerator<T>, IEnumerable<T>
protected readonly RelationInfo.ItemLoaderInfo ItemLoader;
readonly IKeyValueDBTransaction _keyValueTr;

protected IKeyValueDBCursor? _cursor;
public IKeyValueDBCursor? Cursor;
bool _seekNeeded;

protected readonly byte[] KeyBytes;
readonly byte[] _keyBytes;

public RelationEnumerator(IInternalObjectDBTransaction tr, RelationInfo relationInfo,
ReadOnlySpan<byte> keyBytes, int loaderIndex) : this(tr, keyBytes.ToArray(),
Expand All @@ -488,53 +488,53 @@ public RelationEnumerator(IInternalObjectDBTransaction tr, byte[] keyBytes, Rela
ItemLoader = loaderInfo;
_keyValueTr = _transaction.KeyValueDBTransaction;

KeyBytes = keyBytes;
_keyBytes = keyBytes;
_seekNeeded = true;
}

public bool MoveNext()
{
ObjectDisposedException.ThrowIf(_cursor == null, this);
ObjectDisposedException.ThrowIf(Cursor == null, this);
_transaction.ThrowIfDisposed();
if (_seekNeeded)
{
var ret = _cursor.FindFirstKey(KeyBytes);
var ret = Cursor.FindFirstKey(_keyBytes);
_seekNeeded = false;
return ret;
}

return _cursor.FindNextKey(KeyBytes);
return Cursor.FindNextKey(_keyBytes);
}

public T Current
{
[SkipLocalsInit]
get
{
ObjectDisposedException.ThrowIf(_cursor == null, this);
ObjectDisposedException.ThrowIf(Cursor == null, this);
Span<byte> keyBuffer = stackalloc byte[2048];
var keyBytes = _cursor.GetKeySpan(keyBuffer);
var keyBytes = Cursor.GetKeySpan(keyBuffer);
return CreateInstance(keyBytes);
}
}

protected virtual T CreateInstance(in ReadOnlySpan<byte> keyBytes)
{
return (T)ItemLoader.CreateInstance(_transaction, _cursor!, keyBytes);
return (T)ItemLoader.CreateInstance(_transaction, Cursor!, keyBytes);
}

object IEnumerator.Current => Current!;

public void Reset()
{
_cursor ??= _keyValueTr.CreateCursor();
Cursor ??= _keyValueTr.CreateCursor();
_seekNeeded = true;
}

public void Dispose()
{
_cursor?.Dispose();
_cursor = null;
Cursor?.Dispose();
Cursor = null;
}

public IEnumerator<T> GetEnumerator()
Expand All @@ -556,8 +556,6 @@ public RelationPrimaryKeyEnumerator(IInternalObjectDBTransaction tr, RelationInf
: base(tr, relationInfo, keyBytes, loaderIndex)
{
}

public IKeyValueDBCursor Cursor => _cursor;
}

class RelationSecondaryKeyEnumerator<T> : RelationEnumerator<T>
Expand Down Expand Up @@ -945,13 +943,14 @@ public class RelationAdvancedOrderedEnumerator<TKey, TValue> : IOrderedDictionar
protected readonly IRelationDbManipulator Manipulator;
protected readonly RelationInfo.ItemLoaderInfo ItemLoader;
readonly IInternalObjectDBTransaction _tr;
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable could be useful for debugging
readonly IKeyValueDBTransaction _keyValueTr;
IKeyValueDBCursor? _startCursor;
IKeyValueDBCursor? _endCursor;
IKeyValueDBCursor? _cursor;
bool _seekNeeded;
readonly bool _ascending;
protected readonly byte[] KeyBytes;
readonly byte[] _keyBytes;
protected ReaderFun<TKey>? KeyReader;

public RelationAdvancedOrderedEnumerator(IRelationDbManipulator manipulator, EnumerationOrder order,
Expand All @@ -968,13 +967,13 @@ public RelationAdvancedOrderedEnumerator(IRelationDbManipulator manipulator, Enu
_keyValueTr = _tr.KeyValueDBTransaction;
_seekNeeded = true;

KeyBytes = startKeyBytes.Slice(0, prefixLen).ToArray();
_keyBytes = startKeyBytes[..prefixLen].ToArray();


_startCursor = _keyValueTr.CreateCursor();
if (startKeyProposition == KeyProposition.Ignored)
{
if (!_startCursor.FindFirstKey(KeyBytes))
if (!_startCursor.FindFirstKey(_keyBytes))
{
return;
}
Expand All @@ -986,12 +985,12 @@ public RelationAdvancedOrderedEnumerator(IRelationDbManipulator manipulator, Enu
case FindResult.Exact:
if (startKeyProposition == KeyProposition.Excluded)
{
if (!_startCursor.FindNextKey(KeyBytes)) return;
if (!_startCursor.FindNextKey(_keyBytes)) return;
}

break;
case FindResult.Previous:
if (!_startCursor.FindNextKey(KeyBytes)) return;
if (!_startCursor.FindNextKey(_keyBytes)) return;
break;
case FindResult.Next:
break;
Expand All @@ -1009,7 +1008,7 @@ public RelationAdvancedOrderedEnumerator(IRelationDbManipulator manipulator, Enu

if (endKeyProposition == KeyProposition.Ignored)
{
if (!_endCursor.FindLastKey(KeyBytes)) return;
if (!_endCursor.FindLastKey(_keyBytes)) return;
}
else
{
Expand All @@ -1018,14 +1017,14 @@ public RelationAdvancedOrderedEnumerator(IRelationDbManipulator manipulator, Enu
case FindResult.Exact:
if (endKeyProposition == KeyProposition.Excluded)
{
if (!_endCursor.FindPreviousKey(KeyBytes)) return;
if (!_endCursor.FindPreviousKey(_keyBytes)) return;
}

break;
case FindResult.Previous:
break;
case FindResult.Next:
if (!_endCursor.FindPreviousKey(KeyBytes)) return;
if (!_endCursor.FindPreviousKey(_keyBytes)) return;
break;
case FindResult.NotFound:
return;
Expand Down Expand Up @@ -1115,7 +1114,7 @@ public unsafe bool NextKey(out TKey key)
{
if (_ascending)
{
if (!_cursor.FindNextKey(KeyBytes))
if (!_cursor.FindNextKey(_keyBytes))
{
key = default;
return false;
Expand All @@ -1129,7 +1128,7 @@ public unsafe bool NextKey(out TKey key)
}
else
{
if (!_cursor.FindPreviousKey(KeyBytes))
if (!_cursor.FindPreviousKey(_keyBytes))
{
key = default;
return false;
Expand All @@ -1143,7 +1142,7 @@ public unsafe bool NextKey(out TKey key)
}
}

var keySpan = _cursor.GetKeySpan(stackalloc byte[2048])[KeyBytes.Length..];
var keySpan = _cursor.GetKeySpan(stackalloc byte[2048])[_keyBytes.Length..];
fixed (void* _ = keySpan)
{
var reader = MemReader.CreateFromPinnedSpan(keySpan);
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## [unreleased]

### Fixed

Constraint Enumerator could be not correctly iterated multiple times.

## 33.0.5

### Fixed
Expand Down

0 comments on commit be13eee

Please sign in to comment.