跳到主要内容

C++ API

Goose C++ API 的最新稳定版本为 Galileo

警告:Goose 的 C++ API 属于内部接口。 它不保证稳定,可能在无通知情况下变更。 若你要基于 Goose 构建应用,建议使用 C API

安装

Goose C++ API 可作为 libgoose 包的一部分安装。详情请参阅安装页面

基础 API 用法

Goose 提供一套自定义 C++ API。其核心抽象包括:数据库实例(Goose 类)、到该实例的多个 Connection,以及查询结果 QueryResult。C++ API 头文件为 goose/goose.h

启动与关闭

要使用 Goose,首先需通过构造函数初始化 Goose 实例。Goose() 的参数是要读写的数据库文件路径。传入特殊值 nullptr 可创建内存数据库。注意内存数据库不会持久化到磁盘(即进程结束后数据全部丢失)。Goose 构造函数第二个参数是可选的 DBConfig 对象,可配置读写模式、内存限制等数据库参数。若数据库文件不可用,Goose 构造函数可能抛出异常。

有了 Goose 实例后,可通过 Connection() 构造一个或多个连接。连接本身应是线程安全的,但查询期间会加锁。因此在多线程环境下,建议每个线程使用独立连接。

Goose db(nullptr);
Connection con(db);

查询

连接通过 Query() 方法从 C++ 向 Goose 发送 SQL 查询字符串。Query() 返回前会将结果完整物化为内存中的 MaterializedQueryResult,之后即可消费结果。Goose 也提供查询流式 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 goose::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 字段串联访问。

Goose 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);

警告:不要用预编译语句向 Goose 插入大量数据。更好的方案请参阅数据导入文档

UDF API

UDF API 支持定义用户自定义函数。goose::Connection 通过 CreateScalarFunction()CreateVectorizedFunction() 及其变体暴露该能力。 这些方法创建的 UDF 位于所属连接的临时 schema(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 函数指针。

该方法会根据模板类型自动推断对应 LogicalType:

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

在 Goose 中,某些基础类型(如 int32_t)可能映射到同一个 LogicalType(如 INTEGERTIMEDATE)。若需消歧,可使用下述重载方法。

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

该函数会校验模板类型与传入 LogicalType 的对应关系,必须满足:

  • 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_funcvectorized UDF 函数。
  • varargs:可变参数类型;若函数不支持可变参数,则使用 LogicalTypeId::INVALID(默认)。

该方法会根据模板类型自动推断对应 LogicalType:

  • boolLogicalType::BOOLEAN
  • int8_tLogicalType::TINYINT
  • int16_tLogicalType::SMALLINT
  • int32_tLogicalType::INTEGER
  • int64_tLogicalType::BIGINT
  • floatLogicalType::FLOAT
  • doubleLogicalType::DOUBLE
  • string_tLogicalType::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)