警告 DuckDB 的 C++ API 是内部的。 它不保证稳定,并且可能会在没有通知的情况下更改。 如果您想在 DuckDB 上构建应用程序,我们建议使用 C API。
Installation
DuckDB C++ API 可以作为 libduckdb
包的一部分进行安装。详情请参阅 安装页面。
Basic API Usage
DuckDB 实现了一个自定义的 C++ API。这是围绕数据库实例的抽象(DuckDB
类)、多个数据库实例的 Connection
和作为查询结果的 QueryResult
实例构建的。C++ API 的头文件是 duckdb.hpp
。
Startup & Shutdown
要使用DuckDB,您必须首先使用其构造函数初始化一个DuckDB
实例。DuckDB()
接受一个参数,即用于读取和写入的数据库文件。特殊值nullptr
可用于创建一个内存数据库。请注意,对于内存数据库,没有数据会持久化到磁盘(即,当您退出进程时,所有数据都会丢失)。DuckDB
构造函数的第二个参数是一个可选的DBConfig
对象。在DBConfig
中,您可以设置各种数据库参数,例如读/写模式或内存限制。DuckDB
构造函数可能会抛出异常,例如如果数据库文件不可用。
使用DuckDB
实例,你可以通过Connection()
构造函数创建一个或多个Connection
实例。虽然连接应该是线程安全的,但在查询期间它们会被锁定。因此,如果你处于多线程环境中,建议每个线程使用自己的连接。
DuckDB db(nullptr);
Connection con(db);
Querying
连接暴露了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
中。如果成功,其他字段将被设置:刚刚执行的语句类型(例如,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函数的指针。
此方法自动从模板类型名中发现相应的逻辑类型:
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: is the name to register the UDF function;
- args: 是函数使用的LogicalType参数,应与模板Args类型匹配;
- ret_type: 是函数的返回逻辑类型,应与模板TR类型匹配;
- udf_func: is a pointer to the UDF function.
此函数检查模板类型与作为参数传递的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;
- 字典向量;
- FlatVector;
- 列表向量;
- StringVector;
- 结构向量;
- 序列向量。
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 支持的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)