From 88e616c9509da46caf8c2e4b7fda8dd19a80c8b5 Mon Sep 17 00:00:00 2001
From: David Li
Date: Thu, 19 Mar 2026 13:47:59 +0900
Subject: [PATCH 1/6] feat(rust/driver_manager): reconcile with C++ driver
manager
Closes #4089.
---
docs/source/format/connection_profiles.rst | 83 +++---
docs/source/format/driver_manifests.rst | 6 +-
docs/source/glossary.rst | 5 +
rust/driver_manager/src/lib.rs | 8 +-
rust/driver_manager/src/profile.rs | 120 ++++++--
rust/driver_manager/src/search.rs | 14 +-
rust/driver_manager/tests/common/mod.rs | 27 ++
.../tests/connection_profile.rs | 280 ++++++++++++------
.../tests/test_env_var_profiles.rs | 45 +--
9 files changed, 389 insertions(+), 199 deletions(-)
diff --git a/docs/source/format/connection_profiles.rst b/docs/source/format/connection_profiles.rst
index 87d8ac142c..795e7c95c4 100644
--- a/docs/source/format/connection_profiles.rst
+++ b/docs/source/format/connection_profiles.rst
@@ -15,30 +15,37 @@
.. specific language governing permissions and limitations
.. under the License.
-==================================
-Driver Manager Connection Profiles
-==================================
+===========================================
+ADBC Driver Manager and Connection Profiles
+===========================================
-Overview
-========
+.. note:: This document describes using the :term:`driver manager` to load
+ drivers. The driver manager is not required to use ADBC in general
+ but it allows loading drivers written in a different language from the
+ application and improves the experience when using multiple drivers in
+ a single application. For more information on how the driver manager
+ works see :doc:`how_manager`.
-There are two ways to pass connection options to driver managers:
+There are two ways to pass driver options through the driver manager:
-1. Directly specifying all connection options as arguments to driver manager functions in your
- application code. (see the `SetOption` family of functions in :doc:`specification` for details)
-2. Referring to a **connection profile** which contains connection options, and optionally overriding
- some options in your application code.
+1. Directly specifying all options as arguments to the driver manager in your
+ application code (see the `SetOption` family of functions in
+ :doc:`specification` for details).
+2. Referring to a :term:`connection profile` which contains options, and
+ optionally overriding some options by setting them through the above
+ method.
-The ADBC driver manager supports **connection profiles** that specify a driver and connection options
-in a reusable configuration. This allows users to:
+Connection profiles combine a driver and driver options in a reusable
+configuration. This allows users to:
- Define connection information in files or environment variables
- Share connection configurations across applications
- Distribute standardized connection settings
- Avoid hardcoding driver names and credentials in application code
-Profiles are loaded during ``AdbcDatabaseInit()`` before initializing the driver. Options
-from the profile are applied automatically but do not override options already set via ``AdbcDatabaseSetOption()``.
+Profiles are loaded during ``AdbcDatabaseInit()`` before initializing the
+driver. Options from the profile are applied automatically but do not override
+options already set via ``AdbcDatabaseSetOption()``.
Quick Start
===========
@@ -74,9 +81,12 @@ Filesystem-based profiles use TOML format with the following structure:
.. code-block:: toml
+ # The version is required.
profile_version = 1
+ # It is optional to provide the driver.
driver = "snowflake"
+ # The Options table is required, even if empty
[Options]
# String options
adbc.snowflake.sql.account = "mycompany"
@@ -111,18 +121,19 @@ driver
The ``driver`` field specifies which ADBC driver to load. This can be:
-- A driver name (e.g., ``"snowflake"``)
+- A driver or driver manifest name (e.g., ``"snowflake"``)
- A path to a shared library (e.g., ``"/usr/local/lib/libadbc_driver_snowflake.so"``)
- A path to a driver manifest (e.g., ``"/etc/adbc/drivers/snowflake.toml"``)
If omitted, the driver must be specified through other means (e.g., the ``driver`` option or ``uri`` parameter).
+If the application specifies a driver, and specifies a profile that itself references a driver, the two must match exactly, or it is an error.
The driver will be loaded identically to if it was specified via ``AdbcDatabaseSetOption("driver", "")``.
For more detils, see :doc:`driver_manifests`.
Options Section
---------------
-The ``[Options]`` section contains driver-specific configuration options. Options can be of the following types:
+The ``[Options]`` section contains driver-specific configuration options. This section must be present, even if empty. Options can be of the following types:
**String values**
Applied using ``AdbcDatabaseSetOption()``
@@ -153,6 +164,10 @@ The ``[Options]`` section contains driver-specific configuration options. Option
adbc.snowflake.sql.client_session_keep_alive = true
+.. warning:: If the application overrides option values but uses a different
+ type for the value than the profile does, it is undefined which
+ will take effect.
+
Value Substitution
------------------
@@ -190,7 +205,7 @@ Profile Search Locations
When using a profile name (not an absolute path), the driver manager searches for ``.toml`` in the following locations:
-1. **Additional Search Paths** (if configured via ``AdbcDriverManagerDatabaseSetAdditionalSearchPathList()``)
+1. **Additional Search Paths** (if configured via ``additional_profile_search_path_list`` option)
2. **ADBC_PROFILE_PATH** environment variable (colon-separated on Unix, semicolon-separated on Windows)
3. **Conda Environment** (if built with Conda support and ``CONDA_PREFIX`` is set):
@@ -561,34 +576,28 @@ Sets a custom connection profile provider. Must be called before ``AdbcDatabaseI
Setting Additional Search Paths
--------------------------------
-.. code-block:: c
-
- AdbcStatusCode AdbcDriverManagerDatabaseSetAdditionalSearchPathList(
- struct AdbcDatabase* database,
- const char* path_list,
- struct AdbcError* error);
-
-Adds additional directories to search for profiles. Must be called before ``AdbcDatabaseInit()``.
-
-**Parameters:**
-
-- ``database``: Database object to configure
-- ``path_list``: OS-specific path separator delimited list (``:``) on Unix, ``;`` on Windows), or ``NULL`` to clear
-- ``error``: Optional error output
-
-**Returns:** ``ADBC_STATUS_OK`` on success, error code otherwise.
+This can be done via the ``additional_profile_search_path_list`` option. It
+must be set before ``AdbcDatabaseInit()``. The value of this option is an
+OS-specific delimited list (``:`` on Unix, ``;`` on Windows), or ``NULL`` to
+clear.
**Example:**
.. code-block:: c
// Unix/Linux/macOS
- AdbcDriverManagerDatabaseSetAdditionalSearchPathList(
- &database, "/opt/app/profiles:/etc/app/profiles", &error);
+ AdbcDatabaseSetOption(
+ &database,
+ "additional_profile_search_path_list",
+ "/opt/app/profiles:/etc/app/profiles",
+ &error);
// Windows
- AdbcDriverManagerDatabaseSetAdditionalSearchPathList(
- &database, "C:\\App\\Profiles;C:\\ProgramData\\App\\Profiles", &error);
+ AdbcDatabaseSetOption(
+ &database,
+ "additional_profile_search_path_list",
+ "C:\\App\\Profiles;C:\\ProgramData\\App\\Profiles",
+ &error);
See Also
diff --git a/docs/source/format/driver_manifests.rst b/docs/source/format/driver_manifests.rst
index 1d4896298e..77753f81ae 100644
--- a/docs/source/format/driver_manifests.rst
+++ b/docs/source/format/driver_manifests.rst
@@ -28,9 +28,9 @@ ADBC Driver Manager and Manifests
There are two ways to load a driver with the driver manager:
-1. Directly specifying the dynamic library to load
-2. Referring to a driver manifest file which contains metadata along with the
- location of the dynamic library to be loaded
+1. Directly specifying the dynamic library to load.
+2. Referring to a :term:`driver manifest` file which contains metadata along
+ with the location of the dynamic library to be loaded.
With either method, you specify the dynamic library or driver manifest as the
``driver`` option to the driver manager or you can use an explicit function for
diff --git a/docs/source/glossary.rst b/docs/source/glossary.rst
index 843493a985..ab87f4c6f4 100644
--- a/docs/source/glossary.rst
+++ b/docs/source/glossary.rst
@@ -34,6 +34,11 @@ Glossary
In ADBC, the connection object/struct represents a single connection to a
database. Multiple connections may be created from one :term:`database`.
+ connection profile
+ A preconfigured driver and options that can be loaded by the
+ :term:`driver manager` for convenience. Specified via a TOML file. See
+ :doc:`format/connection_profiles`.
+
database
In ADBC, the database object/struct holds state that is shared across
connections.
diff --git a/rust/driver_manager/src/lib.rs b/rust/driver_manager/src/lib.rs
index efd8cb91ea..47970de48c 100644
--- a/rust/driver_manager/src/lib.rs
+++ b/rust/driver_manager/src/lib.rs
@@ -494,14 +494,13 @@ impl ManagedDatabase {
additional_search_paths: Option>,
opts: impl IntoIterator- ::Option, OptionValue)>,
) -> Result {
- let profile_provider = FilesystemProfileProvider;
Self::from_uri_with_profile_provider(
uri,
entrypoint,
version,
load_flags,
additional_search_paths,
- profile_provider,
+ FilesystemProfileProvider::default(),
opts,
)
}
@@ -534,7 +533,7 @@ impl ManagedDatabase {
/// use adbc_driver_manager::profile::FilesystemProfileProvider;
/// use adbc_core::LOAD_FLAG_DEFAULT;
///
- /// let provider = FilesystemProfileProvider;
+ /// let provider = FilesystemProfileProvider::default();
/// let opts = vec![(OptionDatabase::Username, OptionValue::String("admin".to_string()))];
///
/// let db = ManagedDatabase::from_uri_with_profile_provider(
@@ -575,8 +574,7 @@ impl ManagedDatabase {
(drv, final_opts)
}
DriverLocator::Profile(profile) => {
- let profile =
- profile_provider.get_profile(profile, additional_search_paths.clone())?;
+ let profile = profile_provider.get_profile(profile)?;
let (driver_name, init_func) = profile.get_driver_name()?;
let drv: ManagedDriver;
diff --git a/rust/driver_manager/src/profile.rs b/rust/driver_manager/src/profile.rs
index 8e93ff19f7..6697383f07 100644
--- a/rust/driver_manager/src/profile.rs
+++ b/rust/driver_manager/src/profile.rs
@@ -76,7 +76,6 @@ pub trait ConnectionProfileProvider {
/// # Arguments
///
/// * `name` - The profile name or path to locate
- /// * `additional_path_list` - Optional additional directories to search for profiles
///
/// # Returns
///
@@ -88,11 +87,7 @@ pub trait ConnectionProfileProvider {
/// - The profile cannot be found
/// - The profile file is malformed
/// - The profile version is unsupported
- fn get_profile(
- &self,
- name: &str,
- additional_path_list: Option>,
- ) -> Result;
+ fn get_profile(&self, name: &str) -> Result;
}
/// Provides connection profiles from TOML files on the filesystem.
@@ -104,7 +99,7 @@ pub trait ConnectionProfileProvider {
/// # Search Order
///
/// Profiles are searched in the following order:
-/// 1. Additional paths provided via `get_profile()`
+/// 1. Additional paths provided via `new_with_search_paths`
/// 2. `ADBC_PROFILE_PATH` environment variable paths
/// 3. User configuration directory (`~/.config/adbc/profiles` on Linux,
/// `~/Library/Application Support/ADBC/Profiles` on macOS,
@@ -117,21 +112,27 @@ pub trait ConnectionProfileProvider {
/// ConnectionProfileProvider, FilesystemProfileProvider
/// };
///
-/// let provider = FilesystemProfileProvider;
-/// let profile = provider.get_profile("my_database", None)?;
+/// let provider = FilesystemProfileProvider::default();
+/// let profile = provider.get_profile("my_database")?;
/// # Ok::<(), adbc_core::error::Error>(())
/// ```
-pub struct FilesystemProfileProvider;
+#[derive(Clone, Default)]
+pub struct FilesystemProfileProvider {
+ additional_paths: Option>,
+}
+
+impl FilesystemProfileProvider {
+ /// Search the given paths (if any) for profiles.
+ pub fn new_with_search_paths(additional_paths: Option>) -> Self {
+ Self { additional_paths }
+ }
+}
impl ConnectionProfileProvider for FilesystemProfileProvider {
type Profile = FilesystemProfile;
- fn get_profile(
- &self,
- name: &str,
- additional_path_list: Option>,
- ) -> Result {
- let profile_path = find_filesystem_profile(name, additional_path_list)?;
+ fn get_profile(&self, name: &str) -> Result {
+ let profile_path = find_filesystem_profile(name, &self.additional_paths)?;
FilesystemProfile::from_path(profile_path)
}
}
@@ -266,14 +267,28 @@ impl FilesystemProfile {
let profile = DeTable::parse(&contents)
.map_err(|e| Error::with_message_and_status(e.to_string(), Status::InvalidArguments))?;
- let profile_version = profile
- .get_ref()
- .get("profile_version")
- .and_then(|v| v.get_ref().as_integer())
- .map(|v| v.as_str())
- .unwrap_or("1");
+ let raw_profile_version = profile.get_ref().get("profile_version").ok_or_else(|| {
+ Error::with_message_and_status(
+ "missing 'profile_version' in profile".to_string(),
+ Status::InvalidArguments,
+ )
+ })?;
+
+ let profile_version = raw_profile_version
+ .as_ref()
+ .as_integer()
+ .and_then(|i| i64::from_str_radix(i.as_str(), i.radix()).ok())
+ .ok_or_else(|| {
+ Error::with_message_and_status(
+ format!(
+ "invalid 'profile_version' in profile: {:?}",
+ raw_profile_version.as_ref()
+ ),
+ Status::InvalidArguments,
+ )
+ })?;
- if profile_version != "1" {
+ if profile_version != 1 {
return Err(Error::with_message_and_status(
format!(
"unsupported profile version '{}', expected '1'",
@@ -287,7 +302,12 @@ impl FilesystemProfile {
.get_ref()
.get("driver")
.and_then(|v| v.get_ref().as_str())
- .unwrap_or("")
+ .ok_or_else(|| {
+ Error::with_message_and_status(
+ "missing or invalid 'driver' field in profile".to_string(),
+ Status::InvalidArguments,
+ )
+ })?
.to_string();
let options_table = profile
@@ -707,6 +727,24 @@ key = "value"
"just a plain string",
Ok("just a plain string"),
),
+ TestCase(
+ "not actually a substitution",
+ vec![],
+ "{{ env_var(NONEXISTENT)",
+ Ok("{{ env_var(NONEXISTENT)"),
+ ),
+ TestCase(
+ "not actually a substitution (2)",
+ vec![],
+ "{{ env_var(NONEXISTENT) }",
+ Ok("{{ env_var(NONEXISTENT) }"),
+ ),
+ TestCase(
+ "not actually a substitution (3)",
+ vec![],
+ "{ env_var(NONEXISTENT) }",
+ Ok("{ env_var(NONEXISTENT) }"),
+ ),
TestCase(
"string with special chars but no templates",
vec![],
@@ -731,6 +769,30 @@ key = "value"
"foo{{ env_var(ADBC_TEST_PPV_NONEXISTENT_XYZ) }}bar",
Ok("foobar"),
),
+ TestCase(
+ "env var not set interpolates the empty string (2)",
+ vec![],
+ "foo{{ env_var(ADBC_TEST_PPV_NONEXISTENT_XYZ) }}",
+ Ok("foo"),
+ ),
+ TestCase(
+ "env var not set interpolates the empty string (3)",
+ vec![],
+ "{{ env_var(ADBC_TEST_PPV_NONEXISTENT_XYZ) }}bar",
+ Ok("bar"),
+ ),
+ TestCase(
+ "env var not set interpolates the empty string (4)",
+ vec![],
+ "foo{{ env_var(ADBC_TEST_PPV_NONEXISTENT_XYZ) }}bar{{ env_var(ADBC_TEST_PPV_NONEXISTENT_XYZ2) }}baz",
+ Ok("foobarbaz"),
+ ),
+ TestCase(
+ "env var not set interpolates the empty string (5)",
+ vec![],
+ "{{ env_var(ADBC_TEST_PPV_NONEXISTENT_XYZ) }}foobarbaz{{ env_var(ADBC_TEST_PPV_NONEXISTENT_XYZ2) }}",
+ Ok("foobarbaz"),
+ ),
TestCase(
"mixed literal text and env var",
vec![("ADBC_TEST_PPV_PORT", "5432")],
@@ -752,6 +814,12 @@ key = "value"
"{{ env_var(ADBC_TEST_PPV_DB) }}",
Ok("mydb"),
),
+ TestCase(
+ "no whitespace inside braces",
+ vec![("ADBC_TEST_PPV_DB", "mydb")],
+ "{{env_var(ADBC_TEST_PPV_DB)}}",
+ Ok("mydb"),
+ ),
TestCase(
"invalid expression not env_var",
vec![],
@@ -859,11 +927,11 @@ test_key = "test_value"
});
std::fs::write(&profile_path, profile_content).unwrap();
- let provider = FilesystemProfileProvider;
let search_paths = search_paths_opt.map(|mut paths| {
paths.push(tmp_dir.path().to_path_buf());
paths
});
+ let provider = FilesystemProfileProvider::new_with_search_paths(search_paths);
let profile_arg = if name.contains("absolute") {
profile_path.to_str().unwrap().to_string()
@@ -871,7 +939,7 @@ test_key = "test_value"
profile_name.to_string()
};
- let result = provider.get_profile(&profile_arg, search_paths);
+ let result = provider.get_profile(&profile_arg);
if should_succeed {
let profile =
diff --git a/rust/driver_manager/src/search.rs b/rust/driver_manager/src/search.rs
index 904daabb04..2fa9f29fd9 100644
--- a/rust/driver_manager/src/search.rs
+++ b/rust/driver_manager/src/search.rs
@@ -845,7 +845,7 @@ fn get_search_paths(lvls: LoadFlags) -> Vec {
/// Returns `Status::NotFound` if the profile cannot be located in any search path.
pub(crate) fn find_filesystem_profile(
name: &str,
- additional_path_list: Option>,
+ additional_path_list: &Option>,
) -> Result {
// Convert the name to a PathBuf to ensure proper platform-specific path handling.
// This normalizes forward slashes to backslashes on Windows.
@@ -905,8 +905,8 @@ pub(crate) fn find_filesystem_profile(
/// # Returns
///
/// A vector of paths to search for profiles, in priority order.
-fn get_profile_search_paths(additional_path_list: Option>) -> Vec {
- let mut result = additional_path_list.unwrap_or_default();
+fn get_profile_search_paths(additional_path_list: &Option>) -> Vec {
+ let mut result = additional_path_list.clone().unwrap_or_default();
// Add ADBC_PROFILE_PATH environment variable paths
if let Some(paths) = env::var_os("ADBC_PROFILE_PATH") {
@@ -1741,7 +1741,7 @@ mod tests {
profile_name.to_string()
};
- let result = find_filesystem_profile(&profile_arg, search_paths);
+ let result = find_filesystem_profile(&profile_arg, &search_paths);
if should_succeed {
assert!(
@@ -1793,7 +1793,7 @@ mod tests {
let result = find_filesystem_profile(
"searched_profile",
- Some(vec![
+ &Some(vec![
tmp_dir1.path().to_path_buf(),
tmp_dir2.path().to_path_buf(),
]),
@@ -1813,7 +1813,7 @@ mod tests {
.tempdir()
.unwrap();
- let paths = get_profile_search_paths(Some(vec![tmp_dir.path().to_path_buf()]));
+ let paths = get_profile_search_paths(&Some(vec![tmp_dir.path().to_path_buf()]));
assert!(paths.contains(&tmp_dir.path().to_path_buf()));
assert!(!paths.is_empty());
@@ -1823,7 +1823,7 @@ mod tests {
#[test]
fn test_get_profile_search_paths_empty() {
- let paths = get_profile_search_paths(None);
+ let paths = get_profile_search_paths(&None);
// Should still return some paths (env vars, user config, etc.)
assert!(!paths.is_empty() || paths.is_empty()); // Just verify it doesn't panic
}
diff --git a/rust/driver_manager/tests/common/mod.rs b/rust/driver_manager/tests/common/mod.rs
index 28df83f104..408398c0ae 100644
--- a/rust/driver_manager/tests/common/mod.rs
+++ b/rust/driver_manager/tests/common/mod.rs
@@ -16,6 +16,7 @@
// under the License.
use std::collections::HashSet;
+use std::ffi::{OsStr, OsString};
use std::ops::Deref;
use std::sync::Arc;
@@ -340,3 +341,29 @@ pub fn test_ingestion_roundtrip(connection: &mut ManagedConnection) {
connection.rollback().unwrap();
}
+
+pub struct SetEnv {
+ env_var: &'static str,
+ original_value: Option,
+}
+
+impl SetEnv {
+ pub fn new(env_var: &'static str, new_value: impl AsRef) -> Self {
+ let original_value = std::env::var_os(env_var);
+ std::env::set_var(env_var, new_value);
+ Self {
+ env_var,
+ original_value,
+ }
+ }
+}
+
+impl Drop for SetEnv {
+ fn drop(&mut self) {
+ if let Some(original_value) = &self.original_value {
+ std::env::set_var(self.env_var, original_value);
+ } else {
+ std::env::remove_var(self.env_var);
+ }
+ }
+}
diff --git a/rust/driver_manager/tests/connection_profile.rs b/rust/driver_manager/tests/connection_profile.rs
index 8a0eae3aec..f0797fc889 100644
--- a/rust/driver_manager/tests/connection_profile.rs
+++ b/rust/driver_manager/tests/connection_profile.rs
@@ -126,12 +126,10 @@ uri = ":memory:"
fn test_filesystem_profile_load_simple() {
let (tmp_dir, profile_path) = write_profile_to_tempfile("simple", &simple_profile());
- let provider = FilesystemProfileProvider;
+ let search_paths = Some(vec![tmp_dir.path().to_path_buf()]);
+ let provider = FilesystemProfileProvider::new_with_search_paths(search_paths);
let profile = provider
- .get_profile(
- profile_path.to_str().unwrap(),
- Some(vec![tmp_dir.path().to_path_buf()]),
- )
+ .get_profile(profile_path.to_str().unwrap())
.unwrap();
let (driver_name, init_func) = profile.get_driver_name().unwrap();
@@ -158,12 +156,10 @@ fn test_filesystem_profile_nested_options() {
let (tmp_dir, profile_path) =
write_profile_to_tempfile("nested", &profile_with_nested_options());
- let provider = FilesystemProfileProvider;
+ let search_paths = Some(vec![tmp_dir.path().to_path_buf()]);
+ let provider = FilesystemProfileProvider::new_with_search_paths(search_paths);
let profile = provider
- .get_profile(
- profile_path.to_str().unwrap(),
- Some(vec![tmp_dir.path().to_path_buf()]),
- )
+ .get_profile(profile_path.to_str().unwrap())
.unwrap();
let options: Vec<_> = profile.get_options().unwrap().into_iter().collect();
@@ -194,12 +190,10 @@ fn test_filesystem_profile_nested_options() {
fn test_filesystem_profile_all_option_types() {
let (tmp_dir, profile_path) = write_profile_to_tempfile("all_types", &profile_with_all_types());
- let provider = FilesystemProfileProvider;
+ let provider =
+ FilesystemProfileProvider::new_with_search_paths(Some(vec![tmp_dir.path().to_path_buf()]));
let profile = provider
- .get_profile(
- profile_path.to_str().unwrap(),
- Some(vec![tmp_dir.path().to_path_buf()]),
- )
+ .get_profile(profile_path.to_str().unwrap())
.unwrap();
let options: Vec<_> = profile.get_options().unwrap().into_iter().collect();
@@ -251,22 +245,85 @@ fn test_filesystem_profile_error_cases() {
Status::InvalidArguments,
"unsupported profile version",
),
+ (
+ "no version",
+ r#"
+driver = "adbc_driver_sqlite"
+[Options]
+"#
+ .to_string(),
+ Status::InvalidArguments,
+ "missing 'profile_version' in profile",
+ ),
+ (
+ "bad version",
+ r#"
+profile_version = "1"
+driver = "adbc_driver_sqlite"
+[Options]
+"#
+ .to_string(),
+ Status::InvalidArguments,
+ "invalid 'profile_version' in profile",
+ ),
(
"invalid toml",
invalid_toml().to_string(),
Status::InvalidArguments,
- "",
+ "TOML parse error",
+ ),
+ (
+ "no driver",
+ r#"
+profile_version = 1
+[Options]
+"#
+ .to_string(),
+ Status::InvalidArguments,
+ "missing or invalid 'driver' field in profile",
+ ),
+ (
+ "numeric driver",
+ r#"
+profile_version = 1
+driver = 2
+[Options]
+"#
+ .to_string(),
+ Status::InvalidArguments,
+ "missing or invalid 'driver' field in profile",
+ ),
+ (
+ "table driver",
+ r#"
+profile_version = 1
+[driver]
+foo = "bar"
+[Options]
+"#
+ .to_string(),
+ Status::InvalidArguments,
+ "missing or invalid 'driver' field in profile",
+ ),
+ (
+ "no options",
+ r#"
+profile_version = 1
+driver = "foo"
+"#
+ .to_string(),
+ Status::InvalidArguments,
+ "missing or invalid 'Options' table in profile",
),
];
for (name, profile_content, expected_status, expected_msg_fragment) in test_cases {
let (tmp_dir, profile_path) = write_profile_to_tempfile(name, &profile_content);
- let provider = FilesystemProfileProvider;
- let result = provider.get_profile(
- profile_path.to_str().unwrap(),
- Some(vec![tmp_dir.path().to_path_buf()]),
- );
+ let provider = FilesystemProfileProvider::new_with_search_paths(Some(vec![tmp_dir
+ .path()
+ .to_path_buf()]));
+ let result = provider.get_profile(profile_path.to_str().unwrap());
assert!(result.is_err(), "Test case '{}': expected error", name);
let err = result.unwrap_err();
@@ -293,8 +350,8 @@ fn test_filesystem_profile_error_cases() {
#[test]
fn test_filesystem_profile_not_found() {
- let provider = FilesystemProfileProvider;
- let result = provider.get_profile("nonexistent_profile", None);
+ let provider = FilesystemProfileProvider::default();
+ let result = provider.get_profile("nonexistent_profile");
assert!(result.is_err());
let err = result.unwrap_err();
@@ -302,27 +359,6 @@ fn test_filesystem_profile_not_found() {
assert!(err.message.contains("Profile not found"));
}
-#[test]
-fn test_filesystem_profile_without_driver() {
- let (tmp_dir, profile_path) = write_profile_to_tempfile("no_driver", &profile_without_driver());
-
- let provider = FilesystemProfileProvider;
- let profile = provider
- .get_profile(
- profile_path.to_str().unwrap(),
- Some(vec![tmp_dir.path().to_path_buf()]),
- )
- .unwrap();
-
- let (driver_name, _) = profile.get_driver_name().unwrap();
- // Should get empty string for missing driver
- assert_eq!(driver_name, "");
-
- tmp_dir
- .close()
- .expect("Failed to close/remove temporary directory");
-}
-
#[test]
#[cfg_attr(not(feature = "driver_manager_test_lib"), ignore)]
fn test_database_from_uri_with_profile() {
@@ -385,12 +421,12 @@ fn test_profile_loading_scenarios() {
for (name, profile_name, profile_content, use_search_path, use_absolute) in test_cases {
let (tmp_dir, profile_path) = write_profile_to_tempfile(profile_name, &profile_content);
- let provider = FilesystemProfileProvider;
let search_paths = if use_search_path {
Some(vec![tmp_dir.path().to_path_buf()])
} else {
None
};
+ let provider = FilesystemProfileProvider::new_with_search_paths(search_paths);
let profile_arg = if use_absolute {
profile_path.to_str().unwrap()
@@ -399,7 +435,7 @@ fn test_profile_loading_scenarios() {
};
let profile = provider
- .get_profile(profile_arg, search_paths)
+ .get_profile(profile_arg)
.unwrap_or_else(|e| panic!("Test case '{}' failed: {:?}", name, e));
let (driver_name, _) = profile.get_driver_name().unwrap();
@@ -419,9 +455,9 @@ fn test_profile_loading_scenarios() {
fn test_profile_display() {
let (tmp_dir, profile_path) = write_profile_to_tempfile("display", &simple_profile());
- let provider = FilesystemProfileProvider;
+ let provider = FilesystemProfileProvider::default();
let profile = provider
- .get_profile(profile_path.to_str().unwrap(), None)
+ .get_profile(profile_path.to_str().unwrap())
.unwrap();
let display_str = format!("{}", profile);
@@ -460,8 +496,7 @@ fn test_profile_hierarchical_path_via_env_var() {
);
// Set ADBC_PROFILE_PATH to the parent directory
- let prev_value = env::var_os("ADBC_PROFILE_PATH");
- env::set_var("ADBC_PROFILE_PATH", tmp_dir.path());
+ let _guard = common::SetEnv::new("ADBC_PROFILE_PATH", tmp_dir.path());
// Verify the environment variable is set correctly
assert_eq!(
@@ -470,14 +505,8 @@ fn test_profile_hierarchical_path_via_env_var() {
);
// Try to load the profile using hierarchical relative path
- let provider = FilesystemProfileProvider;
- let result = provider.get_profile("databases/postgres/production", None);
-
- // Restore the original environment variable
- match prev_value {
- Some(val) => env::set_var("ADBC_PROFILE_PATH", val),
- None => env::remove_var("ADBC_PROFILE_PATH"),
- }
+ let provider = FilesystemProfileProvider::default();
+ let result = provider.get_profile("databases/postgres/production");
// Verify the profile was loaded successfully
let profile = result.expect("Failed to load profile from hierarchical path");
@@ -501,8 +530,6 @@ fn test_profile_hierarchical_path_via_env_var() {
#[test]
#[serial]
fn test_profile_hierarchical_path_with_extension_via_env_var() {
- use std::env;
-
let tmp_dir = tempfile::Builder::new()
.prefix("adbc_profile_env_test2")
.tempdir()
@@ -518,18 +545,11 @@ fn test_profile_hierarchical_path_with_extension_via_env_var() {
std::fs::write(&profile_path, simple_profile()).expect("Failed to write profile");
// Set ADBC_PROFILE_PATH to the parent directory
- let prev_value = env::var_os("ADBC_PROFILE_PATH");
- env::set_var("ADBC_PROFILE_PATH", tmp_dir.path());
+ let _guard = common::SetEnv::new("ADBC_PROFILE_PATH", tmp_dir.path());
// Try to load the profile using hierarchical relative path with .toml extension
- let provider = FilesystemProfileProvider;
- let result = provider.get_profile("configs/dev/database.toml", None);
-
- // Restore the original environment variable
- match prev_value {
- Some(val) => env::set_var("ADBC_PROFILE_PATH", val),
- None => env::remove_var("ADBC_PROFILE_PATH"),
- }
+ let provider = FilesystemProfileProvider::default();
+ let result = provider.get_profile("configs/dev/database.toml");
// Verify the profile was loaded successfully
let profile = result.expect("Failed to load profile from hierarchical path with extension");
@@ -558,11 +578,9 @@ fn test_profile_hierarchical_path_additional_search_paths() {
std::fs::write(&profile_path, simple_profile()).expect("Failed to write profile");
// Load profile using hierarchical path via additional_search_paths
- let provider = FilesystemProfileProvider;
- let result = provider.get_profile(
- "projects/myapp/local",
- Some(vec![tmp_dir.path().to_path_buf()]),
- );
+ let provider =
+ FilesystemProfileProvider::new_with_search_paths(Some(vec![tmp_dir.path().to_path_buf()]));
+ let result = provider.get_profile("projects/myapp/local");
// Verify the profile was loaded successfully
let profile = result.expect("Failed to load profile from hierarchical path");
@@ -606,18 +624,11 @@ fn test_profile_conda_prefix() {
std::fs::write(&filepath, simple_profile()).expect("Failed to write profile");
// Set CONDA_PREFIX environment variable
- let prev_value = env::var("CONDA_PREFIX").ok();
- env::set_var("CONDA_PREFIX", tmp_dir.path());
+ let _guard = common::SetEnv::new("CONDA_PREFIX", tmp_dir.path());
let uri = "profile://sqlite-profile";
let result = ManagedDatabase::from_uri(uri, None, AdbcVersion::V100, LOAD_FLAG_DEFAULT, None);
- // Restore environment variable
- match prev_value {
- Some(val) => env::set_var("CONDA_PREFIX", val),
- None => env::remove_var("CONDA_PREFIX"),
- }
-
if is_conda_build {
assert!(result.is_ok(), "Expected success for conda build");
} else {
@@ -635,3 +646,106 @@ fn test_profile_conda_prefix() {
.close()
.expect("Failed to close/remove temporary directory")
}
+
+#[test]
+#[cfg_attr(not(feature = "driver_manager_test_lib"), ignore)]
+fn test_profile_load_manifest() {
+ let driver_path = PathBuf::from(
+ env::var_os("ADBC_DRIVER_MANAGER_TEST_LIB")
+ .expect("ADBC_DRIVER_MANAGER_TEST_LIB must be set for driver manager manifest tests"),
+ )
+ .to_string_lossy()
+ .to_string();
+ let manifest_dir = tempfile::Builder::new()
+ .prefix("adbc-test-manifest")
+ .tempdir()
+ .unwrap();
+ let profile_dir = tempfile::Builder::new()
+ .prefix("adbc-test-profile")
+ .tempdir()
+ .unwrap();
+
+ let manifest_contents = format!(
+ r#"
+manifest_version = 1
+[Driver]
+shared = "{driver_path}"
+"#
+ );
+
+ let manifest_path = manifest_dir.path().join("sqlite.toml");
+ std::fs::write(&manifest_path, &manifest_contents).unwrap();
+
+ let manifest_path = profile_dir.path().join("sqlitemani.toml");
+ std::fs::write(&manifest_path, &manifest_contents).unwrap();
+
+ let profile_contents = r#"
+profile_version = 1
+driver = "sqlite"
+[Options]
+uri = ":memory:"
+"#;
+
+ let profile_path = profile_dir.path().join("sqlitedev.toml");
+ std::fs::write(&profile_path, profile_contents).unwrap();
+ let profile_path = manifest_dir.path().join("sqliteprof.toml");
+ std::fs::write(&profile_path, profile_contents).unwrap();
+
+ let provider = FilesystemProfileProvider::new_with_search_paths(Some(vec![profile_dir
+ .path()
+ .to_path_buf()]));
+ let database = ManagedDatabase::from_uri_with_profile_provider(
+ "profile://sqlitedev",
+ None,
+ AdbcVersion::V100,
+ LOAD_FLAG_DEFAULT,
+ Some(vec![manifest_dir.path().to_path_buf()]),
+ provider.clone(),
+ std::iter::empty(),
+ )
+ .unwrap();
+
+ common::test_database(&database);
+
+ // should not be able to load a profile from manifest dir or vice versa
+ let result = ManagedDatabase::from_uri_with_profile_provider(
+ "profile://sqliteprof",
+ None,
+ AdbcVersion::V100,
+ LOAD_FLAG_DEFAULT,
+ Some(vec![manifest_dir.path().to_path_buf()]),
+ provider.clone(),
+ std::iter::empty(),
+ );
+ assert!(result.is_err());
+ let err = result.err().unwrap();
+ assert!(err.message.contains("Profile not found: sqliteprof"));
+
+ let result = ManagedDatabase::from_uri_with_profile_provider(
+ "sqlitemani://",
+ None,
+ AdbcVersion::V100,
+ LOAD_FLAG_DEFAULT,
+ Some(vec![manifest_dir.path().to_path_buf()]),
+ provider.clone(),
+ std::iter::empty(),
+ );
+ assert!(result.is_err());
+ let err = result.err().unwrap();
+ assert!(err.message.contains("Driver not found: sqlitemani"));
+
+ // but of course loading manifest from manifest dir is OK
+ let result = ManagedDatabase::from_uri_with_profile_provider(
+ "sqlite://",
+ None,
+ AdbcVersion::V100,
+ LOAD_FLAG_DEFAULT,
+ Some(vec![manifest_dir.path().to_path_buf()]),
+ provider.clone(),
+ std::iter::empty(),
+ );
+ assert!(result.is_ok());
+
+ manifest_dir.close().unwrap();
+ profile_dir.close().unwrap();
+}
diff --git a/rust/driver_manager/tests/test_env_var_profiles.rs b/rust/driver_manager/tests/test_env_var_profiles.rs
index bd35d3453f..d04610267b 100644
--- a/rust/driver_manager/tests/test_env_var_profiles.rs
+++ b/rust/driver_manager/tests/test_env_var_profiles.rs
@@ -18,6 +18,8 @@
use std::env;
use std::path::PathBuf;
+mod common;
+
use adbc_core::options::AdbcVersion;
use adbc_core::{error::Status, LOAD_FLAG_DEFAULT};
use adbc_driver_manager::ManagedDatabase;
@@ -37,8 +39,7 @@ fn test_env_var_replacement_basic() {
.expect("Failed to create temporary directory");
// Set a test environment variable
- let prev_value = env::var_os("ADBC_TEST_ENV_VAR");
- env::set_var("ADBC_TEST_ENV_VAR", ":memory:");
+ let _guard = common::SetEnv::new("ADBC_TEST_ENV_VAR", ":memory:");
let profile_content = r#"
profile_version = 1
@@ -53,12 +54,6 @@ uri = "{{ env_var(ADBC_TEST_ENV_VAR) }}"
let result = ManagedDatabase::from_uri(&uri, None, AdbcVersion::V100, LOAD_FLAG_DEFAULT, None);
- // Restore environment variable
- match prev_value {
- Some(val) => env::set_var("ADBC_TEST_ENV_VAR", val),
- None => env::remove_var("ADBC_TEST_ENV_VAR"),
- }
-
match result {
Ok(_db) => {
// Successfully created database with env_var replacement
@@ -198,8 +193,7 @@ fn test_env_var_replacement_interpolation() {
.expect("Failed to create temporary directory");
// Set a test environment variable
- let prev_value = env::var_os("ADBC_TEST_INTERPOLATE");
- env::set_var("ADBC_TEST_INTERPOLATE", "middle_value");
+ let _guard = common::SetEnv::new("ADBC_TEST_INTERPOLATE", "middle_value");
let profile_content = r#"
profile_version = 1
@@ -215,12 +209,6 @@ test_option = "prefix_{{ env_var(ADBC_TEST_INTERPOLATE) }}_suffix"
let result = ManagedDatabase::from_uri(&uri, None, AdbcVersion::V100, LOAD_FLAG_DEFAULT, None);
- // Restore environment variable
- match prev_value {
- Some(val) => env::set_var("ADBC_TEST_INTERPOLATE", val),
- None => env::remove_var("ADBC_TEST_INTERPOLATE"),
- }
-
assert!(result.is_err(), "Expected error for malformed env_var");
if let Err(err) = result {
assert_eq!(
@@ -243,10 +231,8 @@ fn test_env_var_replacement_multiple() {
.expect("Failed to create temporary directory");
// Set test environment variables
- let prev_var1 = env::var_os("ADBC_TEST_VAR1");
- let prev_var2 = env::var_os("ADBC_TEST_VAR2");
- env::set_var("ADBC_TEST_VAR1", "first");
- env::set_var("ADBC_TEST_VAR2", "second");
+ let _guard1 = common::SetEnv::new("ADBC_TEST_VAR1", "first");
+ let _guard2 = common::SetEnv::new("ADBC_TEST_VAR2", "second");
let profile_content = r#"
profile_version = 1
@@ -262,16 +248,6 @@ test_option = "{{ env_var(ADBC_TEST_VAR1) }}_and_{{ env_var(ADBC_TEST_VAR2) }}"
let result = ManagedDatabase::from_uri(&uri, None, AdbcVersion::V100, LOAD_FLAG_DEFAULT, None);
- // Restore environment variables
- match prev_var1 {
- Some(val) => env::set_var("ADBC_TEST_VAR1", val),
- None => env::remove_var("ADBC_TEST_VAR1"),
- }
- match prev_var2 {
- Some(val) => env::set_var("ADBC_TEST_VAR2", val),
- None => env::remove_var("ADBC_TEST_VAR2"),
- }
-
assert!(result.is_err(), "Expected error for malformed env_var");
if let Err(err) = result {
assert_eq!(
@@ -294,8 +270,7 @@ fn test_env_var_replacement_whitespace() {
.expect("Failed to create temporary directory");
// Set a test environment variable
- let prev_value = env::var_os("ADBC_TEST_WHITESPACE");
- env::set_var("ADBC_TEST_WHITESPACE", "value");
+ let _guard = common::SetEnv::new("ADBC_TEST_WHITESPACE", "value");
let profile_content = r#"
profile_version = 1
@@ -311,12 +286,6 @@ test_option = "{{ env_var( ADBC_TEST_WHITESPACE ) }}"
let result = ManagedDatabase::from_uri(&uri, None, AdbcVersion::V100, LOAD_FLAG_DEFAULT, None);
- // Restore environment variable
- match prev_value {
- Some(val) => env::set_var("ADBC_TEST_WHITESPACE", val),
- None => env::remove_var("ADBC_TEST_WHITESPACE"),
- }
-
assert!(result.is_err(), "Expected error for malformed env_var");
if let Err(err) = result {
assert_eq!(
From 0a709674ce2bc91840f6e825a83d3fb2e9b3c1d1 Mon Sep 17 00:00:00 2001
From: David Li
Date: Thu, 19 Mar 2026 14:56:09 +0900
Subject: [PATCH 2/6] update js
---
javascript/__test__/profile.spec.ts | 2 +-
javascript/binding.d.ts | 3 ++-
javascript/lib/types.ts | 11 ++++++++---
javascript/src/client.rs | 21 +++++++++++++++------
javascript/src/lib.rs | 6 ++++--
5 files changed, 30 insertions(+), 13 deletions(-)
diff --git a/javascript/__test__/profile.spec.ts b/javascript/__test__/profile.spec.ts
index 4f346f3c26..3fcab571e9 100644
--- a/javascript/__test__/profile.spec.ts
+++ b/javascript/__test__/profile.spec.ts
@@ -47,7 +47,7 @@ test('profile: load database from profile:// URI', async () => {
const db = new AdbcDatabase({
driver: 'profile://test_sqlite',
- searchPaths: [tmpDir],
+ profileSearchPaths: [tmpDir],
})
const conn = await db.connect()
diff --git a/javascript/binding.d.ts b/javascript/binding.d.ts
index c771d3ea75..c9fb966ff7 100644
--- a/javascript/binding.d.ts
+++ b/javascript/binding.d.ts
@@ -39,7 +39,8 @@ export type _NativeAdbcStatement = NativeAdbcStatement
export interface ConnectOptions {
driver: string
entrypoint?: string
- searchPaths?: Array
+ manifestSearchPaths?: Array
+ profileSearchPaths?: Array
loadFlags?: number
databaseOptions?: Record
}
diff --git a/javascript/lib/types.ts b/javascript/lib/types.ts
index 59fb7487e2..7d3dd6bbd4 100644
--- a/javascript/lib/types.ts
+++ b/javascript/lib/types.ts
@@ -118,7 +118,7 @@ export interface ConnectOptions {
* - URI-style string: `"sqlite:file::memory:"`, `"postgresql://user:pass@host/db"` — the
* driver name is the URI scheme and the remainder is passed as the connection URI.
* - Connection profile URI: `"profile://my_profile"` — loads a named profile from a
- * `.toml` file found in {@link searchPaths} or the default search directories.
+ * `.toml` file found in {@link profileSearchPaths} or the default search directories.
*/
driver: string
/**
@@ -127,10 +127,15 @@ export interface ConnectOptions {
*/
entrypoint?: string
/**
- * Additional directories to search for drivers and driver manifest (`.toml`) profile files (optional).
+ * Additional directories to search for drivers and driver manifest (`.toml`) files (optional).
* Searched before the default system and user configuration directories.
*/
- searchPaths?: string[]
+ manifestSearchPaths?: string[]
+ /**
+ * Additional directories to search for connection profile (`.toml`) files (optional).
+ * Searched before the default system and user configuration directories.
+ */
+ profileSearchPaths?: string[]
/**
* Bitmask controlling how the driver name is resolved (optional).
* Use the {@link LoadFlags} constants to compose a value.
diff --git a/javascript/src/client.rs b/javascript/src/client.rs
index b6bc9ac1e4..3cbcd39654 100644
--- a/javascript/src/client.rs
+++ b/javascript/src/client.rs
@@ -46,7 +46,8 @@ pub type Result = std::result::Result;
pub struct ConnectOptions {
pub driver: String,
pub entrypoint: Option,
- pub search_paths: Option>,
+ pub manifest_search_paths: Option>,
+ pub profile_search_paths: Option>,
pub load_flags: Option,
pub database_options: Option>,
}
@@ -76,20 +77,28 @@ impl AdbcDatabaseCore {
let load_flags = opts.load_flags.unwrap_or(LOAD_FLAG_DEFAULT);
let entrypoint = opts.entrypoint.as_ref().map(|s| s.as_bytes().to_vec());
- let search_paths: Option> = opts
- .search_paths
+ let manifest_search_paths: Option> = opts
+ .manifest_search_paths
+ .map(|paths| paths.into_iter().map(PathBuf::from).collect());
+
+ let profile_search_paths: Option> = opts
+ .profile_search_paths
.map(|paths| paths.into_iter().map(PathBuf::from).collect());
let database_opts = opts.database_options.map(map_database_options);
let database = if opts.driver.contains(':') {
+ let provider = adbc_driver_manager::profile::FilesystemProfileProvider::new_with_search_paths(
+ profile_search_paths,
+ );
// URI-style ("sqlite:file::memory:") or profile URI ("profile://my_profile")
- ManagedDatabase::from_uri_with_opts(
+ ManagedDatabase::from_uri_with_profile_provider(
&opts.driver,
entrypoint.as_deref(),
version,
load_flags,
- search_paths,
+ manifest_search_paths,
+ provider,
database_opts.into_iter().flatten(),
)?
} else {
@@ -99,7 +108,7 @@ impl AdbcDatabaseCore {
entrypoint.as_deref(),
version,
load_flags,
- search_paths,
+ manifest_search_paths,
)?;
match database_opts {
Some(db_opts) => driver.new_database_with_opts(db_opts)?,
diff --git a/javascript/src/lib.rs b/javascript/src/lib.rs
index 94d5bc7a87..5e9b486703 100644
--- a/javascript/src/lib.rs
+++ b/javascript/src/lib.rs
@@ -144,7 +144,8 @@ pub fn default_load_flags() -> u32 {
pub struct ConnectOptions {
pub driver: String,
pub entrypoint: Option,
- pub search_paths: Option>,
+ pub manifest_search_paths: Option>,
+ pub profile_search_paths: Option>,
pub load_flags: Option,
pub database_options: Option>,
}
@@ -154,7 +155,8 @@ impl From for CoreConnectOptions {
Self {
driver: opts.driver,
entrypoint: opts.entrypoint,
- search_paths: opts.search_paths,
+ manifest_search_paths: opts.manifest_search_paths,
+ profile_search_paths: opts.profile_search_paths,
load_flags: opts.load_flags,
database_options: opts.database_options,
}
From 811546204bfc72dc3c0c06359afedc3cc71bc5f5 Mon Sep 17 00:00:00 2001
From: David Li
Date: Thu, 19 Mar 2026 15:38:52 +0900
Subject: [PATCH 3/6] windows
---
rust/driver_manager/tests/connection_profile.rs | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/rust/driver_manager/tests/connection_profile.rs b/rust/driver_manager/tests/connection_profile.rs
index f0797fc889..73df67a1ef 100644
--- a/rust/driver_manager/tests/connection_profile.rs
+++ b/rust/driver_manager/tests/connection_profile.rs
@@ -655,7 +655,8 @@ fn test_profile_load_manifest() {
.expect("ADBC_DRIVER_MANAGER_TEST_LIB must be set for driver manager manifest tests"),
)
.to_string_lossy()
- .to_string();
+ .to_string()
+ .replace("\\", "\\\\");
let manifest_dir = tempfile::Builder::new()
.prefix("adbc-test-manifest")
.tempdir()
From dc11d7071cbd32b3678bc12d1e081e1ca8e8f6be Mon Sep 17 00:00:00 2001
From: David Li
Date: Thu, 19 Mar 2026 15:58:51 +0900
Subject: [PATCH 4/6] windows
---
rust/driver_manager/tests/connection_profile.rs | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/rust/driver_manager/tests/connection_profile.rs b/rust/driver_manager/tests/connection_profile.rs
index 73df67a1ef..e732ee9370 100644
--- a/rust/driver_manager/tests/connection_profile.rs
+++ b/rust/driver_manager/tests/connection_profile.rs
@@ -720,7 +720,11 @@ uri = ":memory:"
);
assert!(result.is_err());
let err = result.err().unwrap();
- assert!(err.message.contains("Profile not found: sqliteprof"));
+ assert!(
+ err.message.contains("Profile not found: sqliteprof"),
+ "{}",
+ err.message
+ );
let result = ManagedDatabase::from_uri_with_profile_provider(
"sqlitemani://",
@@ -733,7 +737,7 @@ uri = ":memory:"
);
assert!(result.is_err());
let err = result.err().unwrap();
- assert!(err.message.contains("Driver not found: sqlitemani"));
+ assert!(err.message.contains("sqlitemani"), "{}", err.message);
// but of course loading manifest from manifest dir is OK
let result = ManagedDatabase::from_uri_with_profile_provider(
From 82633ab294929906879434ac841ed04efe81a001 Mon Sep 17 00:00:00 2001
From: David Li
Date: Thu, 19 Mar 2026 16:13:35 +0900
Subject: [PATCH 5/6] windows
---
rust/driver_manager/tests/connection_profile.rs | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/rust/driver_manager/tests/connection_profile.rs b/rust/driver_manager/tests/connection_profile.rs
index e732ee9370..845a6d993a 100644
--- a/rust/driver_manager/tests/connection_profile.rs
+++ b/rust/driver_manager/tests/connection_profile.rs
@@ -736,8 +736,12 @@ uri = ":memory:"
std::iter::empty(),
);
assert!(result.is_err());
- let err = result.err().unwrap();
- assert!(err.message.contains("sqlitemani"), "{}", err.message);
+ #[cfg(not(windows))]
+ {
+ // The Windows error just says 'LoadLibraryExW failed'
+ let err = result.err().unwrap();
+ assert!(err.message.contains("sqlitemani"), "{}", err.message);
+ }
// but of course loading manifest from manifest dir is OK
let result = ManagedDatabase::from_uri_with_profile_provider(
From 8eb1abfd0b4ee54ff3f0e07bc9aead2bd6387303 Mon Sep 17 00:00:00 2001
From: David Li
Date: Thu, 19 Mar 2026 16:23:09 +0900
Subject: [PATCH 6/6] windows
---
rust/driver_manager/tests/connection_profile.rs | 12 ------------
1 file changed, 12 deletions(-)
diff --git a/rust/driver_manager/tests/connection_profile.rs b/rust/driver_manager/tests/connection_profile.rs
index 845a6d993a..57207d25ed 100644
--- a/rust/driver_manager/tests/connection_profile.rs
+++ b/rust/driver_manager/tests/connection_profile.rs
@@ -743,18 +743,6 @@ uri = ":memory:"
assert!(err.message.contains("sqlitemani"), "{}", err.message);
}
- // but of course loading manifest from manifest dir is OK
- let result = ManagedDatabase::from_uri_with_profile_provider(
- "sqlite://",
- None,
- AdbcVersion::V100,
- LOAD_FLAG_DEFAULT,
- Some(vec![manifest_dir.path().to_path_buf()]),
- provider.clone(),
- std::iter::empty(),
- );
- assert!(result.is_ok());
-
manifest_dir.close().unwrap();
profile_dir.close().unwrap();
}