C++ API
Предупреждение DuckDB's 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-запроса в DuckDB из C++. 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. См. документацию по импорту данных для более эффективных вариантов.
API пользовательских функций
API пользовательских функций позволяет определять пользовательские функции. Он представлен в duckdb:Connection методами: CreateScalarFunction(), CreateVectorizedFunction(), и их вариантами. Эти методы создают пользовательские функции в временной схеме (TEMP_SCHEMA) владельца соединения, которое является единственным, имеющим право использовать и изменять их.
CreateScalarFunction
Пользователь может написать обычную скалярную функцию и вызвать CreateScalarFunction() для регистрации и последующего использования пользовательской функции в инструкции SELECT, например:
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() автоматически создают векторизованные скалярные пользовательские функции, чтобы они были такими же эффективными, как встроенные функции, у нас есть два варианта интерфейса этого метода, как показано ниже:
1.
template<typename TR, typename... Args> void CreateScalarFunction(string name, TR (*udf_func)(Args…))
- параметры шаблона:
- TR — тип возвращаемого значения функции пользовательской функции;
- Args — аргументы до 3 для функции пользовательской функции (этот метод поддерживает только тернарные функции);
- name: имя для регистрации функции пользовательской функции;
- udf_func: указатель на функцию пользовательской функции.
Этот метод автоматически определяет соответствующие типы LogicalTypes по именам шаблонов типов:
-
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 — тип возвращаемого значения функции пользовательской функции;
- Args — аргументы до 3 для функции пользовательской функции (этот метод поддерживает только тернарные функции);
- name: имя для регистрации функции пользовательской функции;
- args: аргументы LogicalType, которые использует функция, которые должны соответствовать типам шаблона Args;
- ret_type: тип LogicalType возврата функции, который должен соответствовать типу шаблона TR;
- udf_func: указатель на функцию пользовательской функции.
Эта функция проверяет соответствие типов шаблонов переданным типам 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() регистрируют векторизованную пользовательскую функцию, например:
/*
* 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(); Векторизованная пользовательская функция — это указатель типа scalar_function_t:
typedef std::function<void(DataChunk &args, ExpressionState &expr, Vector &result)> scalar_function_t;
- args — DataChunk, который содержит набор входных векторов для пользовательской функции, у всех которых одинаковая длина;
- expr — ExpressionState, предоставляющий информацию о состоянии выражения запроса;
- result: Vector для хранения значений результата.
Существуют различные типы векторов для обработки в векторизованной пользовательской функции:
- ConstantVector;
- DictionaryVector;
- FlatVector;
- ListVector;
- StringVector;
- StructVector;
- SequenceVector.
Общий API метода CreateVectorizedFunction() выглядит следующим образом:
1.
template<typename TR, typename... Args> void CreateVectorizedFunction(string name, scalar_function_t udf_func, LogicalType varargs = LogicalType::INVALID)
- параметры шаблона:
- TR — тип возвращаемого значения функции пользовательской функции;
- Args — аргументы до 3 для функции пользовательской функции.
- name — имя для регистрации функции пользовательской функции;
- udf_func — векторизованная функция пользовательской функции;
- varargs Тип аргументов varargs для поддержки, или LogicalTypeId::INVALID (значение по умолчанию), если функция не принимает аргументы переменной длины.
Этот метод автоматически определяет соответствующие типы LogicalTypes по именам шаблонов типов:
- 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)
© Copyright 2018–2024 Stichting DuckDB Foundation
Licensed under the MIT License.
https://duckdb.org/docs/api/cpp.html