Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
9f282da
feat(c/driver_manager): add connection profile interface
zeroshade Jan 9, 2026
bf9d9fd
pre-commit linting
zeroshade Jan 9, 2026
d7fae5a
typos
zeroshade Jan 9, 2026
a9c1fb4
darn you windows
zeroshade Jan 9, 2026
e279a27
update test cases for ParseDriverURI
zeroshade Jan 9, 2026
14895c5
darn you windows
zeroshade Jan 9, 2026
f75ed95
fix heap issue
zeroshade Jan 9, 2026
f64f3b8
some test fixes
zeroshade Jan 9, 2026
fc8ab25
more test fix
zeroshade Jan 9, 2026
c54fe84
one more test leak
zeroshade Jan 9, 2026
83a1820
windows sucks
zeroshade Jan 9, 2026
0fd7a86
pre-commit
zeroshade Jan 9, 2026
5f83568
fixes from comments and nested tables
zeroshade Jan 9, 2026
c10ffe2
add test using env_var()
zeroshade Jan 9, 2026
336eb02
pre-commit
zeroshade Jan 9, 2026
4f29d52
maybe clang will like this better
zeroshade Jan 9, 2026
65f1c6d
maybe clang will like me now
zeroshade Jan 9, 2026
9528c67
extra complexity to make clang happy?
zeroshade Jan 9, 2026
fda0197
add docs
zeroshade Jan 9, 2026
e421803
yet another attempt
zeroshade Jan 10, 2026
2bd4330
fix remaining issues, i hope
zeroshade Jan 10, 2026
792e223
this might fix the issue?
zeroshade Jan 11, 2026
fe2e94c
Disallow `profile` as a driver manifest basename
ianmcook Jan 15, 2026
9d9adfc
Reserve the `profile` option key for use by driver managers
ianmcook Jan 16, 2026
bc074d3
update from comments
zeroshade Jan 19, 2026
3cebf5a
updates from comments
zeroshade Jan 26, 2026
9896615
updates from comments
zeroshade Feb 11, 2026
c3f34ff
pre-commit
zeroshade Feb 11, 2026
6166eaf
better option for init_func
zeroshade Feb 11, 2026
549fcb3
pre-commit lint
zeroshade Feb 11, 2026
f124d1a
update to {{ }} syntax
zeroshade Feb 12, 2026
b0a32ef
missed closing quotes
zeroshade Feb 12, 2026
0a42959
vscode hates me
zeroshade Feb 12, 2026
33c9d16
Update c/include/arrow-adbc/adbc_driver_manager.h
zeroshade Feb 12, 2026
a8fa1ce
sync
zeroshade Feb 12, 2026
2581b5c
make regex onst static
zeroshade Feb 12, 2026
48d8071
`static const` not `const static`
zeroshade Feb 12, 2026
dc86ed1
remove escaping
zeroshade Feb 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
582 changes: 566 additions & 16 deletions c/driver_manager/adbc_driver_manager.cc

Large diffs are not rendered by default.

513 changes: 509 additions & 4 deletions c/driver_manager/adbc_driver_manager_test.cc

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions c/include/arrow-adbc/adbc.h
Original file line number Diff line number Diff line change
Expand Up @@ -1300,6 +1300,11 @@ AdbcStatusCode AdbcDatabaseGetOptionInt(struct AdbcDatabase* database, const cha
/// Options may be set before AdbcDatabaseInit. Some drivers may
/// support setting options after initialization as well.
///
/// Driver managers may treat some option keys as manager-reserved and
/// handle them without forwarding them to the underlying driver. In
/// particular, the option key "profile" is reserved for connection
/// profiles and must not be implemented or interpreted by drivers.
///
/// \param[in] database The database.
/// \param[in] key The option to set.
/// \param[in] value The option value.
Expand Down
180 changes: 180 additions & 0 deletions c/include/arrow-adbc/adbc_driver_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,186 @@ AdbcStatusCode AdbcDriverManagerDatabaseSetAdditionalSearchPathList(
ADBC_EXPORT
const char* AdbcStatusCodeMessage(AdbcStatusCode code);

/// \defgroup adbc-driver-manager-connection-profile Connection Profiles
/// The ADBC driver manager can support "connection profiles" that specify
/// a driver and options to use when connecting. This allows users to
/// specify connection information in a file or environment variable, and have the
/// driver manager load the appropriate driver and set options accordingly.
///
/// This allows creating reusable connection configurations for sharing and distribution
/// without needing to hardcode driver names and options in application code. Profiles
/// will be loaded during DatabaseInit before attempting to initialize the driver. Any
/// options specified by the profile will be applied but will not override options
/// that have already been set using DatabaseSetOption.
///
/// To facilitate customization, we define an interface for implementing a Connection
/// Profile object along with a provider function definition which can be set into
/// the driver manager to allow for customized profile loading.
///
/// A profile can be specified to the Driver Manager in one of two ways,
/// which will invoke the profile provider during the call to DatabaseInit:
///
/// 1. The "profile" option can be set using DatabaseSetOption with the name of the
/// profile to load.
/// 2. The "uri" being used can have the form "profile://<profile>"
///
/// @{

/// \brief Abstract interface for connection profile providers
struct ADBC_EXPORT AdbcConnectionProfile {
/// \brief Opaque implementation-defined state.
/// This field is NULL if the profile is uninitialized/freed (but
/// it need not have a value even if the profile is initialized).
void* private_data;

/// \brief Release the profile and perform any cleanup.
void (*release)(struct AdbcConnectionProfile* profile);

/// \brief Get the driver to use as specified by this profile.
///
/// It is not required that a profile specify a driver. If the options
// can be reusable across drivers, then the profile does not need to specify
/// a driver (if this provides an empty string or nullptr then the driver
/// must be defined by other means, e.g. by the driver / uri options).
///
/// \param[in] profile The profile to query.
/// \param[out] driver_name The name of the driver to use, or NULL if not specified.
/// \param[out] init_func The init function to use for the driver, or NULL if not
/// specified.
/// \param[out] error An optional location to return an error message
AdbcStatusCode (*GetDriverName)(struct AdbcConnectionProfile* profile,
const char** driver_name, AdbcDriverInitFunc* init_func,
struct AdbcError* error);

/// \brief Get the string options specified by the profile
///
/// The keys and values returned by this function are owned by the profile
/// object itself and do not need to be freed or managed by the caller.
/// They must not be accessed after calling release on the profile.
///
/// The profile can also indicate that a value should be pulled from the environment
/// by having a value in the form `env_var(ENV_VAR_NAME)`. If the driver
/// manager encounters a value of this form, it will replace it with the actual value
/// of the environment variable `ENV_VAR_NAME` before setting the option. This
/// is only valid for option *values* not *keys*.
///
/// \param[in] profile The profile to query.
/// \param[out] keys The keys of the options specified by the profile.
/// \param[out] values The values of the options specified by the profile.
/// \param[out] num_options The number of options specified by the profile,
/// consumers must not access keys or values beyond this count.
/// \param[out] error An optional location to return an error message
AdbcStatusCode (*GetOptions)(struct AdbcConnectionProfile* profile, const char*** keys,
const char*** values, size_t* num_options,
struct AdbcError* error);

/// \brief Get the integer options specified by the profile
///
/// The keys and values returned by this function are owned by the profile
/// object itself and do not need to be freed or managed by the caller. They must not be
/// accessed after calling release on the profile.
///
/// Values returned by this function will be set using the DatabaseSetOptionInt function
/// on the database object being initialized. If the driver does not support the
/// DatabaseSetOptionInt function, then options should only be returned as strings.
///
/// \param[in] profile The profile to query.
/// \param[out] keys The keys of the options specified by the profile.
/// \param[out] values The values of the options specified by the profile.
/// \param[out] num_options The number of options specified by the profile,
/// consumers must not access keys or values beyond this count.
/// \param[out] error An optional location to return an error message
AdbcStatusCode (*GetIntOptions)(struct AdbcConnectionProfile* profile,
const char*** keys, const int64_t** values,
size_t* num_options, struct AdbcError* error);

/// \brief Get the double options specified by the profile
///
/// The keys and values returned by this function are owned by the profile
/// object itself and do not need to be freed or managed by the caller. They must not be
/// accessed after calling release on the profile.
///
/// Values returned by this function will be set using the DatabaseSetOptionDouble
/// function on the database object being initialized. If the driver does not support
/// the DatabaseSetOptionDouble function, then options should only be returned as
/// strings.
///
/// \param[in] profile The profile to query.
/// \param[out] keys The keys of the options specified by the profile.
/// \param[out] values The values of the options specified by the profile.
/// \param[out] num_options The number of options specified by the profile,
/// consumers must not access keys or values beyond this count.
/// \param[out] error An optional location to return an error message
AdbcStatusCode (*GetDoubleOptions)(struct AdbcConnectionProfile* profile,
const char*** keys, const double** values,
size_t* num_options, struct AdbcError* error);
};

/// \brief Common definition for a connection profile provider
///
/// \param[in] profile_name The name of the profile to load. This is the value of the
/// "profile" option or the profile specified in the URI.
/// \param[in] additional_search_path_list A list of additional paths to search for
/// profiles, delimited by the OS specific path list separator.
/// \param[out] out The profile to return. The caller will take ownership of the profile
/// and is responsible for calling release on it when finished.
/// \param[out] error An optional location to return an error message if necessary.
typedef AdbcStatusCode (*AdbcConnectionProfileProvider)(
const char* profile_name, const char* additional_search_path_list,
struct AdbcConnectionProfile* out, struct AdbcError* error);

/// \brief Set a custom connection profile provider for the driver manager.
///
/// If no provider is set, the driver manager will use a default, filesystem-based
/// provider which will look for profiles in the following locations if not given an
/// absolute path to a file:
///
/// 1. The environment variable ADBC_PROFILE_PATH, which is a list of paths to search for
/// profiles.
/// 2. The user-level configuration directory (e.g. ~/.config/adbc/profiles on Linux).
///
/// The filesystem-based profile looks for a file named <profile_name>.toml if there is
/// no extension provided, attempting to parse the toml file for the profile information.
/// If the file is found and parsed successfully, the options specified in the profile
/// which have not already been set will be set as if by DatabaseSetOption just before
/// initialization as part of DatabaseInit.
///
/// For file-based profiles the expected format is as follows:
/// ```toml
/// version = 1
/// driver = "driver_name"
///
/// [options]
/// option1 = "value1"
/// option2 = 42
/// option3 = 3.14
Comment on lines +324 to +330
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to wrap this in a top level [profile] item (like pyproject.toml's [project] or Cargo.toml's [workspace])?

Suggested change
/// version = 1
/// driver = "driver_name"
///
/// [options]
/// option1 = "value1"
/// option2 = 42
/// option3 = 3.14
/// [adbc.profile]
/// version = 1
/// driver = "driver_name"
///
/// [options]
/// option1 = "value1"
/// option2 = 42
/// option3 = 3.14

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I was trying to avoid having to do that. Is there any benefit to doing so?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just seemed like a common idiom in .toml configs, perhaps to allow for future expansion (e.g., if you ever want >1 profile to live in one .toml)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think after a lot of discussion between myself, @lidavidm and @ianmcook we came to the decision that we don't want more than 1 profile to live in one .toml file haha 😄

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having multiple connection profiles defined in a single TOML file could potentially be nice, but it complicates the profile search procedure, and I worry that it could confuse users.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think my point was just that you have some future flexibility if you nest the configuration and if you put adbc and profile in there somewhere there's a better chance somebody who runs across this file without context will know what it is.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't do that for driver TOML files though 😬

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is very easy to change if it ever comes up 🙂

/// ```
///
/// Boolean options will be converted to string equivalents of "true" or "false".
///
/// \param[in] database The database to set the profile provider for.
/// \param[in] provider The profile provider to use. If NULL, the default filesystem-based
/// provider will be used if a profile is needed.
/// \param[out] error An optional location to return an error message if necessary
ADBC_EXPORT
AdbcStatusCode AdbcDriverManagerDatabaseSetProfileProvider(
struct AdbcDatabase* database, AdbcConnectionProfileProvider provider,
struct AdbcError* error);

/// \brief Default Filesystem-based profile provider for the driver manager.
///
/// We expose this so that consumers would be able to write a provider that falls back on
/// the default filesystem-based provider if their custom provider fails to find a
/// profile. This allows for more flexible provider implementations that can still
/// leverage the default behavior when needed.
ADBC_EXPORT
AdbcStatusCode AdbcProfileProviderFilesystem(const char* profile_name,
const char* additional_search_path_list,
struct AdbcConnectionProfile* out,
struct AdbcError* error);

/// @}

#endif // ADBC_DRIVER_MANAGER_H

#ifdef __cplusplus
Expand Down
Loading
Loading