快速入门

示例源码: quickstart.cc

在这里,我们将简要介绍如何使用 C++17 中的 SQLite 驱动程序来了解 ADBC 的基本功能。

安装

本快速入门实际上是一个可读的 C++ 文件。您可以克隆该存储库,构建示例,并跟随操作。

我们假设您使用 conda-forge 来管理依赖项。需要 CMake、C++17 编译器和 ADBC 库。可以通过以下方式进行安装

mamba install cmake compilers libadbc-driver-manager libadbc-driver-sqlite

构建

我们在这里使用 CMake。从 ADBC 存储库的源代码检出目录开始

mkdir build
cd build
cmake ../docs/source/cpp/recipe
cmake --build . --target recipe-quickstart
./recipe-quickstart

使用 ADBC

让我们先从一些包含文件开始

59// For EXIT_SUCCESS
60#include <cstdlib>
61// For strerror
62#include <cstring>
63#include <iostream>
64
65#include <arrow-adbc/adbc.h>
66#include <nanoarrow.h>

然后添加一些(非常基础的)错误检查辅助函数。

 70// Error-checking helper for ADBC calls.
 71// Assumes that there is an AdbcError named `error` in scope.
 72#define CHECK_ADBC(EXPR)                                          \
 73  if (AdbcStatusCode status = (EXPR); status != ADBC_STATUS_OK) { \
 74    if (error.message != nullptr) {                               \
 75      std::cerr << error.message << std::endl;                    \
 76    }                                                             \
 77    return EXIT_FAILURE;                                          \
 78  }
 79
 80// Error-checking helper for ArrowArrayStream.
 81#define CHECK_STREAM(STREAM, EXPR)                            \
 82  if (int status = (EXPR); status != 0) {                     \
 83    std::cerr << "(" << std::strerror(status) << "): ";       \
 84    const char* message = (STREAM).get_last_error(&(STREAM)); \
 85    if (message != nullptr) {                                 \
 86      std::cerr << message << std::endl;                      \
 87    } else {                                                  \
 88      std::cerr << "(no error message)" << std::endl;         \
 89    }                                                         \
 90    return EXIT_FAILURE;                                      \
 91  }
 92
 93// Error-checking helper for Nanoarrow.
 94#define CHECK_NANOARROW(EXPR)                                              \
 95  if (int status = (EXPR); status != 0) {                                  \
 96    std::cerr << "(" << std::strerror(status) << "): failed" << std::endl; \
 97    return EXIT_FAILURE;                                                   \
 98  }
 99
100int main() {

加载驱动程序

我们将使用驱动程序管理器(driver manager)加载 SQLite 驱动程序。通过这种方式,我们无需显式链接到驱动程序。

107  AdbcError error = {};
108
109  AdbcDatabase database = {};
110  CHECK_ADBC(AdbcDatabaseNew(&database, &error));

驱动程序管理器确定我们所需驱动程序的方式是通过 driver 选项。

113  CHECK_ADBC(AdbcDatabaseSetOption(&database, "driver", "adbc_driver_sqlite", &error));
114  CHECK_ADBC(AdbcDatabaseInit(&database, &error));

创建连接

ADBC 区分“数据库 (database)”、“连接 (connection)”和“语句 (statement)”。一个“数据库”持有跨多个连接共享的状态。例如,在 SQLite 驱动程序中,它持有 SQLite 的实际实例。一个“连接”是指到数据库的单一连接。

125  AdbcConnection connection = {};
126  CHECK_ADBC(AdbcConnectionNew(&connection, &error));
127  CHECK_ADBC(AdbcConnectionInit(&connection, &database, &error));

创建语句

语句允许我们执行查询。它们既可用于预处理查询,也可用于非预处理(“即席”)查询。

135  AdbcStatement statement = {};
136  CHECK_ADBC(AdbcStatementNew(&connection, &statement, &error));

执行查询

我们通过在语句上设置查询,然后调用 AdbcStatementExecuteQuery() 来执行查询。结果通过 Arrow C 数据接口返回。

147  struct ArrowArrayStream stream = {};
148  int64_t rows_affected = -1;
149
150  CHECK_ADBC(AdbcStatementSetSqlQuery(&statement, "SELECT 42 AS THEANSWER", &error));
151  CHECK_ADBC(AdbcStatementExecuteQuery(&statement, &stream, &rows_affected, &error));

虽然 API 提供了行数,但 SQLite 驱动程序实际上无法提前知道结果集中的具体行数,因此该值实际上将仅为 -1,以表明该值未知。

157  std::cout << "Got " << rows_affected << " rows" << std::endl;

我们需要一个 Arrow 实现来读取实际结果。为此,我们可以使用 Arrow C++Nanoarrow。为了简单起见,我们在这里使用 Nanoarrow。(此示例的 CMake 配置会在构建过程中自动下载并编译 Nanoarrow 源码。)

首先,我们将获取数据的模式(schema)

170  ArrowSchema schema = {};
171  CHECK_STREAM(stream, stream.get_schema(&stream, &schema));

然后我们可以使用 Nanoarrow 将其打印出来

174  char buf[1024] = {};
175  ArrowSchemaToString(&schema, buf, sizeof(buf), /*recursive=*/1);
176  std::cout << "Result schema: " << buf << std::endl;

现在我们可以读取数据了。数据以 Arrow 记录批次(record batches)流的形式返回。

182  while (true) {
183    ArrowArray batch = {};
184    CHECK_STREAM(stream, stream.get_next(&stream, &batch));
185
186    if (batch.release == nullptr) {
187      // Stream has ended
188      break;
189    }

我们也可以使用 Nanoarrow 打印出数据。

192    ArrowArrayView view = {};
193    CHECK_NANOARROW(ArrowArrayViewInitFromSchema(&view, &schema, nullptr));
194    CHECK_NANOARROW(ArrowArrayViewSetArray(&view, &batch, nullptr));
195    std::cout << "Got a batch with " << batch.length << " rows" << std::endl;
196    for (int64_t i = 0; i < batch.length; i++) {
197      std::cout << "THEANSWER[" << i
198                << "] = " << view.children[0]->buffer_views[1].data.as_int64[i]
199                << std::endl;
200    }
201    ArrowArrayViewReset(&view);
202  }
203  // Output:
204  // Got a batch with 1 rows
205  // THEANSWER[0] = 42
206
207  stream.release(&stream);

清理

最后,我们必须释放所有的资源。

213  CHECK_ADBC(AdbcStatementRelease(&statement, &error));
214  CHECK_ADBC(AdbcConnectionRelease(&connection, &error));
215  CHECK_ADBC(AdbcDatabaseRelease(&database, &error));
216  return EXIT_SUCCESS;
217}
标准输出
Got -1 rows
Result schema: struct<THEANSWER: int64>
Got a batch with 1 rows
THEANSWER[0] = 42