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;

Reply via email to