diff --git a/DuckDB.NET.Data/DuckDBDataReader.cs b/DuckDB.NET.Data/DuckDBDataReader.cs index c6eaf136..2e750c89 100644 --- a/DuckDB.NET.Data/DuckDBDataReader.cs +++ b/DuckDB.NET.Data/DuckDBDataReader.cs @@ -305,17 +305,22 @@ public override object GetProviderSpecificValue(int ordinal) public override int GetValues(object[] values) { + ArgumentNullException.ThrowIfNull(values); CheckRowRead(); 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 b1721b38..1ff6eb36 100644 --- a/DuckDB.NET.Test/DuckDBDataReaderTests.cs +++ b/DuckDB.NET.Test/DuckDBDataReaderTests.cs @@ -843,5 +843,28 @@ public void GetOrdinalThrowsIndexOutOfRangeForAnUnknownName() reader.Invoking(r => r.GetOrdinal("missing")).Should().Throw(); } -} + // 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(); + + reader.GetValues(new object[0]).Should().Be(0); + reader.Invoking(r => r.GetValues(null!)).Should().Throw(); + + 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()); + } +}