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 3df4635cae fix(csharp): fall back to the standard driver entrypoint
(#4815)
3df4635cae is described below
commit 3df4635caeeed89fa9c71d136278a0e540316a84
Author: Efe <[email protected]>
AuthorDate: Fri Oct 2 19:48:30 2026 +0300
fix(csharp): fall back to the standard driver entrypoint (#4815)
## Description
When a native driver does not export the filename-derived entrypoint,
the C# driver manager now retries the standard AdbcDriverInit symbol,
matching the behavior of the other driver managers. Explicit
caller-provided entrypoints remain authoritative.
The AOT fixture regression renames a library that exports only
AdbcDriverInit and loads it through AdbcDriverManager, covering the
fallback path. On Windows, the test leaves its copied fixture in the
disposable CI workspace because DriverManager intentionally keeps native
drivers loaded for the process lifetime and Windows cannot delete a
loaded DLL.
Fixes #4630
## Testing
- `git diff --check` — passed
- `dotnet test csharp/test/Apache.Arrow.Adbc.Testing.csproj --no-restore
--filter FullyQualifiedName~TomlConnectionProfileTests --framework
net8.0` — not run locally: dotnet is not installed in the current
environment
- NativeAOT fixture test — passed in CI on Windows, Ubuntu, and macOS
after the Windows cleanup fix
- All current PR checks passed: C# platform builds, NativeAOT interop,
`check PR`, and pre-commit
The AOT fixture test is skipped unless `ADBC_TEST_AOT_FIXTURE_PATH`
points to a published fixture, as in the repository CI workflow.
---------
Signed-off-by: Efe Gökdemir <[email protected]>
Co-authored-by: Efe Gökdemir <[email protected]>
---
.../src/Apache.Arrow.Adbc/C/CAdbcDriverImporter.cs | 25 +++++++-
.../DriverManager/AdbcDriverManager.cs | 33 +++++++++--
.../AotFixtureTests.cs | 69 +++++++++++++++++++++-
3 files changed, 118 insertions(+), 9 deletions(-)
diff --git a/csharp/src/Apache.Arrow.Adbc/C/CAdbcDriverImporter.cs
b/csharp/src/Apache.Arrow.Adbc/C/CAdbcDriverImporter.cs
index 713fbe4d32..b250665e18 100644
--- a/csharp/src/Apache.Arrow.Adbc/C/CAdbcDriverImporter.cs
+++ b/csharp/src/Apache.Arrow.Adbc/C/CAdbcDriverImporter.cs
@@ -50,7 +50,20 @@ namespace Apache.Arrow.Adbc.C
/// <param name="file">The path to the driver to load</param>
/// <param name="canUnload">Whether the driver can be safely
unloaded</param>
/// <param name="entryPoint">The name of the entry point. If not
provided, the name AdbcDriverInit will be used.</param>
- public static AdbcDriver Load(string file, bool canUnload, string?
entryPoint = null)
+ public static AdbcDriver Load(string file, bool canUnload, string?
entryPoint = null) =>
+ Load(file, canUnload, entryPoint, fallbackEntryPoint: null);
+
+ /// <summary>
+ /// Loads a native driver, trying the fallback entry point when the
requested symbol is absent.
+ /// </summary>
+ internal static AdbcDriver LoadWithFallback(string file, string
entryPoint, string fallbackEntryPoint) =>
+ Load(file, canUnload: false, entryPoint, fallbackEntryPoint);
+
+ private static AdbcDriver Load(
+ string file,
+ bool canUnload,
+ string? entryPoint,
+ string? fallbackEntryPoint)
{
if (file == null)
{
@@ -71,7 +84,15 @@ namespace Apache.Arrow.Adbc.C
try
{
entryPoint = entryPoint ?? driverInit;
- IntPtr export = NativeLibrary.GetExport(library, entryPoint);
+ IntPtr export;
+ try
+ {
+ export = NativeLibrary.GetExport(library, entryPoint);
+ }
+ catch (EntryPointNotFoundException) when (fallbackEntryPoint
!= null)
+ {
+ export = NativeLibrary.GetExport(library,
fallbackEntryPoint);
+ }
if (export == IntPtr.Zero)
{
if (canUnload) { NativeLibrary.Free(library); }
diff --git a/csharp/src/Apache.Arrow.Adbc/DriverManager/AdbcDriverManager.cs
b/csharp/src/Apache.Arrow.Adbc/DriverManager/AdbcDriverManager.cs
index 3badc3e457..dfae3f468f 100644
--- a/csharp/src/Apache.Arrow.Adbc/DriverManager/AdbcDriverManager.cs
+++ b/csharp/src/Apache.Arrow.Adbc/DriverManager/AdbcDriverManager.cs
@@ -36,6 +36,8 @@ namespace Apache.Arrow.Adbc.DriverManager
/// </summary>
public static class AdbcDriverManager
{
+ private const string DefaultNativeEntrypoint = "AdbcDriverInit";
+
/// <summary>
/// The environment variable that specifies additional driver search
paths.
/// </summary>
@@ -132,7 +134,12 @@ namespace Apache.Arrow.Adbc.DriverManager
typeName: null,
manifestPath: null,
loadMethod: loadMethod,
- () => CAdbcDriverImporter.Load(driverPath,
resolvedEntrypoint));
+ () => entrypoint == null
+ ? CAdbcDriverImporter.LoadWithFallback(
+ driverPath,
+ resolvedEntrypoint,
+ DefaultNativeEntrypoint)
+ : CAdbcDriverImporter.Load(driverPath,
resolvedEntrypoint));
}
//
-----------------------------------------------------------------------
@@ -641,7 +648,7 @@ namespace Apache.Arrow.Adbc.DriverManager
baseName = baseName.Substring(adbcPrefix.Length);
if (string.IsNullOrEmpty(baseName))
- return "AdbcDriverInit";
+ return DefaultNativeEntrypoint;
// Convert snake_case to PascalCase.
string pascal = ToPascalCase(baseName);
@@ -718,7 +725,10 @@ namespace Apache.Arrow.Adbc.DriverManager
DriverManifest manifest =
DriverManifest.LoadFromFile(manifestPath);
// Caller-supplied entrypoint wins over the manifest's. Falls back
to
- // a derived native symbol name only when neither is provided.
+ // a derived native symbol name only when neither is provided. Keep
+ // that provenance so the standard native fallback is not applied
to
+ // an explicit caller- or manifest-selected symbol.
+ bool allowNativeFallback = entrypoint == null &&
manifest.Entrypoint == null;
string resolvedEntrypoint = entrypoint
?? manifest.Entrypoint
?? DeriveEntrypoint(manifest.LibraryPath);
@@ -726,7 +736,12 @@ namespace Apache.Arrow.Adbc.DriverManager
string? manifestDir =
Path.GetDirectoryName(Path.GetFullPath(manifestPath));
string resolvedPath = ResolveManifestPath(manifest.LibraryPath,
manifestDir);
- return LoadByEntrypointScheme(resolvedPath, resolvedEntrypoint,
manifestPath, nameof(LoadFromManifest));
+ return LoadByEntrypointScheme(
+ resolvedPath,
+ resolvedEntrypoint,
+ manifestPath,
+ nameof(LoadFromManifest),
+ allowNativeFallback);
}
/// <summary>Returns <c>true</c> if <paramref name="entrypoint"/> uses
a managed-runtime scheme prefix.</summary>
@@ -763,7 +778,8 @@ namespace Apache.Arrow.Adbc.DriverManager
string driverPath,
string entrypoint,
string? manifestPath,
- string loadMethod)
+ string loadMethod,
+ bool allowNativeFallback = false)
{
if (entrypoint.StartsWith(DotnetEntrypointScheme,
StringComparison.Ordinal))
{
@@ -794,7 +810,12 @@ namespace Apache.Arrow.Adbc.DriverManager
typeName: null,
manifestPath: manifestPath,
loadMethod: loadMethod,
- () => CAdbcDriverImporter.Load(driverPath, entrypoint));
+ () => allowNativeFallback
+ ? CAdbcDriverImporter.LoadWithFallback(
+ driverPath,
+ entrypoint,
+ DefaultNativeEntrypoint)
+ : CAdbcDriverImporter.Load(driverPath, entrypoint));
}
/// <summary>
diff --git
a/csharp/test/AotInterop/Apache.Arrow.Adbc.TestFixture.Tests/AotFixtureTests.cs
b/csharp/test/AotInterop/Apache.Arrow.Adbc.TestFixture.Tests/AotFixtureTests.cs
index f7042b6b64..ccbc15a477 100644
---
a/csharp/test/AotInterop/Apache.Arrow.Adbc.TestFixture.Tests/AotFixtureTests.cs
+++
b/csharp/test/AotInterop/Apache.Arrow.Adbc.TestFixture.Tests/AotFixtureTests.cs
@@ -50,10 +50,15 @@ namespace Apache.Arrow.Adbc.TestFixture.Tests
private static AdbcDriver LoadFixture()
{
string? path = ResolveFixturePath();
+ SkipIfFixtureUnavailable(path);
+ return CAdbcDriverImporter.Load(path!);
+ }
+
+ private static void SkipIfFixtureUnavailable(string? path)
+ {
Skip.IfNot(
path != null,
$"Set {FixturePathEnvVar} to the AOT-published
Apache.Arrow.Adbc.TestFixture.Native shared library to run this test.");
- return CAdbcDriverImporter.Load(path!);
}
[SkippableFact]
@@ -63,6 +68,68 @@ namespace Apache.Arrow.Adbc.TestFixture.Tests
Assert.Equal(AdbcVersion.Version_1_1_0, driver.DriverVersion);
}
+ [SkippableFact]
+ public void DriverManagerFallsBackToStandardEntrypoint()
+ {
+ string? fixturePath = ResolveFixturePath();
+ SkipIfFixtureUnavailable(fixturePath);
+ string? fixtureParent = Path.GetDirectoryName(fixturePath);
+ Skip.IfNot(!string.IsNullOrEmpty(fixtureParent), "The AOT fixture
path must have a parent directory.");
+ string directory = Path.Combine(fixtureParent!,
Guid.NewGuid().ToString("N"));
+ Directory.CreateDirectory(directory);
+ string renamedPath = Path.Combine(directory, "adbc_driver_fixture"
+ Path.GetExtension(fixturePath));
+ File.Copy(fixturePath!, renamedPath);
+
+ try
+ {
+ using AdbcDriver driver =
Apache.Arrow.Adbc.DriverManager.AdbcDriverManager.LoadDriver(renamedPath);
+ Assert.Equal(AdbcVersion.Version_1_1_0, driver.DriverVersion);
+ }
+ finally
+ {
+ // DriverManager keeps native drivers loaded for the process
lifetime.
+ // Windows therefore prevents deleting the copied DLL while
the test
+ // process is alive; the CI workspace is disposable.
+ if (!OperatingSystem.IsWindows())
+ {
+ Directory.Delete(directory, recursive: true);
+ }
+ }
+ }
+
+ [SkippableFact]
+ public void
FindLoadDriverManifestWithoutEntrypointFallsBackToStandardEntrypoint()
+ {
+ string? fixturePath = ResolveFixturePath();
+ SkipIfFixtureUnavailable(fixturePath);
+ string? fixtureParent = Path.GetDirectoryName(fixturePath);
+ Skip.IfNot(!string.IsNullOrEmpty(fixtureParent), "The AOT fixture
path must have a parent directory.");
+ string directory = Path.Combine(fixtureParent!,
Guid.NewGuid().ToString("N"));
+ Directory.CreateDirectory(directory);
+ string renamedName = "adbc_driver_fixture";
+ string renamedPath = Path.Combine(directory, renamedName +
Path.GetExtension(fixturePath));
+ File.Copy(fixturePath!, renamedPath);
+ File.WriteAllText(
+ Path.Combine(directory, renamedName + ".toml"),
+ "manifest_version = 1\n[Driver]\nshared = \"" +
Path.GetFileName(renamedPath) + "\"\n");
+
+ try
+ {
+ using AdbcDriver driver =
Apache.Arrow.Adbc.DriverManager.AdbcDriverManager.FindLoadDriver(
+ renamedName,
+ loadOptions:
Apache.Arrow.Adbc.DriverManager.AdbcLoadFlags.Default,
+ additionalSearchPathList: directory);
+ Assert.Equal(AdbcVersion.Version_1_1_0, driver.DriverVersion);
+ }
+ finally
+ {
+ if (!OperatingSystem.IsWindows())
+ {
+ Directory.Delete(directory, recursive: true);
+ }
+ }
+ }
+
[SkippableFact]
public async Task ExecuteQueryRoundTripsThroughAotBoundary()
{