Skip to content

Commit be13eee

Browse files
committed
Constraint Enumerator could be not correctly iterated multiple times.
1 parent f9a25da commit be13eee

File tree

2 files changed

+44
-41
lines changed

2 files changed

+44
-41
lines changed

BTDB/ODBLayer/RelationEnumerator.cs

+40-41
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class RelationConstraintEnumerator<T> : IEnumerator<T>, IEnumerable<T> where T :
2525
protected readonly RelationInfo.ItemLoaderInfo ItemLoader;
2626
readonly ConstraintInfo[] _constraints;
2727
protected readonly IKeyValueDBTransaction KeyValueTr;
28-
protected IKeyValueDBCursor _cursor;
28+
protected IKeyValueDBCursor? _cursor;
2929

3030
bool _seekNeeded;
3131

@@ -123,7 +123,7 @@ unsafe bool FindNextKey(bool first = false)
123123
_constraints[i].Constraint.WritePrefix(ref writer, _buffer);
124124
goto case IConstraint.MatchType.NoPrefix;
125125
case IConstraint.MatchType.NoPrefix:
126-
if (!_cursor.FindFirstKey(writer.GetSpan())) return false;
126+
if (!_cursor!.FindFirstKey(writer.GetSpan())) return false;
127127
goto nextKeyTest;
128128
default:
129129
throw new InvalidOperationException();
@@ -132,7 +132,7 @@ unsafe bool FindNextKey(bool first = false)
132132
i++;
133133
}
134134

135-
if (_cursor.FindFirstKey(writer.GetSpan()))
135+
if (_cursor!.FindFirstKey(writer.GetSpan()))
136136
{
137137
_cursor.GetKeyIntoMemWriter(ref writer);
138138
return true;
@@ -142,7 +142,7 @@ unsafe bool FindNextKey(bool first = false)
142142
}
143143

144144
goNextKey:
145-
if (!_cursor.FindNextKey(new())) return false;
145+
if (!_cursor!.FindNextKey(new())) return false;
146146
nextKeyTest:
147147
var key = _cursor.GetKeySpan(ref buf);
148148
var commonUpToOffset = (uint)writer.GetSpan().CommonPrefixLength(key);
@@ -303,32 +303,32 @@ unsafe bool FindNextKey(bool first = false)
303303
return false;
304304
}
305305

306-
public virtual T Current
307-
{
308-
get => (T)ItemLoader.CreateInstance(_transaction, _cursor, _key.GetSpan());
309-
}
306+
public virtual T Current => (T)ItemLoader.CreateInstance(_transaction, _cursor!, _key.GetSpan());
310307

311308
public virtual T CurrentFromKeySpan(ReadOnlySpan<byte> key)
312309
{
313-
return (T)ItemLoader.CreateInstance(_transaction, _cursor, key);
310+
return (T)ItemLoader.CreateInstance(_transaction, _cursor!, key);
314311
}
315312

316313
object IEnumerator.Current => Current!;
317314

318315
public void Reset()
319316
{
320-
_cursor.Invalidate();
317+
_cursor!.Invalidate();
321318
_seekNeeded = true;
322319
}
323320

324321
public void Dispose()
325322
{
326-
_cursor.Dispose();
323+
_cursor!.Dispose();
327324
_cursor = null!;
328325
}
329326

330327
public IEnumerator<T> GetEnumerator()
331328
{
329+
_seekNeeded = true;
330+
_cursor ??= KeyValueTr.CreateCursor();
331+
332332
return this;
333333
}
334334

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

340340
public T CurrentByKeyIndex(ulong keyIndex)
341341
{
342-
_cursor.FindKeyIndex((long)keyIndex);
342+
_cursor!.FindKeyIndex((long)keyIndex);
343343
_cursor.GetKeyIntoMemWriter(ref _key);
344344
return Current;
345345
}
@@ -365,7 +365,7 @@ public unsafe ulong FastGather(long skip, long take, ICollection<T> target)
365365
_constraints[i].Constraint.WritePrefix(ref writer, _buffer);
366366
goto case IConstraint.MatchType.NoPrefix;
367367
case IConstraint.MatchType.NoPrefix:
368-
if (!_cursor.FindFirstKey(writer.GetSpan())) return 0;
368+
if (!_cursor!.FindFirstKey(writer.GetSpan())) return 0;
369369
goto startIteration;
370370
default:
371371
throw new InvalidOperationException();
@@ -376,7 +376,7 @@ public unsafe ulong FastGather(long skip, long take, ICollection<T> target)
376376

377377
startIteration:
378378
var count = 0UL;
379-
_cursor.FastIterate(ref buf, (index, key) =>
379+
_cursor!.FastIterate(ref buf, (index, key) =>
380380
{
381381
if (!key.StartsWith(_key.GetSpan())) return true;
382382
fixed (void* _ = key)
@@ -451,11 +451,11 @@ public RelationConstraintSecondaryKeyEnumerator(IInternalObjectDBTransaction tr,
451451
}
452452

453453
public override T Current =>
454-
(T)_manipulator.CreateInstanceFromSecondaryKey(ItemLoader, _secondaryKeyIndex, _key.GetSpan());
454+
(T)_manipulator.CreateInstanceFromSecondaryKey(ItemLoader, _secondaryKeyIndex, _key.GetSpan())!;
455455

456456
public override T CurrentFromKeySpan(ReadOnlySpan<byte> key)
457457
{
458-
return (T)_manipulator.CreateInstanceFromSecondaryKey(ItemLoader, _secondaryKeyIndex, key);
458+
return (T)_manipulator.CreateInstanceFromSecondaryKey(ItemLoader, _secondaryKeyIndex, key)!;
459459
}
460460
}
461461

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

468-
protected IKeyValueDBCursor? _cursor;
468+
public IKeyValueDBCursor? Cursor;
469469
bool _seekNeeded;
470470

471-
protected readonly byte[] KeyBytes;
471+
readonly byte[] _keyBytes;
472472

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

491-
KeyBytes = keyBytes;
491+
_keyBytes = keyBytes;
492492
_seekNeeded = true;
493493
}
494494

495495
public bool MoveNext()
496496
{
497-
ObjectDisposedException.ThrowIf(_cursor == null, this);
497+
ObjectDisposedException.ThrowIf(Cursor == null, this);
498498
_transaction.ThrowIfDisposed();
499499
if (_seekNeeded)
500500
{
501-
var ret = _cursor.FindFirstKey(KeyBytes);
501+
var ret = Cursor.FindFirstKey(_keyBytes);
502502
_seekNeeded = false;
503503
return ret;
504504
}
505505

506-
return _cursor.FindNextKey(KeyBytes);
506+
return Cursor.FindNextKey(_keyBytes);
507507
}
508508

509509
public T Current
510510
{
511511
[SkipLocalsInit]
512512
get
513513
{
514-
ObjectDisposedException.ThrowIf(_cursor == null, this);
514+
ObjectDisposedException.ThrowIf(Cursor == null, this);
515515
Span<byte> keyBuffer = stackalloc byte[2048];
516-
var keyBytes = _cursor.GetKeySpan(keyBuffer);
516+
var keyBytes = Cursor.GetKeySpan(keyBuffer);
517517
return CreateInstance(keyBytes);
518518
}
519519
}
520520

521521
protected virtual T CreateInstance(in ReadOnlySpan<byte> keyBytes)
522522
{
523-
return (T)ItemLoader.CreateInstance(_transaction, _cursor!, keyBytes);
523+
return (T)ItemLoader.CreateInstance(_transaction, Cursor!, keyBytes);
524524
}
525525

526526
object IEnumerator.Current => Current!;
527527

528528
public void Reset()
529529
{
530-
_cursor ??= _keyValueTr.CreateCursor();
530+
Cursor ??= _keyValueTr.CreateCursor();
531531
_seekNeeded = true;
532532
}
533533

534534
public void Dispose()
535535
{
536-
_cursor?.Dispose();
537-
_cursor = null;
536+
Cursor?.Dispose();
537+
Cursor = null;
538538
}
539539

540540
public IEnumerator<T> GetEnumerator()
@@ -556,8 +556,6 @@ public RelationPrimaryKeyEnumerator(IInternalObjectDBTransaction tr, RelationInf
556556
: base(tr, relationInfo, keyBytes, loaderIndex)
557557
{
558558
}
559-
560-
public IKeyValueDBCursor Cursor => _cursor;
561559
}
562560

563561
class RelationSecondaryKeyEnumerator<T> : RelationEnumerator<T>
@@ -945,13 +943,14 @@ public class RelationAdvancedOrderedEnumerator<TKey, TValue> : IOrderedDictionar
945943
protected readonly IRelationDbManipulator Manipulator;
946944
protected readonly RelationInfo.ItemLoaderInfo ItemLoader;
947945
readonly IInternalObjectDBTransaction _tr;
946+
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable could be useful for debugging
948947
readonly IKeyValueDBTransaction _keyValueTr;
949948
IKeyValueDBCursor? _startCursor;
950949
IKeyValueDBCursor? _endCursor;
951950
IKeyValueDBCursor? _cursor;
952951
bool _seekNeeded;
953952
readonly bool _ascending;
954-
protected readonly byte[] KeyBytes;
953+
readonly byte[] _keyBytes;
955954
protected ReaderFun<TKey>? KeyReader;
956955

957956
public RelationAdvancedOrderedEnumerator(IRelationDbManipulator manipulator, EnumerationOrder order,
@@ -968,13 +967,13 @@ public RelationAdvancedOrderedEnumerator(IRelationDbManipulator manipulator, Enu
968967
_keyValueTr = _tr.KeyValueDBTransaction;
969968
_seekNeeded = true;
970969

971-
KeyBytes = startKeyBytes.Slice(0, prefixLen).ToArray();
970+
_keyBytes = startKeyBytes[..prefixLen].ToArray();
972971

973972

974973
_startCursor = _keyValueTr.CreateCursor();
975974
if (startKeyProposition == KeyProposition.Ignored)
976975
{
977-
if (!_startCursor.FindFirstKey(KeyBytes))
976+
if (!_startCursor.FindFirstKey(_keyBytes))
978977
{
979978
return;
980979
}
@@ -986,12 +985,12 @@ public RelationAdvancedOrderedEnumerator(IRelationDbManipulator manipulator, Enu
986985
case FindResult.Exact:
987986
if (startKeyProposition == KeyProposition.Excluded)
988987
{
989-
if (!_startCursor.FindNextKey(KeyBytes)) return;
988+
if (!_startCursor.FindNextKey(_keyBytes)) return;
990989
}
991990

992991
break;
993992
case FindResult.Previous:
994-
if (!_startCursor.FindNextKey(KeyBytes)) return;
993+
if (!_startCursor.FindNextKey(_keyBytes)) return;
995994
break;
996995
case FindResult.Next:
997996
break;
@@ -1009,7 +1008,7 @@ public RelationAdvancedOrderedEnumerator(IRelationDbManipulator manipulator, Enu
10091008

10101009
if (endKeyProposition == KeyProposition.Ignored)
10111010
{
1012-
if (!_endCursor.FindLastKey(KeyBytes)) return;
1011+
if (!_endCursor.FindLastKey(_keyBytes)) return;
10131012
}
10141013
else
10151014
{
@@ -1018,14 +1017,14 @@ public RelationAdvancedOrderedEnumerator(IRelationDbManipulator manipulator, Enu
10181017
case FindResult.Exact:
10191018
if (endKeyProposition == KeyProposition.Excluded)
10201019
{
1021-
if (!_endCursor.FindPreviousKey(KeyBytes)) return;
1020+
if (!_endCursor.FindPreviousKey(_keyBytes)) return;
10221021
}
10231022

10241023
break;
10251024
case FindResult.Previous:
10261025
break;
10271026
case FindResult.Next:
1028-
if (!_endCursor.FindPreviousKey(KeyBytes)) return;
1027+
if (!_endCursor.FindPreviousKey(_keyBytes)) return;
10291028
break;
10301029
case FindResult.NotFound:
10311030
return;
@@ -1115,7 +1114,7 @@ public unsafe bool NextKey(out TKey key)
11151114
{
11161115
if (_ascending)
11171116
{
1118-
if (!_cursor.FindNextKey(KeyBytes))
1117+
if (!_cursor.FindNextKey(_keyBytes))
11191118
{
11201119
key = default;
11211120
return false;
@@ -1129,7 +1128,7 @@ public unsafe bool NextKey(out TKey key)
11291128
}
11301129
else
11311130
{
1132-
if (!_cursor.FindPreviousKey(KeyBytes))
1131+
if (!_cursor.FindPreviousKey(_keyBytes))
11331132
{
11341133
key = default;
11351134
return false;
@@ -1143,7 +1142,7 @@ public unsafe bool NextKey(out TKey key)
11431142
}
11441143
}
11451144

1146-
var keySpan = _cursor.GetKeySpan(stackalloc byte[2048])[KeyBytes.Length..];
1145+
var keySpan = _cursor.GetKeySpan(stackalloc byte[2048])[_keyBytes.Length..];
11471146
fixed (void* _ = keySpan)
11481147
{
11491148
var reader = MemReader.CreateFromPinnedSpan(keySpan);

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## [unreleased]
44

5+
### Fixed
6+
7+
Constraint Enumerator could be not correctly iterated multiple times.
8+
59
## 33.0.5
610

711
### Fixed

0 commit comments

Comments
 (0)