最新稳定版的 DuckDB C++ API 为 1.5.0。
警告:DuckDB 的 C++ API 为内部 API。它不保证稳定性,且可能随时更改,恕不另行通知。如果您希望基于 DuckDB 构建应用程序,我们建议使用 C API。
安装
DuckDB C++ API 可以作为 libduckdb 软件包的一部分进行安装。详细信息请参阅安装页面。
基本 API 用法
DuckDB 实现了一个自定义的 C++ API。它围绕数据库实例(DuckDB 类)、针对该数据库实例的多个 Connection(连接)以及作为查询结果的 QueryResult 实例进行抽象构建。C++ API 的头文件为 duckdb.hpp。
启动与关闭
要使用 DuckDB,必须首先使用构造函数初始化一个 DuckDB 实例。DuckDB() 的参数是用于读写的数据库文件。特殊值 nullptr 可用于创建内存数据库。请注意,内存数据库的数据不会持久化到磁盘(即当您退出进程时,所有数据都会丢失)。DuckDB 构造函数的第二个参数是一个可选的 DBConfig 对象。在 DBConfig 中,您可以设置各种数据库参数,例如读写模式或内存限制。DuckDB 构造函数可能会抛出异常,例如当数据库文件不可用时。
利用 DuckDB 实例,您可以使用 Connection() 构造函数创建一个或多个 Connection 实例。虽然连接应当是线程安全的,但它们在查询期间会被锁定。因此,如果您处于多线程环境中,建议每个线程使用自己的连接。
DuckDB db(nullptr);
Connection con(db);
查询
连接提供了 Query() 方法,用于从 C++ 向 DuckDB 发送 SQL 查询字符串。Query() 会在返回之前将查询结果完全实例化为内存中的 MaterializedQueryResult,此时即可使用该查询结果。此外还有用于查询的流式 API,详见下文。
// create a table
con.Query("CREATE TABLE integers (i INTEGER, j INTEGER)");
// insert three rows into the table
con.Query("INSERT INTO integers VALUES (3, 4), (5, 6), (7, NULL)");
auto result = con.Query("SELECT * FROM integers");
if (result->HasError()) {
cerr << result->GetError() << endl;
} else {
cout << result->ToString() << endl;
}
MaterializedQueryResult 实例首先包含两个字段,用以指示查询是否成功。在正常情况下,Query 不会抛出异常。相反,无效的查询或其他问题会导致查询结果实例中的 success 布尔字段被设为 false。在这种情况下,错误消息可能会以字符串形式出现在 error 字段中。对于任何 QueryResult 实例,还可以使用 GetErrorType() 和 GetErrorObject() 方法,这些方法有助于更明确地处理错误。
auto result = con.Query("INSERT INTO integers VALUES (1, 2)");
if (result->HasError()) {
auto errorType = result->GetErrorType();
switch (errorType) {
case duckdb::ExceptionType::CONSTRAINT: {
// Example handling
auto errorObject = result->GetErrorObject();
errorObject.ConvertErrorToJSON();
std::cout << errorObject.Message() << std::endl;
break;
}
// More handling
}
} else {
// Normal code
}
如果执行成功,其他字段将被设置:刚执行的语句类型(例如 StatementType::INSERT_STATEMENT)包含在 statement_type 中。结果集列的高级类型(“逻辑类型”/“SQL 类型”)位于 types 中。结果列的名称位于 names 字符串向量中。如果返回多个结果集(例如因为结果集包含多条语句),则可以使用 next 字段链接结果集。
DuckDB 的 C++ API 还通过 Prepare() 方法支持预处理语句。该方法返回一个 PreparedStatement 实例。该实例可用于执行带参数的预处理语句。以下是一个示例。
std::unique_ptr<PreparedStatement> prepare = con.Prepare("SELECT count(*) FROM a WHERE i = $1");
std::unique_ptr<QueryResult> result = prepare->Execute(12);
警告:请不要使用预处理语句将大量数据插入 DuckDB。请查看数据导入文档以获取更好的方案。
UDF API
UDF API 允许定义用户自定义函数。它在 duckdb::Connection 中通过以下方法公开:CreateScalarFunction()、CreateVectorizedFunction() 及其变体。这些方法将 UDF 创建在所属连接的临时模式(TEMP_SCHEMA)中,只有该连接被允许使用和更改它们。
CreateScalarFunction
用户可以编写普通的标量函数,并调用 CreateScalarFunction() 进行注册,之后即可在 SELECT 语句中使用该 UDF,例如:
bool bigger_than_four(int value) {
return value > 4;
}
connection.CreateScalarFunction<bool, int>("bigger_than_four", &bigger_than_four);
connection.Query("SELECT bigger_than_four(i) FROM (VALUES (3), (5)) tbl(i)")->Print();
CreateScalarFunction() 方法会自动创建向量化标量 UDF,使其像内置函数一样高效。该方法接口有两种变体,如下所示:
1.
template<typename TR, typename... Args>
void CreateScalarFunction(string name, TR (*udf_func)(Args…))
- 模板参数
- TR 是 UDF 函数的返回类型。
- Args 是 UDF 函数的参数(最多支持 3 个,此方法仅支持到三元函数)。
- name 是注册 UDF 函数时使用的名称。
- udf_func 是指向 UDF 函数的指针。
此方法会自动从模板类型名称中发现相应的 LogicalTypes:
bool→LogicalType::BOOLEANint8_t→LogicalType::TINYINTint16_t→LogicalType::SMALLINTint32_t→LogicalType::INTEGERint64_t→LogicalType::BIGINTfloat→LogicalType::FLOATdouble→LogicalType::DOUBLEstring_t→LogicalType::VARCHAR
在 DuckDB 中,一些原始类型(例如 int32_t)会被映射到相同的 LogicalType:INTEGER、TIME 和 DATE。为了消除歧义,用户可以使用以下重载方法。
2.
template<typename TR, typename... Args>
void CreateScalarFunction(string name, vector<LogicalType> args, LogicalType ret_type, TR (*udf_func)(Args…))
使用示例如下:
int32_t udf_date(int32_t a) {
return a;
}
con.Query("CREATE TABLE dates (d DATE)");
con.Query("INSERT INTO dates VALUES ('1992-01-01')");
con.CreateScalarFunction<int32_t, int32_t>("udf_date", {LogicalType::DATE}, LogicalType::DATE, &udf_date);
con.Query("SELECT udf_date(d) FROM dates")->Print();
- 模板参数
- TR 是 UDF 函数的返回类型。
- Args 是 UDF 函数的参数(最多支持 3 个,此方法仅支持到三元函数)。
- name 是注册 UDF 函数时使用的名称。
- args 是函数使用的 LogicalType 参数,应与模板 Args 类型匹配。
- ret_type 是函数的返回 LogicalType,应与模板 TR 类型匹配。
- udf_func 是指向 UDF 函数的指针。
此函数会根据作为参数传递的 LogicalTypes 检查模板类型,且它们必须满足以下匹配关系:
- LogicalTypeId::BOOLEAN → bool
- LogicalTypeId::TINYINT → int8_t
- LogicalTypeId::SMALLINT → int16_t
- LogicalTypeId::DATE, LogicalTypeId::TIME, LogicalTypeId::INTEGER → int32_t
- LogicalTypeId::BIGINT, LogicalTypeId::TIMESTAMP → int64_t
- LogicalTypeId::FLOAT, LogicalTypeId::DOUBLE, LogicalTypeId::DECIMAL → double
- LogicalTypeId::VARCHAR, LogicalTypeId::CHAR, LogicalTypeId::BLOB → string_t
- LogicalTypeId::VARBINARY → blob_t
CreateVectorizedFunction
CreateVectorizedFunction() 方法用于注册向量化 UDF,例如:
/*
* This vectorized function copies the input values to the result vector
*/
template<typename TYPE>
static void udf_vectorized(DataChunk &args, ExpressionState &state, Vector &result) {
// set the result vector type
result.vector_type = VectorType::FLAT_VECTOR;
// get a raw array from the result
auto result_data = FlatVector::GetData<TYPE>(result);
// get the solely input vector
auto &input = args.data[0];
// now get an orrified vector
VectorData vdata;
input.Orrify(args.size(), vdata);
// get a raw array from the orrified input
auto input_data = (TYPE *)vdata.data;
// handling the data
for (idx_t i = 0; i < args.size(); i++) {
auto idx = vdata.sel->get_index(i);
if ((*vdata.nullmask)[idx]) {
continue;
}
result_data[i] = input_data[idx];
}
}
con.Query("CREATE TABLE integers (i INTEGER)");
con.Query("INSERT INTO integers VALUES (1), (2), (3), (999)");
con.CreateVectorizedFunction<int, int>("udf_vectorized_int", &&udf_vectorized<int>);
con.Query("SELECT udf_vectorized_int(i) FROM integers")->Print();
向量化 UDF 是一个类型为 scalar_function_t 的指针。
typedef std::function<void(DataChunk &args, ExpressionState &expr, Vector &result)> scalar_function_t;
- args 是一个 DataChunk,它持有一组长度相同的 UDF 输入向量。
- expr 是一个 ExpressionState,它提供有关查询表达式状态的信息。
- result 是一个用于存储结果值的 Vector。
在向量化 UDF 中有多种需要处理的向量类型:
- ConstantVector
- DictionaryVector
- FlatVector
- ListVector
- StringVector
- StructVector
- SequenceVector
CreateVectorizedFunction() 方法的通用 API 如下:
1.
template<typename TR, typename... Args>
void CreateVectorizedFunction(string name, scalar_function_t udf_func, LogicalType varargs = LogicalType::INVALID)
- 模板参数
- TR 是 UDF 函数的返回类型。
- Args 是 UDF 函数的参数(最多支持 3 个)。
- name 是注册 UDF 函数时使用的名称。
- udf_func 是一个向量化 UDF 函数。
- varargs:支持的可变参数类型,如果函数不接受可变长参数,则为 LogicalTypeId::INVALID(默认值)。
此方法会自动从模板类型名称中发现相应的 LogicalTypes:
bool→LogicalType::BOOLEANint8_t→LogicalType::TINYINTint16_t→LogicalType::SMALLINTint32_t→LogicalType::INTEGERint64_t→LogicalType::BIGINTfloat→LogicalType::FLOATdouble→LogicalType::DOUBLEstring_t→LogicalType::VARCHAR
2.
template<typename TR, typename... Args>
void CreateVectorizedFunction(string name, vector<LogicalType> args, LogicalType ret_type, scalar_function_t udf_func, LogicalType varargs = LogicalType::INVALID)