ADBC 驱动管理器和清单

注意

本文档介绍了如何使用驱动管理器加载驱动程序。驱动管理器通常不是使用 ADBC 所必需的,但它允许加载用与应用程序不同语言编写的驱动程序,并改善了在单个应用程序中使用多个驱动程序时的体验。有关驱动管理器如何工作的更多信息,请参阅驱动程序和驱动管理器如何协同工作

有两种方法可以使用驱动管理器加载驱动程序

  1. 直接指定要加载的动态库

  2. 引用包含元数据以及要加载的动态库位置的驱动程序清单文件

无论采用哪种方法,您都可以将动态库或驱动程序清单指定为驱动管理器的 driver 选项,或者如果您的驱动管理器库公开了加载驱动程序的显式函数(例如,C++,请参见下面的示例),您也可以使用该函数。

注意

除了 driver 选项,还有一个 入口点 选项,如果驱动程序使用非默认入口点,则应使用此选项。

直接加载驱动程序

通过驱动管理器加载驱动程序最简单的方法是提供动态库的文件路径作为驱动程序名称。

您可以使用驱动管理器的 driver 选项指定要加载的驱动程序,也可以使用 AdbcLoadDriver() 直接加载驱动程序。

struct AdbcDatabase database;
struct AdbcError error;

std::memset(&database, 0, sizeof(database));
std::memset(&error, 0, sizeof(error));

auto status = AdbcDatabaseNew(&database, &error);
// if status != ADBC_STATUS_OK then handle the error

// Load the driver by setting the "driver" option
status = AdbcDatabaseSetOption(&database, "driver", "/path/to/libadbc_driver.so", &error);
// if status != ADBC_STATUS_OK then handle the error

// Alternatively, load directly with AdbcLoadDriver
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

您可以通过 GADBCDatabase 将其用作驱动程序

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 */
}

在 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
}

您可以按如下方式使用 DBAPI 接口

import adbc_driver_manager

with adbc_driver_manager.dbapi.connect(driver="/path/to/libadbc_driver.so") as conn:
    # use the connection
    pass

您可以按如下方式使用 DBAPI 接口

library(adbcdrivermanager)
con <- adbc_driver("/path/to/libadbc_driver.so") |>
  adbc_database_init(uri = "...") |>
  adbc_connection_init()

您可以按如下方式使用 ADBC::Database

require "adbc"

ADBC::Database.open(driver: "/path/to/libadbc_driver.so") do |database|
  # use the database
end

Rust 有一个 ManagedDriver 类型,带有用于加载驱动程序的静态方法

use adbc_core::options::AdbcVersion;
use adbc_core::driver_manager::ManagedDriver;

fn get_driver() -> ManagedDriver {
    ManagedDriver::load_from_name("/path/to/libadbc_driver.so", None, AdbcVersion::V100).unwrap()
}

作为传递动态库完整路径的替代方案,您可能更喜欢使用 LD_LIBRARY_PATH(或类似环境变量,取决于您的操作系统),并且只指定文件名(即 libadbc_driver.so 而不是 /path/to/libadbc_driver.so)。

然而,要求提供动态库的路径或将其放在 LD_LIBRARY_PATH 中可能难以确保安全性、可重现性和易用性。因此,有了驱动程序清单的概念。

驱动程序清单

驱动程序清单 是一个 TOML 文件,其中包含有关驱动程序的元数据以及要加载的共享库的位置。然后,如果驱动管理器直接给出共享库路径,它可以找到清单并使用它来加载驱动程序。这使得驱动程序的安装更具可移植性,并可以共享配置。甚至可以创建和编写工具来自动管理驱动程序安装。

清单结构

虽然大多数键是可选的,但我们定义了一组预期出现在驱动程序清单中的键和结构。这为驱动管理器实现和可能用于管理驱动程序安装的工具提供了对清单的一致处理。

下面是驱动程序清单的一个示例

manifest_version = 1

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

通常,唯一必需的键是 Driver.shared 键,它必须存在并且必须是字符串(单个路径)或平台特定路径的表。Driver.shared 键是成功加载驱动程序清单所需的唯一键。其他键是可选的,但提供了有关驱动程序的有用元数据。

manifest_version 键,如果存在,则必须设置为 1。它默认为 1,目前只能设置为 1。驱动管理器实现在读取 manifest_version 高于 1 的清单时必须报错。

平台元组

由于清单使用平台元组来指定驱动程序可能构建的不同系统,因此创建一种命名这些元组的一致方法非常重要。具体来说,操作系统 (OS) 和架构组合的一致命名可以被所有读取和/或写入清单文件的系统使用。

因此,下表应被视为权威列表

操作系统

元组名称

Linux

linux

macOS

macos

Windows

windows

FreeBSD

freebsd

OpenBSD

openbsd

架构

元组名称

i386

x86

x86

x86

x86-64

amd64

x64

amd64

amd64

amd64

arm (32-bit)

arm

armbe (32-bit)

armbe

arm64be

arm64be

aarch64

arm64

arm64

arm64

s390x

s390x

ppc

powerpc

ppc64

powerpc64

ppc64le

powerpc64le

riscv

riscv

riscv64

riscv64

sparc

sparc

sparc64

sparc64

Wasm (32 位)

wasm32

Wasm (64 位)

wasm64

平台元组的构造是:<OS>_<Architecture>,例如:linux_amd64

注意

对于使用 musl 而不是 GNU C Library (glibc) 或 MinGW 的替代场景,元组应具有该环境的适当后缀。即 linux_amd64_muslwindows_amd64_mingw

清单位置和发现

当驱动管理器获得要加载的驱动程序名称时,它会定义如何尝试定位要加载的驱动程序的行为。这种定义的行为将允许驱动管理器和绑定在不同实现之间保持一致,同时还为驱动程序的安装方式提供了灵活性。

给定驱动程序的名称,该名称首先必须解析为要加载的动态库,或者包含要加载的动态库路径的驱动程序清单。以下流程图描述了如何进行此解析

Flowchart diagram showing the how the driver manager resolves a simple driver name and eventually attempts to load the driver or returns an error.

流程图显示了驱动管理器如何解析简单的驱动程序名称,并最终尝试加载驱动程序或返回错误。

因此,如果驱动程序名称是文件的路径,驱动管理器将尝试直接加载该文件。如果没有提供扩展名,它将首先查找带有 .toml 扩展名的文件,如果失败,它将查找适用于所用平台的扩展名(例如,Linux 为 .so,macOS 为 .dylib,Windows 为 .dll)。

注意

如果驱动程序名称是相对路径,它将相对于当前工作目录解析。因此,出于安全原因,需要通过一个选项显式启用相对路径,否则会产生错误。

如流程图所示,如果驱动程序名称是没有扩展名且不是文件路径的字符串,则驱动程序管理器将首先搜索相应的清单文件,然后退回到查看 LD_LIBRARY_PATH(或您的操作系统中的等效项)是否能找到名称相同的库。搜索清单文件是通过查找具有所提供名称但带有 .toml 扩展名的文件来完成的(例如,如果您将 sqlite 作为驱动程序名称,它将查找 sqlite.toml)。提供了用于控制将搜索哪些目录以查找清单的选项,其行为因平台而异。

类型 AdbcLoadFlags 是一组位标志,用于控制要搜索的目录。这些标志是

这些可以提供给 AdbcFindLoadDriver() 或通过使用 AdbcDriverManagerDatabaseSetLoadFlags()

类型 GADBCLoadFlags 是一组位标志,用于控制要搜索的目录。这些标志是

  • GADBC_LOAD_SEARCH_ENV - 搜索环境变量 ADBC_DRIVER_PATH 中的目录路径,并且(在使用 conda 构建或安装时)搜索 conda 环境

  • GADBC_LOAD_FLAG_SEARCH_USER - 搜索用户配置目录

  • GADBC_LOAD_FLAG_SEARCH_SYSTEM - 搜索系统配置目录

  • GADBC_LOAD_FLAG_ALLOW_RELATIVE_PATHS - 允许提供相对路径

  • GADBC_LOAD_FLAG_DEFAULT - 所有标志都设置的默认值

这些可以通过使用 gadbc_database_set_load_flags() 来提供。

默认情况下,drivermgr 包将使用默认的加载标志,这使得可以搜索环境变量、用户配置目录和系统配置目录。您可以通过传递选项 drivermgr.LoadFlagsOptionKey 并将其值设置为您在调用 NewDatabaseNewDatabaseWithContext 时要使用的标志的 strconv.Itoa 来设置要使用的标志。这些标志在 drivermgr 包中定义为常量

  • drivermgr.LoadFlagsSearchEnv - 搜索环境变量 ADBC_DRIVER_PATH 中的目录路径

  • drivermgr.LoadFlagsSearchUser - 搜索用户配置目录

  • drivermgr.LoadFlagsSearchSystem - 搜索系统配置目录

  • drivermgr.LoadFlagsAllowRelativePaths - 允许使用相对路径

  • drivermgr.LoadFlagsDefault - 所有标志都设置的默认值

将选项 load_flags 作为选项传递给 AdbcDatabase(或通过 adbc_driver_manager.dbapi.connect 中的 db_kwargs)将允许您通过使用选项值作为所需加载标志的位掩码来控制要搜索的目录。

使用 adbc_driver(..., load_flags = adbc_load_flags()) 将选项传递给驱动管理器,以指定如何定位清单指定的驱动程序。

ADBC::LoadFlags 是一组位标志,用于控制要搜索的目录。这些标志是

  • ADBC::LoadFlags::SEARCH_ENV - 搜索环境变量 ADBC_DRIVER_PATH 中的目录路径,并且(在使用 conda 构建或安装时)搜索 conda 环境

  • ADBC::LoadFlags::SEARCH_USER - 搜索用户配置目录

  • ADBC::LoadFlags::SEARCH_SYSTEM - 搜索系统配置目录

  • ADBC::LoadFlags::ALLOW_RELATIVE_PATHS - 允许提供相对路径

  • ADBC::LoadFlags::DEFAULT - 所有标志都设置的默认值

这些可以通过使用 ADBC::Database#load_flags= 来提供。将选项 load_flags 作为选项传递给 AdbcDatabase(或通过 adbc_driver_manager.dbapi.connect 中的 db_kwargs)将允许您通过使用选项值作为所需加载标志的位掩码来控制要搜索的目录。

ManagedDriver 类型有一个 load_from_name 方法,它接受一个可选的 load_flags 参数。标志是一个 u32 类型,其类型为 adbc_core::driver_manager::LoadFlags,它具有以下常量

  • LOAD_FLAG_SEARCH_ENV - 搜索环境变量 ADBC_DRIVER_PATH 中的目录路径,并且(在使用 conda 构建或安装时)搜索 conda 环境

  • LOAD_FLAG_SEARCH_USER - 搜索用户配置目录

  • LOAD_FLAG_SEARCH_SYSTEM - 搜索系统配置目录

  • LOAD_FLAG_ALLOW_RELATIVE_PATHS - 允许使用相对路径

  • LOAD_FLAG_DEFAULT - 所有标志都设置的默认值

类 Unix 平台

对于类 Unix 平台(例如 Linux、macOS),驱动管理器将根据提供的选项,按给定顺序搜索以下目录

  1. 如果设置了 LOAD_FLAG_SEARCH_ENV 加载选项,则将搜索环境变量 ADBC_DRIVER_PATH 中的路径

    • ADBC_DRIVER_PATH 是一个以冒号分隔的目录列表

  2. 如果指定了额外的搜索路径,则会搜索这些路径

    • 当在 venv 虚拟环境中运行时,Python 驱动管理器会自动将 $VIRTUAL_ENV/etc/adbc/drivers 添加到搜索路径中

  3. 如果驱动管理器使用 conda 构建或安装,并且设置了 LOAD_FLAG_SEARCH_ENV 加载选项,则会搜索 $CONDA_PREFIX/etc/adbc/drivers

  4. 如果设置了 LOAD_FLAG_SEARCH_USER 加载选项,则将搜索用户级配置目录

    • 在 macOS 上,这将是 ~/Library/Application Support/ADBC/Drivers

    • 在 Linux(以及其他类 Unix 平台)上,首先检查 XDG_CONFIG_HOME 环境变量。如果已设置,驱动管理器将搜索 $XDG_CONFIG_HOME/adbc/drivers,否则将搜索 ~/.config/adbc/drivers

  5. 如果设置了 LOAD_FLAG_SEARCH_SYSTEM 加载选项,则将搜索系统级配置目录

    • 在 macOS 上,如果存在,这将是 /Library/Application Support/ADBC/Drivers

    • 在 Linux(以及其他类 Unix 平台)上,如果存在,这将是 /etc/adbc/drivers

Windows

在 Windows 上,情况略有不同,驱动管理器也会在注册表中搜索驱动程序信息,就像 ODBC 驱动程序一样。在 Windows 上搜索清单的顺序如下

  1. 如果设置了 LOAD_FLAG_SEARCH_ENV 加载选项,则将搜索环境变量 ADBC_DRIVER_PATH 中的路径

    • ADBC_DRIVER_PATH 是一个以分号分隔的目录列表

  2. 如果指定了额外的搜索路径,则会搜索这些路径

    • 当在 venv 虚拟环境中运行时,Python 驱动管理器会自动将 $VIRTUAL_ENV\etc\adbc\drivers 添加到搜索路径中

  3. 如果驱动管理器使用 conda 构建或安装,并且设置了 LOAD_FLAG_SEARCH_ENV 加载选项,则会搜索 $CONDA_PREFIX\etc\adbc\drivers

  4. 如果设置了 LOAD_FLAG_SEARCH_USER 加载选项,则会搜索用户级配置

    • 首先,在注册表中搜索键 HKEY_CURRENT_USER\SOFTWARE\ADBC\Drivers\${name}。如果存在,则使用以下子键

      • name - 驱动程序的显示名称

      • version - 驱动程序的版本

      • source - 驱动程序的来源

      • entrypoint - 如果需要非默认入口点,则为驱动程序使用的入口点

      • driver - 驱动程序共享库的路径

    • 如果未找到注册表项,则搜索目录 %LOCAL_APPDATA%\ADBC\Drivers

  5. 如果设置了 LOAD_FLAG_SEARCH_SYSTEM 加载选项,驱动管理器将搜索系统级配置

    • 搜索注册表键 HKEY_LOCAL_MACHINE\SOFTWARE\ADBC\Drivers\${name}。如果存在,则使用与上述相同的子键。