From 5f5b6229e97ae61dc32be6a4c56df67f0892bd1f Mon Sep 17 00:00:00 2001 From: "Dailey, Alaric" Date: Tue, 20 Oct 2026 06:41:41 -0500 Subject: [PATCH] GetValues copies only what the array holds IDataRecord.GetValues: "You can pass an Object array that contains fewer than the number of columns that are contained in the resulting row. Only the amount of data the Object array holds is copied to the array." A shorter array threw IndexOutOfRangeException. Co-Authored-By: Claude Opus 5.5 --- DuckDB.NET.Data/DuckDBDataReader.cs | 8 ++++++-- DuckDB.NET.Test/DuckDBDataReaderTests.cs | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/DuckDB.NET.Data/DuckDBDataReader.cs b/DuckDB.NET.Data/DuckDBDataReader.cs index f9edb0be..ccda59d0 100644 --- a/DuckDB.NET.Data/DuckDBDataReader.cs +++ b/DuckDB.NET.Data/DuckDBDataReader.cs @@ -296,13 +296,17 @@ public override int GetValues(object[] values) var offset = rowsReadFromCurrentChunk - 1; - for (var i = 0; i < FieldCount; i++) + // IDataRecord.GetValues: "You can pass an Object array that contains fewer than the number of columns that are + // contained in the resulting row. Only the amount of data the Object array holds is copied to the array." + var count = Math.Min(values.Length, FieldCount); + + for (var i = 0; i < count; i++) { var reader = vectorReaders[i]; values[i] = reader.IsValid(offset) ? reader.GetValue(offset) : DBNull.Value; } - return FieldCount; + return count; } public override Stream GetStream(int ordinal) diff --git a/DuckDB.NET.Test/DuckDBDataReaderTests.cs b/DuckDB.NET.Test/DuckDBDataReaderTests.cs index 133ff737..0d18793b 100644 --- a/DuckDB.NET.Test/DuckDBDataReaderTests.cs +++ b/DuckDB.NET.Test/DuckDBDataReaderTests.cs @@ -819,4 +819,26 @@ public void ReadIntegerAsAnotherIntegerType() reader.Invoking(r => r.GetFieldValue(1)).Should().Throw().WithMessage("Cannot cast from Int32 to UInt64 in column negative"); reader.Invoking(r => r.GetFieldValue(2)).Should().Throw().WithMessage("Cannot cast from Int16 to Byte in column small"); } + + // https://learn.microsoft.com/dotnet/api/system.data.idatarecord.getvalues: "You can pass an Object array that + // contains fewer than the number of columns that are contained in the resulting row. Only the amount of data the + // Object array holds is copied to the array. You can also pass an Object array whose length is more than the + // number of columns that are contained in the resulting row." + [Fact] + public void GetValuesCopiesOnlyWhatTheArrayHolds() + { + Command.CommandText = "SELECT 1 AS a, 2 AS b, 3 AS c"; + using var reader = Command.ExecuteReader(); + reader.Read().Should().BeTrue(); + + var shorter = new object[2]; + reader.GetValues(shorter).Should().Be(2); + shorter.Should().Equal(1, 2); + + var longer = new object[5]; + reader.GetValues(longer).Should().Be(3); + longer.Take(3).Should().Equal(1, 2, 3); + longer.Skip(3).Should().AllSatisfy(value => value.Should().BeNull()); + } } +