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:
bool→LogicalType::BOOLEANint8_t→LogicalType::TINYINTint16_t→LogicalType::SMALLINTint32_t→LogicalType::INTEGERint64_t→LogicalType::BIGINTfloat→LogicalType::FLOATdouble→LogicalType::DOUBLEstring_t→LogicalType::VARCHAR
在 Goose 中,某些基础类型(如 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:函数参数 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_func:vectorized UDF 函数。
- varargs:可变参数类型;若函数不支持可变参数,则使用
LogicalTypeId::INVALID(默认)。
该方法会根据模板类型自动推断对应 LogicalType:
bool→LogicalType::BOOLEANint8_t→LogicalType::TINYINTint16_t→LogicalType::SMALLINTint32_t→LogicalType::INTEGERint64_t→LogicalType::BIGINTfloat→LogicalType::FLOATdouble→LogicalType::DOUBLEstring_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)