This is an automated email from the ASF dual-hosted git repository.
CurtHagenlocher pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-adbc.git
The following commit(s) were added to refs/heads/main by this push:
new 2fb366fcf test(csharp): upgrade DuckDB to 1.5.6 and test ADBC 1.1 APIs
(#4883)
2fb366fcf is described below
commit 2fb366fcf921ce9b67fae5ddf6b4d27e0bf49341
Author: Curt Hagenlocher <[email protected]>
AuthorDate: Mon Oct 12 19:04:45 2026 -0700
test(csharp): upgrade DuckDB to 1.5.6 and test ADBC 1.1 APIs (#4883)
## What's changed
DuckDB 1.5 adds support for ADBC 1.1. This upgrades the DuckDB used by
the C# tests to 1.5.6 and adds tests for the ADBC 1.1 APIs that the C#
importer currently exposes:
- `StatementExecuteSchema`: checks the returned schema against the
actual result schema, and that `ExecuteSchema` doesn't execute the
statement (`INSERT ... RETURNING` inserts no rows).
- `ConnectionCancel` / `StatementCancel`: cancelling while idle is
harmless, and cancelling a long-running query interrupts it.
### Fetching DuckDB
`DuckDB.NET.Bindings.Full` 1.5.x no longer targets `netstandard2.0`, so
a `PackageReference` fails to restore for net472. The tests (and
Benchmarks) only use the native library from the package, so it's now
fetched with `PackageDownload`, which skips the framework compatibility
check. The version lives in a `DuckDbVersion` property in
`Directory.Packages.props`. As a result, all target frameworks now test
against the same DuckDB version.
`PackageDownload` didn't restore when its version came from central
package management, which is why the version is a property instead.
Dependabot probably won't update it.
### Notes
- DuckDB reports a cancelled query as `ADBC_STATUS_INVALID_ARGUMENT`
("INTERRUPT Error: Interrupted!") rather than `ADBC_STATUS_CANCELLED`,
so the cancel tests check the message.
- `GetOption` and typed `SetOption` aren't yet exposed by the C#
importer, so they're not tested here. That will be a follow-up PR.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 5.5 <[email protected]>
---
csharp/Benchmarks/Benchmarks.csproj | 16 ++-
csharp/Directory.Packages.props | 6 +-
.../Apache.Arrow.Adbc.Testing.csproj | 16 ++-
.../test/Apache.Arrow.Adbc.Tests/DuckDbFixture.cs | 2 +
.../Apache.Arrow.Adbc.Tests/ImportedDuckDbTests.cs | 113 +++++++++++++++++++++
5 files changed, 142 insertions(+), 11 deletions(-)
diff --git a/csharp/Benchmarks/Benchmarks.csproj
b/csharp/Benchmarks/Benchmarks.csproj
index 8cac6da51..34066b238 100644
--- a/csharp/Benchmarks/Benchmarks.csproj
+++ b/csharp/Benchmarks/Benchmarks.csproj
@@ -15,7 +15,10 @@
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" />
- <PackageReference Include="DuckDB.NET.Bindings.Full"
GeneratePathProperty="true" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <PackageDownload Include="DuckDB.NET.Bindings.Full"
Version="[$(DuckDbVersion)]" />
</ItemGroup>
<ItemGroup>
@@ -29,10 +32,13 @@
<ProjectReference
Include="..\test\Apache.Arrow.Adbc.Tests\Apache.Arrow.Adbc.Testing.csproj" />
</ItemGroup>
- <Target Name="CopyDuckDb" AfterTargets="Build"
Condition="'$(PkgDuckDB_NET_Bindings_Full)' != ''">
- <Copy
Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))'
== 'true'"
SourceFiles="$(PkgDuckDB_NET_Bindings_Full)\runtimes\win-$(ProcessArchitecture)\native\duckdb.dll"
DestinationFolder="$(OutputPath)" />
- <Copy
Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))'
== 'true'"
SourceFiles="$(PkgDuckDB_NET_Bindings_Full)\runtimes\linux-$(ProcessArchitecture)\native\libduckdb.so"
DestinationFolder="$(OutputPath)" />
- <Copy
Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))'
== 'true'"
SourceFiles="$(PkgDuckDB_NET_Bindings_Full)\runtimes\osx\native\libduckdb.dylib"
DestinationFolder="$(OutputPath)" />
+ <Target Name="CopyDuckDb" AfterTargets="Build">
+ <PropertyGroup>
+
<DuckDbPackagePath>$(NuGetPackageRoot)duckdb.net.bindings.full\$(DuckDbVersion)</DuckDbPackagePath>
+ </PropertyGroup>
+ <Copy
Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))'
== 'true'"
SourceFiles="$(DuckDbPackagePath)\runtimes\win-$(ProcessArchitecture)\native\duckdb.dll"
DestinationFolder="$(OutputPath)" />
+ <Copy
Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))'
== 'true'"
SourceFiles="$(DuckDbPackagePath)\runtimes\linux-$(ProcessArchitecture)\native\libduckdb.so"
DestinationFolder="$(OutputPath)" />
+ <Copy
Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))'
== 'true'"
SourceFiles="$(DuckDbPackagePath)\runtimes\osx\native\libduckdb.dylib"
DestinationFolder="$(OutputPath)" />
</Target>
</Project>
diff --git a/csharp/Directory.Packages.props b/csharp/Directory.Packages.props
index e4b3e2de6..e6c943ca3 100644
--- a/csharp/Directory.Packages.props
+++ b/csharp/Directory.Packages.props
@@ -18,6 +18,11 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
+ <!--
+ DuckDB is fetched with PackageDownload rather than PackageReference
because only the native
+ library is used, and newer DuckDB.NET packages don't support every
target framework we test.
+ -->
+ <DuckDbVersion>1.5.6</DuckDbVersion>
</PropertyGroup>
<ItemGroup>
@@ -30,7 +35,6 @@
<PackageVersion Include="Azure.Identity" Version="1.17.1" />
<PackageVersion Include="BenchmarkDotNet" Version="0.15.8" />
<PackageVersion Include="coverlet.collector" Version="10.1.0" />
- <PackageVersion Include="DuckDB.NET.Bindings.Full" Version="1.4.4" />
<PackageVersion Include="K4os.Compression.LZ4" Version="1.3.8" />
<PackageVersion Include="K4os.Compression.LZ4.Streams" Version="1.3.8" />
<PackageVersion Include="Google.Cloud.BigQuery.Storage.V1"
Version="3.17.0" />
diff --git
a/csharp/test/Apache.Arrow.Adbc.Tests/Apache.Arrow.Adbc.Testing.csproj
b/csharp/test/Apache.Arrow.Adbc.Tests/Apache.Arrow.Adbc.Testing.csproj
index 252aa2a38..56bcd0c86 100644
--- a/csharp/test/Apache.Arrow.Adbc.Tests/Apache.Arrow.Adbc.Testing.csproj
+++ b/csharp/test/Apache.Arrow.Adbc.Tests/Apache.Arrow.Adbc.Testing.csproj
@@ -28,19 +28,25 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles;
analyzers</IncludeAssets>
</PackageReference>
- <PackageReference Include="DuckDB.NET.Bindings.Full"
GeneratePathProperty="true" />
<PackageReference Include="Xunit.SkippableFact" />
</ItemGroup>
+ <ItemGroup>
+ <PackageDownload Include="DuckDB.NET.Bindings.Full"
Version="[$(DuckDbVersion)]" />
+ </ItemGroup>
+
<ItemGroup>
<ProjectReference
Include="..\..\src\Apache.Arrow.Adbc\Apache.Arrow.Adbc.csproj" />
<ProjectReference
Include="..\..\src\Client\Apache.Arrow.Adbc.Client.csproj" />
</ItemGroup>
- <Target Name="CopyDuckDb" AfterTargets="Build"
Condition="'$(PkgDuckDB_NET_Bindings_Full)' != ''">
- <Copy
Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))'
== 'true'"
SourceFiles="$(PkgDuckDB_NET_Bindings_Full)\runtimes\win-$(ProcessArchitecture)\native\duckdb.dll"
DestinationFolder="$(OutputPath)" />
- <Copy
Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))'
== 'true'"
SourceFiles="$(PkgDuckDB_NET_Bindings_Full)\runtimes\linux-$(ProcessArchitecture)\native\libduckdb.so"
DestinationFolder="$(OutputPath)" />
- <Copy
Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))'
== 'true'"
SourceFiles="$(PkgDuckDB_NET_Bindings_Full)\runtimes\osx\native\libduckdb.dylib"
DestinationFolder="$(OutputPath)" />
+ <Target Name="CopyDuckDb" AfterTargets="Build">
+ <PropertyGroup>
+
<DuckDbPackagePath>$(NuGetPackageRoot)duckdb.net.bindings.full\$(DuckDbVersion)</DuckDbPackagePath>
+ </PropertyGroup>
+ <Copy
Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))'
== 'true'"
SourceFiles="$(DuckDbPackagePath)\runtimes\win-$(ProcessArchitecture)\native\duckdb.dll"
DestinationFolder="$(OutputPath)" />
+ <Copy
Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))'
== 'true'"
SourceFiles="$(DuckDbPackagePath)\runtimes\linux-$(ProcessArchitecture)\native\libduckdb.so"
DestinationFolder="$(OutputPath)" />
+ <Copy
Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))'
== 'true'"
SourceFiles="$(DuckDbPackagePath)\runtimes\osx\native\libduckdb.dylib"
DestinationFolder="$(OutputPath)" />
</Target>
</Project>
diff --git a/csharp/test/Apache.Arrow.Adbc.Tests/DuckDbFixture.cs
b/csharp/test/Apache.Arrow.Adbc.Tests/DuckDbFixture.cs
index 73fc9fe26..fe1019c17 100644
--- a/csharp/test/Apache.Arrow.Adbc.Tests/DuckDbFixture.cs
+++ b/csharp/test/Apache.Arrow.Adbc.Tests/DuckDbFixture.cs
@@ -50,6 +50,8 @@ namespace Apache.Arrow.Adbc.Tests
_driver = CAdbcDriverImporter.Load(file, "duckdb_adbc_init");
}
+ public int DriverVersion => _driver.DriverVersion;
+
public AdbcDatabase OpenDatabase(string name)
{
if (_driver == null) throw new
ObjectDisposedException("DuckDbFixture");
diff --git a/csharp/test/Apache.Arrow.Adbc.Tests/ImportedDuckDbTests.cs
b/csharp/test/Apache.Arrow.Adbc.Tests/ImportedDuckDbTests.cs
index 57525ed71..ea8a9562f 100644
--- a/csharp/test/Apache.Arrow.Adbc.Tests/ImportedDuckDbTests.cs
+++ b/csharp/test/Apache.Arrow.Adbc.Tests/ImportedDuckDbTests.cs
@@ -279,6 +279,119 @@ namespace Apache.Arrow.Adbc.Tests
Assert.Empty(found);
}
+ [Fact]
+ public void DriverVersionIs11()
+ {
+ Assert.Equal(AdbcVersion.Version_1_1_0, _duckDb.DriverVersion);
+ }
+
+ [Fact]
+ public void ExecuteSchema()
+ {
+ using var database = _duckDb.OpenDatabase("executeschema.db");
+ using var connection = database.Connect(null);
+ using var statement = connection.CreateStatement();
+
+ statement.SqlQuery = "CREATE TABLE test(foo INTEGER, bar VARCHAR,
baz DOUBLE);";
+ statement.ExecuteUpdate();
+
+ statement.SqlQuery = "SELECT foo, bar, baz, foo + 1 AS foo1 FROM
test";
+ Schema schema = statement.ExecuteSchema();
+
+ Assert.Equal(4, schema.FieldsList.Count);
+ Assert.Equal("foo", schema.FieldsList[0].Name);
+ Assert.Equal(ArrowTypeId.Int32,
schema.FieldsList[0].DataType.TypeId);
+ Assert.Equal("bar", schema.FieldsList[1].Name);
+ Assert.Equal(ArrowTypeId.String,
schema.FieldsList[1].DataType.TypeId);
+ Assert.Equal("baz", schema.FieldsList[2].Name);
+ Assert.Equal(ArrowTypeId.Double,
schema.FieldsList[2].DataType.TypeId);
+ Assert.Equal("foo1", schema.FieldsList[3].Name);
+ Assert.Equal(ArrowTypeId.Int32,
schema.FieldsList[3].DataType.TypeId);
+
+ // The schema should match what's actually returned on execution
+ var results = statement.ExecuteQuery();
+ using var stream = results.Stream;
+ Assert.NotNull(stream);
+ Assert.Equal(schema.FieldsList.Count,
stream.Schema.FieldsList.Count);
+ for (int i = 0; i < schema.FieldsList.Count; i++)
+ {
+ Assert.Equal(schema.FieldsList[i].Name,
stream.Schema.FieldsList[i].Name);
+ Assert.Equal(schema.FieldsList[i].DataType.TypeId,
stream.Schema.FieldsList[i].DataType.TypeId);
+ }
+ }
+
+ [Fact]
+ public void ExecuteSchemaDoesNotExecute()
+ {
+ using var database =
_duckDb.OpenDatabase("executeschemanoexec.db");
+ using var connection = database.Connect(null);
+ using var statement = connection.CreateStatement();
+
+ statement.SqlQuery = "CREATE TABLE test(foo INTEGER);";
+ statement.ExecuteUpdate();
+
+ statement.SqlQuery = "INSERT INTO test VALUES (1), (2) RETURNING
foo";
+ Schema schema = statement.ExecuteSchema();
+ Assert.Single(schema.FieldsList);
+ Assert.Equal(ArrowTypeId.Int32,
schema.FieldsList[0].DataType.TypeId);
+
+ Assert.Equal(0, GetResultCount(statement, "SELECT * FROM test"));
+ }
+
+ [Fact]
+ public void CancelIdle()
+ {
+ using var database = _duckDb.OpenDatabase("cancelidle.db");
+ using var connection = database.Connect(null);
+ using var statement = connection.CreateStatement();
+
+ // Cancelling with nothing in progress is a no-op, and doesn't
break later use
+ connection.Cancel();
+ statement.Cancel();
+
+ Assert.Equal(3, GetResultCount(statement, "SELECT * FROM
range(3)"));
+ }
+
+ [Fact]
+ public async Task CancelStatement()
+ {
+ using var database = _duckDb.OpenDatabase("cancelstatement.db");
+ using var connection = database.Connect(null);
+ using var statement = connection.CreateStatement();
+
+ statement.SqlQuery = "SELECT count(*) FROM range(1000000000000)";
+ Task<long> query = Task.Run(() =>
GetResultCount(statement.ExecuteQuery()));
+
+ await Task.Delay(500);
+ statement.Cancel();
+
+ Task completed = await Task.WhenAny(query,
Task.Delay(TimeSpan.FromSeconds(30)));
+ Assert.Same(query, completed);
+ AdbcException exception = await
Assert.ThrowsAnyAsync<AdbcException>(() => query);
+ // DuckDB reports interruption as InvalidArgument rather than
Cancelled
+ Assert.Contains("Interrupted", exception.Message);
+ }
+
+ [Fact]
+ public async Task CancelConnection()
+ {
+ using var database = _duckDb.OpenDatabase("cancelconnection.db");
+ using var connection = database.Connect(null);
+ using var statement = connection.CreateStatement();
+
+ statement.SqlQuery = "SELECT count(*) FROM range(1000000000000)";
+ Task<long> query = Task.Run(() =>
GetResultCount(statement.ExecuteQuery()));
+
+ await Task.Delay(500);
+ connection.Cancel();
+
+ Task completed = await Task.WhenAny(query,
Task.Delay(TimeSpan.FromSeconds(30)));
+ Assert.Same(query, completed);
+ AdbcException exception = await
Assert.ThrowsAnyAsync<AdbcException>(() => query);
+ // DuckDB reports interruption as InvalidArgument rather than
Cancelled
+ Assert.Contains("Interrupted", exception.Message);
+ }
+
private static long GetResultCount(AdbcStatement statement, string
query)
{
statement.SqlQuery = query;