Skip to content

Commit c66d59f

Browse files
authored
feat(rust/driver_manager): implement connection profiles (#3973)
Implementing connection profiles for the Rust driver manager including the default FilesystemProfileProvider and updating the objects and tests accordingly.
1 parent 0f81654 commit c66d59f

File tree

7 files changed

+2290
-22
lines changed

7 files changed

+2290
-22
lines changed

rust/Cargo.lock

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/driver_manager/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ adbc_ffi.workspace = true
4040
arrow-array.workspace = true
4141
arrow-schema.workspace = true
4242
libloading = "0.8.8"
43+
path-slash = "0.2.1"
44+
regex = "1.12.3"
4345
toml = { version = "1.0.3", default-features = false, features = [
4446
"parse",
4547
"display",

rust/driver_manager/src/lib.rs

Lines changed: 223 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
// would prevent any parallelism between driver calls, which is not desirable.
102102

103103
pub mod error;
104+
pub mod profile;
104105
pub(crate) mod search;
105106

106107
use std::collections::HashSet;
@@ -127,7 +128,10 @@ use adbc_core::{
127128
};
128129
use adbc_ffi::driver_method;
129130

130-
use self::search::{parse_driver_uri, DriverLibrary};
131+
use self::search::{parse_driver_uri, DriverLibrary, DriverLocator};
132+
use crate::profile::{
133+
process_profile_value, ConnectionProfile, ConnectionProfileProvider, FilesystemProfileProvider,
134+
};
131135

132136
const ERR_CANCEL_UNSUPPORTED: &str =
133137
"Canceling connection or statement is not supported with ADBC 1.0.0";
@@ -354,6 +358,67 @@ pub struct ManagedDatabase {
354358
}
355359

356360
impl ManagedDatabase {
361+
/// Creates a new database connection from a URI string.
362+
///
363+
/// This method supports both direct driver URIs and profile references.
364+
///
365+
/// # URI Formats
366+
///
367+
/// ## Direct Driver Connection
368+
/// - `"driver_name:connection_string"` - Loads the specified driver and connects
369+
/// - `"driver_name://host:port/database"` - Standard database URI format
370+
///
371+
/// ## Profile Reference
372+
/// - `"profile://name"` - Loads connection configuration from a profile file
373+
/// - `"profile:///absolute/path/to/profile.toml"` - Absolute path to profile
374+
/// - `"profile://relative/path/to/profile.toml"` - Relative path to profile
375+
///
376+
/// # Arguments
377+
///
378+
/// * `uri` - The connection URI or profile reference
379+
/// * `entrypoint` - Optional driver entrypoint name (uses default if `None`)
380+
/// * `version` - ADBC version to use
381+
/// * `load_flags` - Flags controlling driver loading behavior
382+
/// * `additional_search_paths` - Optional paths to search for drivers or profiles
383+
///
384+
/// # Returns
385+
///
386+
/// A configured `ManagedDatabase` ready for creating connections.
387+
///
388+
/// # Errors
389+
///
390+
/// Returns an error if:
391+
/// - The URI format is invalid
392+
/// - The driver cannot be loaded
393+
/// - The profile cannot be found or parsed
394+
/// - Database initialization fails
395+
///
396+
/// # Examples
397+
///
398+
/// ```no_run
399+
/// use adbc_core::options::AdbcVersion;
400+
/// use adbc_driver_manager::ManagedDatabase;
401+
/// use adbc_core::LOAD_FLAG_DEFAULT;
402+
///
403+
/// // Direct connection
404+
/// let db = ManagedDatabase::from_uri(
405+
/// "sqlite::memory:",
406+
/// None,
407+
/// AdbcVersion::V100,
408+
/// LOAD_FLAG_DEFAULT,
409+
/// None
410+
/// )?;
411+
///
412+
/// // Profile connection
413+
/// let db = ManagedDatabase::from_uri(
414+
/// "profile://my_database",
415+
/// None,
416+
/// AdbcVersion::V100,
417+
/// LOAD_FLAG_DEFAULT,
418+
/// None
419+
/// )?;
420+
/// # Ok::<(), adbc_core::error::Error>(())
421+
/// ```
357422
pub fn from_uri(
358423
uri: &str,
359424
entrypoint: Option<&[u8]>,
@@ -371,6 +436,53 @@ impl ManagedDatabase {
371436
)
372437
}
373438

439+
/// Creates a new database connection from a URI with additional options.
440+
///
441+
/// This is similar to [`from_uri`](Self::from_uri), but allows passing additional
442+
/// database options that override any options from profiles.
443+
///
444+
/// # Arguments
445+
///
446+
/// * `uri` - The connection URI or profile reference
447+
/// * `entrypoint` - Optional driver entrypoint name
448+
/// * `version` - ADBC version to use
449+
/// * `load_flags` - Flags controlling driver loading behavior
450+
/// * `additional_search_paths` - Optional paths to search for drivers or profiles
451+
/// * `opts` - Database options to apply (override profile options)
452+
///
453+
/// # Returns
454+
///
455+
/// A configured `ManagedDatabase` with the specified options applied.
456+
///
457+
/// # Option Priority
458+
///
459+
/// Options are applied in this order (later values override earlier ones):
460+
/// 1. Profile options (if using a profile URI)
461+
/// 2. Options provided via `opts` parameter
462+
/// 3. URI connection string (for direct driver URIs)
463+
///
464+
/// # Examples
465+
///
466+
/// ```no_run
467+
/// use adbc_core::options::{AdbcVersion, OptionDatabase, OptionValue};
468+
/// use adbc_driver_manager::ManagedDatabase;
469+
/// use adbc_core::LOAD_FLAG_DEFAULT;
470+
///
471+
/// let opts = vec![
472+
/// (OptionDatabase::Username, OptionValue::String("user".to_string())),
473+
/// (OptionDatabase::Password, OptionValue::String("pass".to_string())),
474+
/// ];
475+
///
476+
/// let db = ManagedDatabase::from_uri_with_opts(
477+
/// "profile://my_database",
478+
/// None,
479+
/// AdbcVersion::V100,
480+
/// LOAD_FLAG_DEFAULT,
481+
/// None,
482+
/// opts,
483+
/// )?;
484+
/// # Ok::<(), adbc_core::error::Error>(())
485+
/// ```
374486
pub fn from_uri_with_opts(
375487
uri: &str,
376488
entrypoint: Option<&[u8]>,
@@ -379,20 +491,121 @@ impl ManagedDatabase {
379491
additional_search_paths: Option<Vec<PathBuf>>,
380492
opts: impl IntoIterator<Item = (<Self as Optionable>::Option, OptionValue)>,
381493
) -> Result<Self> {
382-
let (driver, final_uri) = parse_driver_uri(uri)?;
383-
384-
let mut drv = ManagedDriver::load_from_name(
385-
driver,
494+
let profile_provider = FilesystemProfileProvider;
495+
Self::from_uri_with_profile_provider(
496+
uri,
386497
entrypoint,
387498
version,
388499
load_flags,
389500
additional_search_paths,
390-
)?;
501+
profile_provider,
502+
opts,
503+
)
504+
}
505+
506+
/// Creates a new database connection from a URI with a custom profile provider.
507+
///
508+
/// This advanced method allows using a custom implementation of
509+
/// [`ConnectionProfileProvider`] to load profiles from alternative sources
510+
/// (e.g., remote configuration services, encrypted storage, etc.).
511+
///
512+
/// # Arguments
513+
///
514+
/// * `uri` - The connection URI or profile reference
515+
/// * `entrypoint` - Optional driver entrypoint name
516+
/// * `version` - ADBC version to use
517+
/// * `load_flags` - Flags controlling driver loading behavior
518+
/// * `additional_search_paths` - Optional paths to search for drivers or profiles
519+
/// * `profile_provider` - Custom profile provider implementation
520+
/// * `opts` - Database options to apply (override profile options)
521+
///
522+
/// # Returns
523+
///
524+
/// A configured `ManagedDatabase` using the custom profile provider.
525+
///
526+
/// # Examples
527+
///
528+
/// ```no_run
529+
/// use adbc_core::options::{AdbcVersion, OptionDatabase, OptionValue};
530+
/// use adbc_driver_manager::ManagedDatabase;
531+
/// use adbc_driver_manager::profile::FilesystemProfileProvider;
532+
/// use adbc_core::LOAD_FLAG_DEFAULT;
533+
///
534+
/// let provider = FilesystemProfileProvider;
535+
/// let opts = vec![(OptionDatabase::Username, OptionValue::String("admin".to_string()))];
536+
///
537+
/// let db = ManagedDatabase::from_uri_with_profile_provider(
538+
/// "profile://my_database",
539+
/// None,
540+
/// AdbcVersion::V100,
541+
/// LOAD_FLAG_DEFAULT,
542+
/// None,
543+
/// provider,
544+
/// opts,
545+
/// )?;
546+
/// # Ok::<(), adbc_core::error::Error>(())
547+
/// ```
548+
pub fn from_uri_with_profile_provider(
549+
uri: &str,
550+
entrypoint: Option<&[u8]>,
551+
version: AdbcVersion,
552+
load_flags: LoadFlags,
553+
additional_search_paths: Option<Vec<PathBuf>>,
554+
profile_provider: impl ConnectionProfileProvider,
555+
opts: impl IntoIterator<Item = (<Self as Optionable>::Option, OptionValue)>,
556+
) -> Result<Self> {
557+
let result = parse_driver_uri(uri)?;
558+
let (mut drv, default_opts) = match result {
559+
DriverLocator::Uri(driver, final_uri) => {
560+
let drv = ManagedDriver::load_from_name(
561+
driver,
562+
entrypoint,
563+
version,
564+
load_flags,
565+
additional_search_paths,
566+
)?;
567+
568+
let final_opts = vec![(
569+
OptionDatabase::Uri,
570+
OptionValue::String(final_uri.to_string()),
571+
)];
572+
(drv, final_opts)
573+
}
574+
DriverLocator::Profile(profile) => {
575+
let profile =
576+
profile_provider.get_profile(profile, additional_search_paths.clone())?;
577+
let (driver_name, init_func) = profile.get_driver_name()?;
578+
579+
let drv: ManagedDriver;
580+
if let Some(init_fn) = init_func {
581+
drv = ManagedDriver::load_static(init_fn, version)?;
582+
} else {
583+
drv = ManagedDriver::load_from_name(
584+
driver_name,
585+
entrypoint,
586+
version,
587+
load_flags,
588+
additional_search_paths,
589+
)?;
590+
}
591+
592+
let profile_opts: Vec<(OptionDatabase, OptionValue)> = profile
593+
.get_options()?
594+
.into_iter()
595+
.map(|(k, v)| -> Result<(OptionDatabase, OptionValue)> {
596+
if let OptionValue::String(s) = v {
597+
let result = process_profile_value(&s)?;
598+
Ok((k, result))
599+
} else {
600+
Ok((k, v))
601+
}
602+
})
603+
.collect::<Result<Vec<_>>>()?;
604+
(drv, profile_opts)
605+
}
606+
};
391607

392-
drv.new_database_with_opts(opts.into_iter().chain(std::iter::once((
393-
OptionDatabase::Uri,
394-
OptionValue::String(final_uri.to_string()),
395-
))))
608+
drv.new_database_with_opts(default_opts.into_iter().chain(opts))
396609
}
397610

398611
fn ffi_driver(&self) -> &adbc_ffi::FFI_AdbcDriver {

0 commit comments

Comments
 (0)