DuckDB C++ API 的最新版本是 1.3.2。
警告 DuckDB 的 C++ 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()
方法将 SQL 查询字符串从 C++ 发送到 DuckDB。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
中可用。如果成功,则设置其他字段:刚执行的语句类型(例如,StatementType::INSERT_STATEMENT
)包含在statement_type
中。结果集列的高级(“逻辑类型”/“SQL 类型”)类型在types
中。结果列的名称在names
字符串向量中。如果返回多个结果集,例如因为结果集包含多个语句,则可以使用next
字段链接结果集。
DuckDB 还通过 Prepare()
方法支持 C++ API 中的预处理语句。此方法返回一个 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()
和变体。这些方法在所有者连接的临时模式 (TEMP_SCHEMA
) 中创建 UDF,只有所有者连接才允许使用和更改它们。
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 函数的指针。
此方法从模板类型名称中自动发现相应的逻辑类型
bool
→LogicalType::BOOLEAN
int8_t
→LogicalType::TINYINT
int16_t
→LogicalType::SMALLINT
int32_t
→LogicalType::INTEGER
int64_t
→LogicalType::BIGINT
float
→LogicalType::FLOAT
double
→LogicalType::DOUBLE
string_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:函数使用的逻辑类型参数,应与模板 Args 类型匹配;
- ret_type:函数的逻辑返回类型,应与模板 TR 类型匹配;
- udf_func:指向 UDF 函数的指针。
此函数检查模板类型与作为参数传递的逻辑类型是否匹配,匹配规则如下:
- 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,它包含一组输入向量,这些向量都具有相同的长度;
- 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(默认值)。
此方法从模板类型名称中自动发现相应的逻辑类型
- bool → LogicalType::BOOLEAN;
- int8_t → LogicalType::TINYINT;
- int16_t → LogicalType::SMALLINT
- int32_t → LogicalType::INTEGER
- int64_t → LogicalType::BIGINT
- float → LogicalType::FLOAT
- double → LogicalType::DOUBLE
- string_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)