Skip to content

Commit cee85a7

Browse files
Giorgiclaude
andcommitted
Fix IndexOutOfRangeException in reader metadata after exhausting result. Fixes #332
DataTable.Load (and similar consumers) read all rows and then call NextResult() to probe for more result sets. InitNextReader() disposed the vector readers and set vectorReaders=[] at the top of the method, before checking whether another result set existed. When none did, it returned false with vectorReaders emptied but fieldCount still holding the previous result's column count. A subsequent GetSchemaTable()/GetName()/GetFieldType() then indexed past the emptied array and threw IndexOutOfRangeException. This regressed in 1.5.0 with the cross-chunk reader-reuse refactor; in 1.4.4 the readers were only replaced when a new result set was found, so an exhausted reader kept its column metadata. Move the dispose + clear inside the QueryResult branch so they run only when switching to a new result set. An exhausted reader now retains its column metadata, restoring 1.4.4 behavior. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 9efba7e commit cee85a7

2 files changed

Lines changed: 36 additions & 7 deletions

File tree

DuckDB.NET.Data/DuckDBDataReader.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,18 @@ internal DuckDBDataReader(DuckDBCommand command, IEnumerable<DuckDBResult> query
3838

3939
private bool InitNextReader()
4040
{
41-
foreach (var reader in vectorReaders)
42-
{
43-
reader?.Dispose();
44-
}
45-
46-
vectorReaders = [];
47-
4841
while (resultEnumerator.MoveNext())
4942
{
5043
var result = resultEnumerator.Current;
5144
if (NativeMethods.Query.DuckDBResultReturnType(result) == DuckDBResultType.QueryResult)
5245
{
46+
foreach (var reader in vectorReaders)
47+
{
48+
reader?.Dispose();
49+
}
50+
51+
vectorReaders = [];
52+
5353
currentChunkIndex = 0;
5454
currentResult = result;
5555

DuckDB.NET.Test/DuckDBDataReaderTests.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,35 @@ public void ReadDecimalSchema()
353353
schemaTable.Rows[0]["NumericPrecision"].Should().Be(10);
354354
}
355355

356+
[Theory]
357+
[InlineData(true)]
358+
[InlineData(false)]
359+
public void Repro332_SchemaTableAfterNextResult(bool streaming)
360+
{
361+
Command.CommandText = "SELECT 1 AS a, 2 AS b";
362+
Command.UseStreamingMode = streaming;
363+
using var reader = Command.ExecuteReader();
364+
365+
while (reader.Read()) { }
366+
reader.NextResult().Should().BeFalse(); // exhausts result set, like DataTable.Load does
367+
368+
var schemaTable = reader.GetSchemaTable();
369+
schemaTable.Rows.Count.Should().Be(2);
370+
}
371+
372+
[Fact]
373+
public void Repro332_DataTableLoad()
374+
{
375+
Command.CommandText = "SELECT 1 AS a, 2 AS b";
376+
using var reader = Command.ExecuteReader();
377+
378+
var table = new DataTable();
379+
table.Load(reader); // calls NextResult() internally after reading rows
380+
381+
var schemaTable = reader.GetSchemaTable();
382+
schemaTable.Rows.Count.Should().Be(2);
383+
}
384+
356385
[Fact]
357386
public void ReadDecimalSchemaWithoutTableRow()
358387
{

0 commit comments

Comments
 (0)