blob: 6e17531ac8bcf22b26e89c66810a42f34010e9f1 [file]
.. Licensed to the Apache Software Foundation (ASF) under one
.. or more contributor license agreements. See the NOTICE file
.. distributed with this work for additional information
.. regarding copyright ownership. The ASF licenses this file
.. to you under the Apache License, Version 2.0 (the
.. "License"); you may not use this file except in compliance
.. with the License. You may obtain a copy of the License at
..
.. http://www.apache.org/licenses/LICENSE-2.0
..
.. Unless required by applicable law or agreed to in writing,
.. software distributed under the License is distributed on an
.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
.. KIND, either express or implied. See the License for the
.. specific language governing permissions and limitations
.. under the License.
===========================================
ADBC Driver Manager and Connection Profiles
===========================================
.. note:: This page is a detailed reference on the connection profile format and
behavior, aimed at developers of ADBC drivers and libraries and
advanced users. For basic usage of connection profiles, see
:doc:`Connection Profiles <../connection_profiles>` instead.
Profiles are loaded by the :term:`driver manager`, which is not
required to use ADBC in general but which 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 on how the driver manager works, see :doc:`how_manager`.
There are two ways to pass database options through the driver manager:
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.
Connection profiles combine a driver and database 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()``.
Quick Start
===========
Using a Profile via URI
-----------------------
The simplest way to use a profile is through a URI:
.. code-block:: c
AdbcDatabase database;
AdbcDatabaseNew(&database, &error);
AdbcDatabaseSetOption(&database, "uri", "profile://my_snowflake_prod", &error);
AdbcDatabaseInit(&database, &error);
Using a Profile via Option
---------------------------
Alternatively, specify the profile name directly:
.. code-block:: c
AdbcDatabase database;
AdbcDatabaseNew(&database, &error);
AdbcDatabaseSetOption(&database, "profile", "my_snowflake_prod", &error);
AdbcDatabaseInit(&database, &error);
Profile File Format
===================
Filesystem-based profiles use TOML format with the following structure:
.. code-block:: toml
# The version is required.
profile_version = 1
# The driver is optional, but if not provided it must be set by the application.
driver = "snowflake"
# The Options table is required, even if empty
[Options]
# String options
adbc.snowflake.sql.account = "mycompany"
adbc.snowflake.sql.warehouse = "COMPUTE_WH"
adbc.snowflake.sql.database = "PRODUCTION"
adbc.snowflake.sql.schema = "PUBLIC"
# Integer options
adbc.snowflake.sql.client_session_keep_alive_heartbeat_frequency = 3600
# Double options
adbc.snowflake.sql.client_timeout = 30.5
# Boolean options (converted to "true" or "false" strings)
adbc.snowflake.sql.client_session_keep_alive = true
profile_version
---------------
- **Required**: Yes
- **Type**: Integer
- **Supported values**: ``1``
The ``profile_version`` field specifies the profile format version. Currently, only version 1 is supported.
This will enable future changes while maintaining backward compatibility.
driver
------
- **Required**: No
- **Type**: String
The ``driver`` field specifies which ADBC driver to load. This can be:
- 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", "<driver>")``.
For more detils, see :doc:`driver_manifests`.
Options Section
---------------
The ``[Options]`` section contains driver-specific configuration options to apply to the ``AdbcDatabase`` upon creation. This section must be present, even if empty. Options can be of the following types:
**String values**
Applied using ``AdbcDatabaseSetOption()``
.. code-block:: toml
adbc.snowflake.sql.account = "mycompany"
adbc.snowflake.sql.warehouse = "COMPUTE_WH"
**Integer values**
Applied using ``AdbcDatabaseSetOptionInt()``
.. code-block:: toml
adbc.snowflake.sql.client_session_keep_alive_heartbeat_frequency = 3600
**Double values**
Applied using ``AdbcDatabaseSetOptionDouble()``
.. code-block:: toml
adbc.snowflake.sql.client_timeout = 30.5
**Boolean values**
Converted to strings ``"true"`` or ``"false"`` and applied using ``AdbcDatabaseSetOption()``
.. code-block:: toml
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
------------------
Profile values support substitution of environment variables and other dynamic content.
This allows profiles to reference sensitive information (like passwords or tokens) without
hardcoding them in the profile file. Dynamic values can be injected by the presence of the ``{{ }}`` syntax,
similar to many templating engines. Within the double curly braces, the driver manager can
recognize certain functions to perform substitutions.
Currently, the only recognized function is ``env_var()`` for environment variable substitution,
but this may be extended in the future to support other types of dynamic content.
.. important::
Dynamic content substitution only applies to option **values**, not **keys**.
Environment Variable Substitution
'''''''''''''''''''''''''''''''''
Profile values can reference environment variables using the ``{{ env_var() }}`` syntax:
.. code-block:: toml
profile_version = 1
driver = "adbc_driver_snowflake"
[Options]
adbc.snowflake.sql.account = "{{ env_var(SNOWFLAKE_ACCOUNT) }}"
adbc.snowflake.sql.auth_token = "{{ env_var(SNOWFLAKE_TOKEN) }}"
adbc.snowflake.sql.warehouse = "COMPUTE_WH"
When the driver manager encounters ``{{ env_var(VAR_NAME) }}``, it replaces the placeholder with the contents of environment variable ``VAR_NAME``. If the environment variable is not set, the placeholder is replaced with an empty string and processing of the rest of the value continues (e.g. ``"foo{{ env_var(MISSING) }}bar"`` becomes ``"foobar"``).
.. _profile-search-locations:
Profile Search Locations
=========================
When using a profile name (not an absolute path), the driver manager searches for ``<profile_name>.toml`` in the following locations:
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):
- ``$CONDA_PREFIX/etc/adbc/profiles/``
4. **User Configuration Directory**:
- Linux: ``$XDG_CONFIG_HOME/adbc/profiles`` if set, else ``~/.config/adbc/profiles/``
- macOS: ``~/Library/Application Support/ADBC/Profiles/``
- Windows: ``%LOCALAPPDATA%\ADBC\Profiles\``
The driver manager searches locations in order and uses the first matching profile file found.
Using Absolute Paths
--------------------
To specify an absolute path to a profile file:
.. code-block:: c
// Via profile option
AdbcDatabaseSetOption(&database, "profile", "/etc/adbc/profiles/production.toml", &error);
// Via URI (must have .toml extension)
AdbcDatabaseSetOption(&database, "uri", "profile:///etc/adbc/profiles/production.toml", &error);
Examples
========
Example 1: Snowflake Production Profile
----------------------------------------
File: ``~/.config/adbc/profiles/snowflake_prod.toml``
.. code-block:: toml
profile_version = 1
driver = "snowflake"
[Options]
adbc.snowflake.sql.account = "{{ env_var(SNOWFLAKE_ACCOUNT) }}"
adbc.snowflake.sql.auth_token = "{{ env_var(SNOWFLAKE_TOKEN) }}"
adbc.snowflake.sql.warehouse = "PRODUCTION_WH"
adbc.snowflake.sql.database = "PROD_DB"
adbc.snowflake.sql.schema = "PUBLIC"
adbc.snowflake.sql.client_session_keep_alive = true
adbc.snowflake.sql.client_session_keep_alive_heartbeat_frequency = 3600
Usage:
.. code-block:: c
// Set environment variables
setenv("SNOWFLAKE_ACCOUNT", "mycompany", 1);
setenv("SNOWFLAKE_TOKEN", "secret_token", 1);
// Use profile
AdbcDatabase database;
AdbcDatabaseNew(&database, &error);
AdbcDatabaseSetOption(&database, "uri", "profile://snowflake_prod", &error);
AdbcDatabaseInit(&database, &error);
Example 2: PostgreSQL Development Profile
------------------------------------------
File: ``~/.config/adbc/profiles/postgres_dev.toml``
.. code-block:: toml
profile_version = 1
driver = "postgresql"
[Options]
uri = "postgresql://localhost:5432/dev_db?sslmode=disable"
username = "dev_user"
password = "{{ env_var(POSTGRES_DEV_PASSWORD) }}"
Example 3: Driver-Agnostic Profile
-----------------------------------
Profiles can omit the driver field for reusable configurations:
File: ``~/.config/adbc/profiles/default_timeouts.toml``
.. code-block:: toml
profile_version = 1
# No driver specified - can be used with any driver
[Options]
adbc.connection.timeout = 30.0
adbc.statement.timeout = 60.0
Usage (driver specified separately):
.. code-block:: c
AdbcDatabase database;
AdbcDatabaseNew(&database, &error);
AdbcDatabaseSetOption(&database, "driver", "adbc_driver_snowflake", &error);
AdbcDatabaseSetOption(&database, "profile", "default_timeouts", &error);
AdbcDatabaseInit(&database, &error);
Advanced Usage
==============
Option Precedence
-----------------
Options are applied in the following order (later overrides earlier):
1. Driver defaults
2. Profile options (from ``[Options]`` section)
3. Options set via ``AdbcDatabaseSetOption()`` before ``AdbcDatabaseInit()``
Example:
.. code-block:: c
AdbcDatabase database;
AdbcDatabaseNew(&database, &error);
// Profile sets warehouse = "COMPUTE_WH"
AdbcDatabaseSetOption(&database, "profile", "snowflake_prod", &error);
// This overrides the profile setting
AdbcDatabaseSetOption(&database, "adbc.snowflake.sql.warehouse", "ANALYTICS_WH", &error);
AdbcDatabaseInit(&database, &error);
// Result: warehouse = "ANALYTICS_WH"
.. note:: Options of different types are set separately. For example, if the
profile defines an option with an integer value, and the application
sets the same option but with a string value, it is
implementation-defined as to which value will take precedence. If
the application were to use an integer value instead, then the
application value would take precedence as expected.
Custom Profile Providers
=========================
Applications can implement custom profile providers to load profiles from alternative sources (databases, key vaults, configuration services, etc.).
Interface Definition
--------------------
A profile provider must implement the ``AdbcConnectionProfile`` interface:
.. code-block:: c
struct AdbcConnectionProfile {
void* private_data;
// this will be called by the driver manager after retrieving the necessary information from the profile.
void (*release)(struct AdbcConnectionProfile* profile);
AdbcStatusCode (*GetDriverName)(struct AdbcConnectionProfile* profile,
const char** driver_name,
AdbcDriverInit* init_func,
struct AdbcError* error);
AdbcStatusCode (*GetOptions)(struct AdbcConnectionProfile* profile,
const char*** keys, const char*** values,
size_t* num_options, struct AdbcError* error);
AdbcStatusCode (*GetIntOptions)(struct AdbcConnectionProfile* profile,
const char*** keys, const int64_t** values,
size_t* num_options, struct AdbcError* error);
AdbcStatusCode (*GetDoubleOptions)(struct AdbcConnectionProfile* profile,
const char*** keys, const double** values,
size_t* num_options, struct AdbcError* error);
};
Provider Function
-----------------
The provider function signature:
.. code-block:: c
typedef AdbcStatusCode (*AdbcConnectionProfileProvider)(
const char* profile_name,
const char* additional_search_path_list,
struct AdbcConnectionProfile* out,
struct AdbcError* error);
Example Implementation
----------------------
.. code-block:: c
// Example: Load profiles from a key-value store
AdbcStatusCode MyCustomProfileProvider(const char* profile_name,
const char* additional_search_path_list,
struct AdbcConnectionProfile* out,
struct AdbcError* error) {
// Fetch profile from custom source
MyProfileData* data = LoadProfileFromKeyVault(profile_name);
if (!data) {
SetError(error, "Profile not found in key vault");
return ADBC_STATUS_NOT_FOUND;
}
std::memset(out, 0, sizeof(struct AdbcConnectionProfile));
// Populate profile structure
out->private_data = data;
out->release = MyProfileRelease;
out->GetDriverName = MyGetDriverName;
out->GetOptions = MyGetOptions;
out->GetIntOptions = MyGetIntOptions;
out->GetDoubleOptions = MyGetDoubleOptions;
return ADBC_STATUS_OK;
}
// Register custom provider
AdbcDatabase database;
AdbcDatabaseNew(&database, &error);
AdbcDriverManagerDatabaseSetProfileProvider(&database, MyCustomProfileProvider, &error);
AdbcDatabaseSetOption(&database, "profile", "prod_config", &error);
AdbcDatabaseInit(&database, &error);
Use Cases
=========
Development vs. Production
---------------------------
Maintain separate profiles for different environments:
.. code-block:: bash
# Development
export ADBC_PROFILE=snowflake_dev
# Production
export ADBC_PROFILE=snowflake_prod
Application code:
.. code-block:: c
const char* profile = getenv("ADBC_PROFILE");
if (!profile) profile = "default";
AdbcDatabaseSetOption(&database, "profile", profile, &error);
Credential Management
---------------------
Store credentials separately from code:
.. code-block:: toml
[Options]
adbc.snowflake.sql.account = "mycompany"
adbc.snowflake.sql.auth_token = "{{ env_var(SNOWFLAKE_TOKEN) }}"
Then set ``SNOWFLAKE_TOKEN`` via environment variable, secrets manager, or configuration service.
Multi-Tenant Applications
--------------------------
Use profiles to support different customer configurations:
.. code-block:: c
char profile_name[256];
snprintf(profile_name, sizeof(profile_name), "customer_%s", customer_id);
AdbcDatabaseSetOption(&database, "profile", profile_name, &error);
Testing
-------
Use profiles to switch between mock and real databases:
.. code-block:: c
#ifdef TESTING
const char* profile = "mock_database";
#else
const char* profile = "production";
#endif
AdbcDatabaseSetOption(&database, "profile", profile, &error);
Error Handling
==============
Profile Not Found
-----------------
If a profile cannot be found, ``AdbcDatabaseInit()`` returns ``ADBC_STATUS_NOT_FOUND`` with a detailed error message listing all searched locations:
.. code-block:: text
[Driver Manager] Profile not found: my_profile
Also searched these paths for profiles:
ADBC_PROFILE_PATH: /custom/path
user config dir: /home/user/.config/adbc/profiles
system config dir: /etc/adbc/profiles
Invalid Profile Format
----------------------
If a profile file exists but is malformed, ``AdbcDatabaseInit()`` returns ``ADBC_STATUS_INVALID_ARGUMENT``:
.. code-block:: text
[Driver Manager] Could not open profile. Error at line 5: expected '=' after key.
Profile: /home/user/.config/adbc/profiles/my_profile.toml
Missing Driver
--------------
If a profile doesn't specify a driver and none is provided via other means:
.. code-block:: text
[Driver Manager] Must provide 'driver' parameter
(or encode driver in 'uri' parameter)
Best Practices
==============
1. **Use environment variables for secrets**: Never store credentials directly in profile files.
.. code-block:: toml
# Good
password = "{{ env_var(DB_PASSWORD) }}"
# Bad
password = "my_secret_password"
2. **Organize profiles hierarchically**: Group related profiles in subdirectories using additional search paths.
3. **Document profile schemas**: Maintain documentation of required environment variables for each profile.
4. **Version control without secrets**: Profile files can be version controlled when using ``{{ env_var(VAR_NAME) }}`` for sensitive values.
5. **Test profile loading**: Verify profiles load correctly in CI/CD pipelines.
6. **Use meaningful names**: Name profiles descriptively (e.g., ``snowflake_prod_analytics`` vs. ``profile1``).
7. **Validate environment variables**: Check that required environment variables are set before calling ``AdbcDatabaseInit()``.
API Reference
=============
Setting a Profile Provider
---------------------------
.. code-block:: c
AdbcStatusCode AdbcDriverManagerDatabaseSetProfileProvider(
struct AdbcDatabase* database,
AdbcConnectionProfileProvider provider,
struct AdbcError* error);
Sets a custom connection profile provider. Must be called before ``AdbcDatabaseInit()``.
**Parameters:**
- ``database``: Database object to configure
- ``provider``: Profile provider function, or ``NULL`` for default filesystem provider
- ``error``: Optional error output
**Returns:** ``ADBC_STATUS_OK`` on success, error code otherwise.
Setting Additional Search Paths
--------------------------------
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
AdbcDatabaseSetOption(
&database,
"additional_profile_search_path_list",
"/opt/app/profiles:/etc/app/profiles",
&error);
// Windows
AdbcDatabaseSetOption(
&database,
"additional_profile_search_path_list",
"C:\\App\\Profiles;C:\\ProgramData\\App\\Profiles",
&error);
See Also
========
- :doc:`how_manager` - Driver Manager overview
- :doc:`specification` - Driver specification and options
- :doc:`../cpp/driver_manager` - CPP Driver Manager Reference