⌘+k ctrl+k
1.3 (稳定版)
搜索快捷键 cmd + k | ctrl + k
C++ API

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 函数的指针。

此方法从模板类型名称中自动发现相应的逻辑类型

  • boolLogicalType::BOOLEAN
  • int8_tLogicalType::TINYINT
  • int16_tLogicalType::SMALLINT
  • int32_tLogicalType::INTEGER
  • int64_t LogicalType::BIGINT
  • floatLogicalType::FLOAT
  • doubleLogicalType::DOUBLE
  • string_tLogicalType::VARCHAR

在 DuckDB 中,一些原始类型,例如int32_t,被映射到相同的LogicalTypeINTEGERTIMEDATE。为了消除歧义,用户可以使用以下重载方法。

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)