This is an automated email from the ASF dual-hosted git repository.
zeroshade 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 252874584 docs: add docs for driver manifests (#3176)
252874584 is described below
commit 252874584280d849e02a095a12c67b4f5b5b2916
Author: Matt Topol <[email protected]>
AuthorDate: Mon Jul 21 13:56:54 2025 -0400
docs: add docs for driver manifests (#3176)
With the driver manager implementations for C/C++, Go, Rust and Python
updated to utilize and leverage driver manifests, we should properly
document how manifests work and what the format is.
This change updates the docs to include a description of the driver
manifest file and the process used to search for a driver manifest or
driver given just a name. This includes the Load Flags and example code.
---------
Co-authored-by: Sutou Kouhei <[email protected]>
---
docs/source/format/driver_manifests.rst | 382 +++++++++++++++++++++++++++++++
docs/source/format/manifest_load.mmd | 43 ++++
docs/source/format/manifest_load.mmd.svg | 1 +
docs/source/index.rst | 1 +
4 files changed, 427 insertions(+)
diff --git a/docs/source/format/driver_manifests.rst
b/docs/source/format/driver_manifests.rst
new file mode 100644
index 000000000..7e07955ca
--- /dev/null
+++ b/docs/source/format/driver_manifests.rst
@@ -0,0 +1,382 @@
+.. 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 Manifests
+=================================
+
+.. note:: This document focuses on scenarios that utilize the driver manager
+ to load drivers. The driver manager is not required to utilize ADBC
+ in general, but allows a convenient experience for dynamically
+ loading arbitrary drivers.
+
+The ADBC driver manager is itself, an ADBC driver which simply loads another
driver
+dynamically and forwards the calls to the loaded driver. For more information
on the
+driver manager see :doc:`how_manager`.
+
+There are essentially two ways to specify a driver for the driver manager to
load:
+
+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
+
+When using the driver manager, you can either utilize the ``driver`` option to
the
+driver manager, or you can use functions in the language bindings which
explicitly
+load a driver by name.
+
+.. note:: In addition to the ``driver`` option, there is also an
``entrypoint`` option
+ which can be used to specify the entrypoint function to call for
populating
+ the driver function table. If the driver does not use the default
entrypoint
+ function, it can be indicated with this option.
+
+Directly Loading a Driver
+=========================
+
+The simplest mechanism for loading a driver via the driver manager is to
provide a
+direct file path to the dynamic library as the driver name.
+
+.. tab-set::
+
+ .. tab-item:: C/C++
+ :sync: cpp
+
+ You can use the :c:func:`AdbcLoadDriver` function to load the driver
directly or you can use it as a driver
+ itself via :c:struct:`AdbcDatabase`.
+
+ .. code-block:: cpp
+
+ // load directly
+ struct AdbcDriver driver;
+ struct AdbcError error;
+
+ std::memset(&driver, 0, sizeof(driver));
+ std::memset(&error, 0, sizeof(error));
+
+ auto status = AdbcLoadDriver("/path/to/libadbc_driver.so", nullptr,
+ ADBC_VERSION_1_1_0, &driver, &error);
+ // if status != ADBC_STATUS_OK then handle the error
+
+ // or use the Driver Manager as a driver itself
+ struct AdbcDatabase database;
+ struct AdbcError error;
+ std::memset(&database, 0, sizeof(database));
+ std::memset(&error, 0, sizeof(error));
+ auto status = AdbcDatabaseNew(&database, &error);
+ // check status
+ status = AdbcDatabaseSetOption(&database, "driver",
"/path/to/libadbc_driver.so", &error);
+ // check status
+
+ .. tab-item:: GLib
+ :sync: glib
+
+ You can use it as a driver via ``GADBCDatabase``
+
+ .. code-block:: c
+
+ GError *error = NULL;
+ GADBCDatabase *database = gadbc_database_new(&error);
+ if (!database) {
+ /* handle error */
+ }
+ if (!gadbc_database_set_option(database, "driver",
"/path/to/libadbc_driver.so", &error)) {
+ /* handle error */
+ }
+
+ .. tab-item:: Go
+ :sync: go
+
+ Loading a driver in Go is similar:
+
+ .. code-block:: go
+
+ import (
+ "context"
+
+ "github.com/apache/arrow-adbc/go/adbc"
+ "github.com/apache/arrow-adbc/go/adbc/drivermgr"
+ )
+
+ func main() {
+ var drv drivermgr.Driver
+ db, err := drv.NewDatabase(map[string]string{
+ "driver": "/path/to/libadbc_driver.so",
+ })
+ if err != nil {
+ // handle error
+ }
+ defer db.Close()
+
+ // ... do stuff
+ }
+
+ .. tab-item:: Python
+ :sync: python
+
+ You can use the ``DBAPI`` interface as follows:
+
+ .. code-block:: python
+
+ import adbc_driver_manager
+
+ with
adbc_driver_manager.dbapi.connect(driver="/path/to/libadbc_driver.so") as conn:
+ # use the connection
+ pass
+
+ .. tab-item:: R
+ :sync: r
+
+ You can use the ``DBAPI`` interface as follows:
+
+ .. code-block:: r
+
+ library(adbcdrivermanager)
+ con <- adbc_driver("/path/to/libadbc_driver.so") |>
+ adbc_database_init(uri = "...") |>
+ adbc_connection_init()
+
+ .. tab-item:: Ruby
+ :sync: ruby
+
+ You can use the ``ADBC::Database`` as follows:
+
+ .. code-block:: ruby
+
+ require "adbc"
+
+ ADBC::Database.open(driver: "/path/to/libadbc_driver.so") do
|database|
+ # use the database
+ end
+
+ .. tab-item:: Rust
+ :sync: rust
+
+ Rust has a ``ManagedDriver`` type with static methods for loading
drivers:
+
+ .. code-block:: rust
+
+ use adbc_core::options::AdbcVersion;
+ use adbc_core::driver_manager::ManagedDriver;
+
+ fn get_driver() -> ManagedDriver {
+
ManagedDriver::load_dynamic_from_name("/path/to/libadbc_driver.so", None,
AdbcVersion::V100).unwrap()
+ }
+
+In addition to passing the full path to the dynamic library, you can also pass
the
+name of the dynamic library if it is on your ``LD_LIBRARY_PATH``. Such as
using ``adbc_driver``
+instead of ``/path/to/libadbc_driver.so``.
+
+However, the requirement to having the path to the dynamic library or having it
+on your ``LD_LIBRARY_PATH`` can prove difficult for ensuring security,
reproducibility,
+and ease of use. For this reason, there is the concept of a driver manifest.
+
+Driver Manifests
+================
+
+A ``driver manifest`` is a `TOML`_ file that contains both metadata about the
driver along with the location
+of the shared library to load. The driver manager can then locate the
manifest and utilize it to load the
+driver if it was given the shared library path directly. This allows for more
portable installations of
+drivers, and sharing of configurations. Tools can even be created and written
to automatically manage driver
+installations.
+
+.. _TOML: https://toml.io/en/
+
+Manifest Structure
+------------------
+
+While most of the keys are optional, we define a set of keys and structure
which are expected to be present in
+a driver manifest. This provides for consistent handling of manifests by the
driver manager implementations and
+by tools that may be written to manage driver installations.
+
+Below is an example of a driver manifest:
+
+.. code-block:: toml
+
+ name = 'Driver Display Name'
+ version = '1.0.0' # driver version
+ publisher = 'string to identify the publisher'
+ license = 'Apache-2.0' # or otherwise
+ url = 'https://example.com' # URL with more info about the driver
+ # such as a github link or documentation.
+
+ [ADBC]
+ version = '1.1.0' # Maximum supported ADBC spec version
+
+ [ADBC.features]
+ supported = [] # list of strings such as 'bulk insert'
+ unsupported = [] # list of strings such as 'async'
+
+ [Driver]
+ entrypoint = 'AdbcDriverInit' # entrypoint to use if not using default
+ # You can provide just a single path
+ # shared = '/path/to/libadbc_driver.so'
+
+ # or you can provide platform-specific paths for scenarios where the driver
+ # is distributed with multiple platforms supported by a single package.
+ [Driver.shared]
+ # paths to shared libraries to load based on platform tuple
+ linux_amd64 = '/path/to/libadbc_driver.so'
+ osx_amd64 = '/path/to/libadbc_driver.dylib'
+ windows_amd64 = 'C:\\path\\to\\adbc_driver.dll'
+ # ... other platforms as needed
+
+In general, the only *required* key is the ``Driver.shared`` key, which must
exist and must either be
+a string (single path) or a table of platform-specific paths. The
``Driver.shared`` key is the only key
+needed to successfully load a driver manifest. The other keys are optional,
but provide useful metadata
+about the driver.
+
+Manifest Location and Discovery
+-------------------------------
+
+When the driver manager is provided a driver name to load, there is defined
behavior for how it will attempt
+to locate the driver to load. This defined behavior will allow for consistent
behavior across different
+implementations of the driver manager and bindings, while also providing for
flexibility in how drivers are installed.
+
+Given the name of a driver, the name first has to be resolved to either a
dynamic library to load, or a driver manifest
+that contains the path to the dynamic library to load. The following flowchart
describes how this resolution is done:
+
+.. figure:: manifest_load.mmd.svg
+
+ Driver manager attempting to resolve the passed in driver name
+
+Thus, if the driver name is a path to a file the driver manager will attempt
to load that file directly. If there's no
+extension provided, it will first look for a file with a ``.toml`` extension,
and if that fails, it will look for the
+extension appropriate to the platform being used (e.g., ``.so`` for Linux,
``.dylib`` for macOS, and ``.dll`` for Windows).
+
+.. note:: If the driver name is a relative path, it will be resolved relative
to the current working directory. As such, for security
+ reasons, this needs to be explicitly enabled by an option to enable
relative paths, otherwise it will produce an error instead.
+
+As you can see in the flowchart, if the driver name is a string which does not
have an extension and is not a file path, the
+driver manager will then search for a corresponding manifest file, before
falling back seeing if ``LD_LIBRARY_PATH`` can find
+a library with the name provided. Searching for a manifest file is done by
looking for a file with the name provided, but with
+a ``.toml`` extension (e.g. if you pass ``sqlite`` as the driver name, it will
look for ``sqlite.toml``). Options are provided
+to control which directories will be searched for manifests, with the behavior
being slightly different based on the platform.
+
+.. tab-set::
+
+ .. tab-item:: C/C++
+ :sync: cpp
+
+ The type :c:type:`AdbcLoadFlags` is a set of bitflags to control the
directories to be searched. The flags are
+ * :c:macro:`ADBC_LOAD_FLAG_SEARCH_ENV` - search the environment
variable ``ADBC_CONFIG_PATH``
+ * :c:macro:`ADBC_LOAD_FLAG_SEARCH_USER` - search the user
configuration directory
+ * :c:macro:`ADBC_LOAD_FLAG_SEARCH_SYSTEM` - search the system
configuration directory
+ * :c:macro:`ADBC_LOAD_FLAG_ALLOW_RELATIVE_PATHS` - allow a relative
path to be provided
+ * :c:macro:`ADBC_LOAD_FLAG_DEFAULT` - default value with all flags set
+
+ These can either be provided to :c:func:`AdbcFindLoadDriver` or by
using :c:func:`AdbcDriverManagerDatabaseSetLoadFlags`.
+
+ .. tab-item:: GLib
+ :sync: glib
+
+ The type ``GADBCLoadFlags`` is a set of bitflags to control the
directories to be searched. The flags are
+ * ``GADBC_LOAD_SEARCH_ENV` - search the environment variable
``ADBC_CONFIG_PATH``
+ * ``GADBC_LOAD_FLAG_SEARCH_USER`` - search the user configuration
directory
+ * ``GADBC_LOAD_FLAG_SEARCH_SYSTEM`` - search the system configuration
directory
+ * ``GADBC_LOAD_FLAG_ALLOW_RELATIVE_PATHS`` - allow a relative path to
be provided
+ * ``GADBC_LOAD_FLAG_DEFAULT`` - default value with all flags set
+
+ These can be provided by using ``gadbc_database_set_load_flags()``.
+
+ .. tab-item:: Go
+ :sync: go
+
+ The ``drivermgr`` package by default will use the default load flags,
which enable searching the environment variable, user
+ configuration directory, and system configuration directory. You can
set the flags to use by passing the option
+ ``drivermgr.LoadFlagsOptionKey`` with the value being the
``strconv.Itoa`` of the flags you want to use when you call ``NewDatabase``
+ or ``NewDatabaseWithContext``. The flags are defined in the
``drivermgr`` package as constants:
+ * ``drivermgr.LoadFlagsSearchEnv`` - search the environment variable
``ADBC_CONFIG_PATH``
+ * ``drivermgr.LoadFlagsSearchUser`` - search the user configuration
directory
+ * ``drivermgr.LoadFlagsSearchSystem`` - search the system
configuration directory
+ * ``drivermgr.LoadFlagsAllowRelativePaths`` - allow a relative path to
be used
+ * ``drivermgr.LoadFlagsDefault`` - default value with all flags set
+
+ .. tab-item:: Python
+ :sync: python
+
+ Passing the option ``load_flags`` as an option to ``AdbcDatabase`` (or
via ``db_kwargs`` in ``adbc_driver_manager.dbapi.connect``) will
+ allow you to control the directories to be searched by using the value
of the option as the bitmask for the load flag desired.
+
+ .. tab-item:: R
+ :sync: r
+
+ Use ``adbc_driver(... , load_flags = adbc_load_flags())`` to pass
options to the driver manager
+ regarding how to locate drivers specified by manifest.
+
+ .. tab-item:: Ruby
+ :sync: ruby
+
+ The class ``ADBC::LoadFlags`` is a set of bitflags to control the
directories to be searched. The flags are
+ * ``ADBC::LoadFlags::SEARCH_ENV` - search the environment variable
``ADBC_CONFIG_PATH``
+ * ``ADBC::LoadFlags::SEARCH_USER`` - search the user configuration
directory
+ * ``ADBC::LoadFlags::SEARCH_SYSTEM`` - search the system configuration
directory
+ * ``ADBC::LoadFlags::ALLOW_RELATIVE_PATHS`` - allow a relative path to
be provided
+ * ``ADBC::LoadFlags::DEFAULT`` - default value with all flags set
+
+ These can be provided by using ``ADBC::Database#load_flags=``.
+ Passing the option ``load_flags`` as an option to ``AdbcDatabase`` (or
via ``db_kwargs`` in ``adbc_driver_qmanager.dbapi.connect``) will
+ allow you to control the directories to be searched by using the value
of the option as the bitmask for the load flag desired.
+
+ .. tab-item:: Rust
+ :sync: rust
+
+ The ``ManagedDriver`` type has a method ``load_dynamic_from_name``
which takes an optional ``load_flags`` parameter. The flags as a ``u32`` with
+ the type ``adbc_core::driver_manager::LoadFlags``, which has the
following constants:
+ * `LOAD_FLAG_SEARCH_ENV` - search the environment variable
``ADBC_CONFIG_PATH``
+ * `LOAD_FLAG_SEARCH_USER` - search the user configuration directory
+ * `LOAD_FLAG_SEARCH_SYSTEM` - search the system configuration directory
+ * `LOAD_FLAG_ALLOW_RELATIVE_PATHS` - allow a relative path to be used
+ * `LOAD_FLAG_DEFAULT` - default value with all flags set
+
+For unix-like platforms, (e.g. Linux, macOS), the driver manager will search
the following directories based on the options provided, in
+the given order:
+
+#. If the ``LOAD_FLAG_SEARCH_ENV`` load option is set, then the environment
variable ``ADBC_CONFIG_PATH`` will be searched
+
+ * ``ADBC_CONFIG_PATH`` is a colon-separated list of directories to search
for ``${name}.toml``
+
+#. If the ``LOAD_FLAG_SEARCH_USER`` load option is set, then a user-level
configuration directory will be searched
+
+ * On macOS, this will be ``~/Library/Application Support/ADBC``
+ * On Linux (and other unix-like platforms), the ``XDG_CONFIG_HOME``
environment variable is checked first. If it is set, the driver manager
+ will search ``$XDG_CONFIG_HOME/adbc``, otherwise it will search
``~/.config/adbc``
+
+#. If the ``LOAD_FLAG_SEARCH_SYSTEM`` load option is set, the driver manager
will search ``/etc/adbc`` if it exists
+
+Things are slightly different on Windows, where the driver manager will also
search for driver information in the registry just as
+would happen for ODBC drivers. The search for a manifest on windows would be
the following:
+
+#. If the ``LOAD_FLAG_SEARCH_ENV`` load option is set, then the environment
variable ``ADBC_CONFIG_PATH`` will be searched
+
+ * ``ADBC_CONFIG_PATH`` is a semicolon-separated list of directories to
search for ``${name}.toml``
+
+#. If the ``LOAD_FLAG_SEARCH_USER`` load option is set, then a user-level
configuration is searched for
+
+ * First, the registry is searched for the key
``HKEY_CURRENT_USER\SOFTWARE\ADBC\Drivers\${name}``. If it exists, then the
following sub-keys
+ are used:
+
+ * ``name`` - the display name of the driver
+ * ``version`` - the version of the driver
+ * ``source`` - the source of the driver
+ * ``entrypoint`` - the entrypoint to use for the driver if a non-default
entrypoint is needed
+ * ``driver`` - the path to the driver shared library
+
+ * If no registry key is found, then the directory
``%LOCAL_APPDATA%\ADBC\drivers`` is searched for ``${name}.toml``
+
+#. If the ``LOAD_FLAG_SEARCH_SYSTEM`` load option is set, the driver manager
will search for a system-level configuration
+
+ * The registry is searched for the key
``HKEY_LOCAL_MACHINE\SOFTWARE\ADBC\Drivers\${name}``. If it exists, then the
same sub-keys
+ as above are used.
diff --git a/docs/source/format/manifest_load.mmd
b/docs/source/format/manifest_load.mmd
new file mode 100644
index 000000000..16de2161f
--- /dev/null
+++ b/docs/source/format/manifest_load.mmd
@@ -0,0 +1,43 @@
+%% 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.
+
+---
+// [MermaidChart: 9800b551-3a02-4ce5-af92-c6d25d464d2b]
+config:
+ theme: neo-dark
+ layout: dagre
+---
+flowchart TD
+ Start["Driver name"] -- Has Extension --> R{"Is Relative Path?"}
+ Start -- No Extension --> Abs{{"Is Absolute Path?"}}
+ R -- Yes --> DR{{"Disallow Relative Path?"}}
+ DR -- Yes --> Err("fa:fa-exclamation-triangle Error")
+ DR -- No --> Ext{"Extension"}
+ R -- No --> Ext
+ Ext -- ".toml" --> LM[["fa:fa-file-text Load Manifest"]]
+ Ext -- Other --> LD("fa:fa-file Attempt to Load Driver")
+ LM -- "Driver.shared" --> LD
+ Abs -- No --> S[["Search for Manifest"]]
+ Abs -- Yes --> L{{"Check for {name}.toml"}}
+ S -- Found --> LM
+ L -- exists --> LM
+ L -- does not exist --> LD
+ Start@{ shape: manual-input}
+ style Start color:#FFFFFF, fill:#AA00FF, stroke:#AA00FF
+ style Err fill:#FFCDD2, stroke:#B71C1C, color:#B71C1C
+ style LD color:#FFFFFF, stroke:#00C853, fill:#00C853
+ style S color:#FFFFFF, stroke:#FFFFFF, fill:#2962FF
diff --git a/docs/source/format/manifest_load.mmd.svg
b/docs/source/format/manifest_load.mmd.svg
new file mode 100644
index 000000000..9960ba89e
--- /dev/null
+++ b/docs/source/format/manifest_load.mmd.svg
@@ -0,0 +1 @@
+<svg id="export-svg" width="100%" xmlns="http://www.w3.org/2000/svg"
class="flowchart" style="max-width: 800.555px; background: rgb(23, 23, 25);"
viewBox="0 0 800.5546875 894.828125" role="graphics-document document"
aria-roledescription="flowchart-v2"><style
xmlns="http://www.w3.org/1999/xhtml">@import
url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css");
p {margin:
0;}</style><style>#export-svg{font-family:arial,sans-serif;font-size:14px;fill:#ccc;}@keyframe
[...]
diff --git a/docs/source/index.rst b/docs/source/index.rst
index e9276e14e..408bd44ad 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -255,6 +255,7 @@ Why ADBC?
format/versioning
format/comparison
format/how_manager
+ format/driver_manifests
format/related_work
.. toctree::