Skip to content

Commit 38ddceb

Browse files
committed
Merge branch 'main' into develop
2 parents 8666a03 + 37858f9 commit 38ddceb

6 files changed

Lines changed: 95 additions & 10 deletions

File tree

DuckDB.NET.Data/Data.csproj

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
- Updated to DuckDB v1.5.3
77

88
New features:
9-
- Projection pushdown for table functions.
10-
- Estimated cardinality hints for table functions.
11-
- IList<T> column support in table functions.
9+
- Improved support for binding ICollection parameters (#326)
10+
11+
Fixes:
12+
- Fixed ObjectDisposedException in enum appender (#327)
1213
</PackageReleaseNotes>
1314
<SignAssembly>True</SignAssembly>
1415
<AssemblyOriginatorKeyFile>..\keyPair.snk</AssemblyOriginatorKeyFile>

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.Data/Extensions/TypeExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ internal static class TypeExtensions
3737
{ typeof(DateTimeOffset), DuckDBType.TimestampTz},
3838
{ typeof(BigInteger), DuckDBType.HugeInt},
3939
{ typeof(string), DuckDBType.Varchar},
40+
{ typeof(byte[]), DuckDBType.Blob},
4041
{ typeof(decimal), DuckDBType.Decimal},
4142
{ typeof(object), DuckDBType.Any},
4243
};

DuckDB.NET.Data/Mapping/DuckDBAppenderMap.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ public IDuckDBAppenderRow AppendToRow(IDuckDBAppenderRow row, T record)
9090
{
9191
// Reference types
9292
string v => row.AppendValue(v),
93+
byte[] v => row.AppendValue(v),
9394

9495
// Value types
9596
bool v => row.AppendValue(v),

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
{

DuckDB.NET.Test/DuckDBMappedAppenderTests.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,59 @@ public void MappedAppender_ValidatesTypeMatching()
6262
reader.GetDateTime(3).Should().Be(new DateTime(1985, 5, 20));
6363
}
6464

65+
// Example entity with a binary column
66+
public class FileEntry
67+
{
68+
public int Id { get; set; }
69+
public byte[] Data { get; set; } = Array.Empty<byte>();
70+
}
71+
72+
public class FileEntryMap : DuckDBAppenderMap<FileEntry>
73+
{
74+
public FileEntryMap()
75+
{
76+
Map(f => f.Id);
77+
Map(f => f.Data);
78+
}
79+
}
80+
81+
[Fact]
82+
public void MappedAppender_SupportsByteArray()
83+
{
84+
Command.CommandText = "CREATE TABLE file_entry(id INTEGER, data BLOB);";
85+
Command.ExecuteNonQuery();
86+
87+
var entries = new[]
88+
{
89+
new FileEntry { Id = 1, Data = new byte[] { 1, 2, 3 } },
90+
new FileEntry { Id = 2, Data = new byte[] { 10, 20, 30, 40 } },
91+
};
92+
93+
using (var appender = Connection.CreateAppender<FileEntry, FileEntryMap>("file_entry"))
94+
{
95+
appender.AppendRecords(entries);
96+
}
97+
98+
Command.CommandText = "SELECT id, data FROM file_entry ORDER BY id";
99+
using var reader = Command.ExecuteReader();
100+
101+
reader.Read().Should().BeTrue();
102+
reader.GetInt32(0).Should().Be(1);
103+
ReadBlob(reader, 1).Should().Equal(1, 2, 3);
104+
105+
reader.Read().Should().BeTrue();
106+
reader.GetInt32(0).Should().Be(2);
107+
ReadBlob(reader, 1).Should().Equal(10, 20, 30, 40);
108+
109+
static byte[] ReadBlob(System.Data.Common.DbDataReader reader, int ordinal)
110+
{
111+
using var stream = reader.GetStream(ordinal);
112+
using var memory = new MemoryStream();
113+
stream.CopyTo(memory);
114+
return memory.ToArray();
115+
}
116+
}
117+
65118
// Example with type mismatch - should throw
66119
public class WrongTypeMap : DuckDBAppenderMap<Person>
67120
{

0 commit comments

Comments
 (0)