diff --git a/csharp.test/TestLogicalTypeRoundtrip.cs b/csharp.test/TestLogicalTypeRoundtrip.cs index 2daebe0b..173da0fc 100644 --- a/csharp.test/TestLogicalTypeRoundtrip.cs +++ b/csharp.test/TestLogicalTypeRoundtrip.cs @@ -285,6 +285,54 @@ public static void TestNestedStructArray([Values(Repetition.Required, Repetition fileReader.Close(); } + /// + /// This checks that LogicalColumnReader's GetEnumerator() works correctly + /// when the column is longer than the buffer length but not an exact multiple + /// (see https://github.com/G-Research/ParquetSharp/issues/242). + /// + [Test] + public static void TestLargeArraysEnumerator() + { + CheckEnumerator(4096, Enumerable.Range(0, 4100).ToArray()); + CheckEnumerator(4096, Enumerable.Range(0, 4100).Select(i => new[] {$"row {i}"}).ToArray()); + } + + private static void CheckEnumerator(int bufferLength, T[] values) + { + using var buffer = new ResizableBuffer(); + + using (var output = new BufferOutputStream(buffer)) + { + var columns = new Column[] {new Column("col0")}; + + using var fileWriter = new ParquetFileWriter(output, columns); + using var rowGroupWriter = fileWriter.AppendBufferedRowGroup(); + + using var col = rowGroupWriter.Column(0).LogicalWriter(bufferLength); + col.WriteBatch(values); + + fileWriter.Close(); + } + + using (var input = new BufferReader(buffer)) + { + using var fileReader = new ParquetFileReader(input); + using var rowGroupReader = fileReader.RowGroup(0); + + using var col = rowGroupReader.Column(0).LogicalReader(bufferLength); + + var enumerator = col.GetEnumerator(); + for (var i = 0; i < values.Length; i++) + { + Assert.IsTrue(enumerator.MoveNext()); + Assert.AreEqual(values[i], enumerator.Current); + } + Assert.IsFalse(enumerator.MoveNext()); + + fileReader.Close(); + } + } + [Test] public static void TestBigArrayRoundtrip() { diff --git a/csharp/LogicalColumnReader.cs b/csharp/LogicalColumnReader.cs index 906939ea..b7d38d02 100644 --- a/csharp/LogicalColumnReader.cs +++ b/csharp/LogicalColumnReader.cs @@ -44,7 +44,7 @@ internal static LogicalColumnReader Create(ColumnReader colu } } - public bool HasNext => Source.HasNext; + public abstract bool HasNext { get; } public abstract TReturn Apply(ILogicalColumnReaderVisitor visitor); @@ -153,6 +153,8 @@ private static (short definitionLevelDelta, int schemaSlice) StructSkip(ReadOnly return (definitionLevel, schemaSlice); } + public override bool HasNext => !_bufferedReader.IsEofDefinition; + public override int ReadBatch(Span destination) { short definitionLevel = 0;