⌘+k ctrl+k
1.1.3 (stable)
Search Shortcut cmd + k | ctrl + k
C++ API

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

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

  • 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: 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)