c/driver/sqlite/sqlite.cc (1,248 lines of code) (raw):
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#include <cstdio>
#include <limits>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <arrow-adbc/adbc.h>
#include <sqlite3.h>
#include <nanoarrow/nanoarrow.hpp>
#define ADBC_FRAMEWORK_USE_FMT
#include "driver/framework/base_driver.h"
#include "driver/framework/connection.h"
#include "driver/framework/database.h"
#include "driver/framework/statement.h"
#include "driver/framework/status.h"
#include "driver/sqlite/statement_reader.h"
namespace adbc::sqlite {
using driver::Result;
using driver::Status;
namespace status = adbc::driver::status;
namespace {
constexpr std::string_view kDefaultUri =
"file:adbc_driver_sqlite?mode=memory&cache=shared";
constexpr std::string_view kConnectionOptionEnableLoadExtension =
"adbc.sqlite.load_extension.enabled";
constexpr std::string_view kConnectionOptionLoadExtensionPath =
"adbc.sqlite.load_extension.path";
constexpr std::string_view kConnectionOptionLoadExtensionEntrypoint =
"adbc.sqlite.load_extension.entrypoint";
/// The batch size for query results (and for initial type inference)
constexpr std::string_view kStatementOptionBatchRows = "adbc.sqlite.query.batch_rows";
std::string_view GetColumnText(sqlite3_stmt* stmt, int index) {
return {
reinterpret_cast<const char*>(sqlite3_column_text(stmt, index)),
static_cast<size_t>(sqlite3_column_bytes(stmt, index)),
};
}
class SqliteMutexGuard {
public:
explicit SqliteMutexGuard(sqlite3* conn) : conn_(conn) {
sqlite3_mutex_enter(sqlite3_db_mutex(conn_));
}
~SqliteMutexGuard() {
if (conn_) {
sqlite3_mutex_leave(sqlite3_db_mutex(conn_));
}
conn_ = nullptr;
}
private:
sqlite3* conn_;
};
class SqliteStringBuilder {
public:
SqliteStringBuilder() : str_(sqlite3_str_new(nullptr)) {}
~SqliteStringBuilder() {
// sqlite3_free is no-op on nullptr
sqlite3_free(result_);
result_ = nullptr;
if (str_) {
sqlite3_free(sqlite3_str_finish(str_));
str_ = nullptr;
}
}
void Reset() {
std::ignore = GetString();
sqlite3_free(result_);
result_ = nullptr;
str_ = sqlite3_str_new(nullptr);
}
void Append(std::string_view fmt, ...) {
if (str_) {
va_list args;
va_start(args, fmt);
sqlite3_str_vappendf(str_, fmt.data(), args);
va_end(args);
}
}
Result<std::string_view> GetString() {
int len = 0;
if (!result_) {
if (int rc = sqlite3_str_errcode(str_); rc == SQLITE_NOMEM) {
return status::Internal("out of memory building query");
} else if (rc == SQLITE_TOOBIG) {
return status::Internal("query too long");
} else if (rc != SQLITE_OK) {
return status::fmt::Internal("unknown SQLite error ({})", rc);
}
len = sqlite3_str_length(str_);
result_ = sqlite3_str_finish(str_);
str_ = nullptr;
}
return std::string_view(result_, len);
}
private:
sqlite3_str* str_ = nullptr;
char* result_ = nullptr;
};
class SqliteQuery {
public:
explicit SqliteQuery(sqlite3* conn, std::string_view query)
: conn_(conn), query_(query) {}
Status Init() {
int rc = sqlite3_prepare_v2(conn_, query_.data(), static_cast<int>(query_.size()),
&stmt_, /*pzTail=*/nullptr);
if (rc != SQLITE_OK) {
return Close(rc);
}
return status::Ok();
}
Result<bool> Next() {
if (!stmt_) {
return status::fmt::Internal(
"query already finished or never initialized\nquery was: {}", query_);
}
int rc = sqlite3_step(stmt_);
if (rc == SQLITE_ROW) {
return true;
} else if (rc == SQLITE_DONE) {
return false;
}
return Close(rc);
}
Status Close(int last_rc) {
if (stmt_) {
int rc = sqlite3_finalize(stmt_);
stmt_ = nullptr;
if (rc != SQLITE_OK && rc != SQLITE_DONE) {
return status::fmt::Internal("failed to execute: {}\nquery was: {}",
sqlite3_errmsg(conn_), query_);
}
} else if (last_rc != SQLITE_OK) {
return status::fmt::Internal("failed to execute: {}\nquery was: {}",
sqlite3_errmsg(conn_), query_);
}
return status::Ok();
}
Status Close() { return Close(SQLITE_OK); }
sqlite3_stmt* stmt() const { return stmt_; }
static Status Execute(sqlite3* conn, std::string_view query) {
SqliteQuery q(conn, query);
UNWRAP_STATUS(q.Init());
while (true) {
UNWRAP_RESULT(bool has_row, q.Next());
if (!has_row) break;
}
return q.Close();
}
template <typename BindFunc, typename RowFunc>
static Status Scan(sqlite3* conn, std::string_view query, BindFunc&& bind_func,
RowFunc&& row_func) {
SqliteQuery q(conn, query);
UNWRAP_STATUS(q.Init());
int rc = std::forward<BindFunc>(bind_func)(q.stmt_);
if (rc != SQLITE_OK) return q.Close();
while (true) {
UNWRAP_RESULT(bool has_row, q.Next());
if (!has_row) break;
rc = std::forward<RowFunc>(row_func)(q.stmt_);
if (rc != SQLITE_OK) break;
}
return q.Close();
}
private:
sqlite3* conn_ = nullptr;
std::string_view query_;
sqlite3_stmt* stmt_ = nullptr;
};
constexpr std::string_view kNoFilter = "%";
struct SqliteGetObjectsHelper : public driver::GetObjectsHelper {
explicit SqliteGetObjectsHelper(sqlite3* conn) : conn(conn) {}
Status Load(driver::GetObjectsDepth depth,
std::optional<std::string_view> catalog_filter,
std::optional<std::string_view> schema_filter,
std::optional<std::string_view> table_filter,
std::optional<std::string_view> column_filter,
const std::vector<std::string_view>& table_types) override {
std::string query =
"SELECT DISTINCT name FROM pragma_database_list() WHERE name LIKE ?";
UNWRAP_STATUS(SqliteQuery::Scan(
conn, query,
[&](sqlite3_stmt* stmt) {
auto filter = catalog_filter.value_or(kNoFilter);
return sqlite3_bind_text(stmt, 1, filter.data(),
static_cast<int>(filter.size()), SQLITE_STATIC);
},
[&](sqlite3_stmt* stmt) {
catalogs.emplace_back(GetColumnText(stmt, 0));
return SQLITE_OK;
}));
// SQLite doesn't have schemas, so we assume each catalog has a single
// unnamed schema.
if (!schema_filter.has_value() || schema_filter->empty()) {
schemas = {""};
} else {
schemas = {};
}
return status::Ok();
}
Status LoadCatalogs(std::optional<std::string_view> catalog_filter) override {
return status::Ok();
};
Result<std::optional<std::string_view>> NextCatalog() override {
if (next_catalog >= catalogs.size()) return std::nullopt;
return catalogs[next_catalog++];
}
Status LoadSchemas(std::string_view catalog,
std::optional<std::string_view> schema_filter) override {
next_schema = 0;
return status::Ok();
};
Result<std::optional<std::string_view>> NextSchema() override {
if (next_schema >= schemas.size()) return std::nullopt;
return schemas[next_schema++];
}
Status LoadTables(std::string_view catalog, std::string_view schema,
std::optional<std::string_view> table_filter,
const std::vector<std::string_view>& table_types) override {
next_table = 0;
tables.clear();
if (!schema.empty()) return status::Ok();
SqliteStringBuilder builder;
builder.Append(R"(SELECT name, type FROM "%w" . sqlite_master WHERE name LIKE ?)",
catalog.data());
if (!table_types.empty()) {
builder.Append(" AND (");
bool first = true;
for (const auto& table_type : table_types) {
if (first) {
builder.Append(" type = %Q", table_type.data());
first = false;
} else {
builder.Append(" OR type = %Q", table_type.data());
}
}
builder.Append(" )");
}
UNWRAP_RESULT(auto query, builder.GetString());
return SqliteQuery::Scan(
conn, query,
[&](sqlite3_stmt* stmt) {
auto filter = table_filter.value_or(kNoFilter);
return sqlite3_bind_text(stmt, 1, filter.data(),
static_cast<int>(filter.size()), SQLITE_STATIC);
},
[&](sqlite3_stmt* stmt) {
tables.emplace_back(GetColumnText(stmt, 0), GetColumnText(stmt, 1));
return SQLITE_OK;
});
};
Result<std::optional<Table>> NextTable() override {
if (next_table >= tables.size()) return std::nullopt;
const auto& table = tables[next_table++];
return Table{table.first, table.second};
}
Status LoadColumns(std::string_view catalog, std::string_view schema,
std::string_view table,
std::optional<std::string_view> column_filter) override {
// XXX: pragma_table_info doesn't appear to work with bind parameters
// XXX: because we're saving the SqliteQuery, we also need to save the string builder
columns_query.Reset();
columns_query.Append(
R"(SELECT cid, name, type, 'notnull', dflt_value FROM pragma_table_info(%Q, %Q) WHERE NAME LIKE ?)",
table.data(), catalog.data());
UNWRAP_RESULT(auto query, columns_query.GetString());
assert(!query.empty());
columns.emplace(conn, query);
UNWRAP_STATUS(columns->Init());
auto filter = column_filter.value_or(kNoFilter);
int rc = sqlite3_bind_text(columns->stmt(), 1, filter.data(),
static_cast<int>(filter.size()), SQLITE_STATIC);
if (rc != SQLITE_OK) {
return columns->Close(rc);
}
// As with columns, we could return constraints iteratively instead of
// reading them all up front, but that complicates the state management
// We can get primary keys and foreign keys, but not unique constraints
// (unless we parse the SQL table definition)
// XXX: n + 1 query pattern. You can join on a pragma so we could avoid
// this in principle but it complicates the unpacking code here quite a
// bit, so ignore for now. Also, we already have to issue a query per table.
constraints.clear();
next_constraint = 0;
// Get the primary key
{
SqliteStringBuilder builder;
builder.Append(
R"(SELECT name FROM pragma_table_info(%Q, %Q) WHERE pk > 0 ORDER BY pk ASC)",
table.data(), catalog.data());
UNWRAP_RESULT(auto pk_query, builder.GetString());
std::vector<std::string> pk;
UNWRAP_STATUS(SqliteQuery::Scan(
conn, pk_query, [](sqlite3_stmt*) { return SQLITE_OK; },
[&](sqlite3_stmt* stmt) {
pk.emplace_back(std::string(GetColumnText(stmt, 0)));
return SQLITE_OK;
}));
if (!pk.empty()) {
// it would be nice to have C++20 designated initializers...
constraints.emplace_back(OwnedConstraint{
std::nullopt,
"PRIMARY KEY",
std::move(pk),
std::nullopt,
});
}
}
// Get any foreign keys
if (catalog == "main") {
// XXX: it appears experimentally that pragma_foreign_key_list won't let
// you specify the database, making the result ambiguous. We'll only
// query for the main catalog, but it appears if there's a table with
// the same name in a different database, SQLite will still happily
// return it here.
constexpr std::string_view kForeignKeyQuery =
R"(SELECT id, seq, "table", "from", "to"
FROM pragma_foreign_key_list(?)
ORDER BY id, seq ASC)";
int prev_id = -1;
UNWRAP_STATUS(SqliteQuery::Scan(
conn, kForeignKeyQuery,
[&](sqlite3_stmt* stmt) {
return sqlite3_bind_text(stmt, 1, table.data(),
static_cast<int>(table.size()), SQLITE_STATIC);
},
[&](sqlite3_stmt* stmt) {
int fk_id = sqlite3_column_int(stmt, 0);
auto to_table = GetColumnText(stmt, 2);
auto from_col = GetColumnText(stmt, 3);
auto to_col = GetColumnText(stmt, 4);
if (fk_id != prev_id) {
prev_id = fk_id;
constraints.emplace_back(OwnedConstraint{
std::nullopt,
"FOREIGN KEY",
{},
std::make_optional<std::vector<OwnedConstraintUsage>>(),
});
}
constraints.back().column_names.emplace_back(from_col);
constraints.back().usage->emplace_back(OwnedConstraintUsage{
"main",
"",
std::string(to_table),
std::string(to_col),
});
return SQLITE_OK;
}));
}
return status::Ok();
};
Result<std::optional<Column>> NextColumn() override {
if (!columns) return std::nullopt;
UNWRAP_RESULT(auto has_next, columns->Next());
if (!has_next) {
auto query = std::move(*columns);
columns.reset();
UNWRAP_STATUS(query.Close());
return std::nullopt;
}
ColumnXdbc xdbc;
bool notnull = sqlite3_column_int(columns->stmt(), 3) != 0;
xdbc.xdbc_type_name = GetColumnText(columns->stmt(), 2);
xdbc.xdbc_nullable =
notnull ? std::make_optional<int16_t>(0) : std::make_optional<int16_t>(1);
if (sqlite3_column_type(columns->stmt(), 4) != SQLITE_NULL) {
xdbc.xdbc_column_def = GetColumnText(columns->stmt(), 4);
}
xdbc.xdbc_is_nullable = notnull ? "NO" : "YES";
return Column{
reinterpret_cast<const char*>(sqlite3_column_text(columns->stmt(), 1)),
sqlite3_column_int(columns->stmt(), 0) + 1,
std::nullopt,
xdbc,
};
}
Result<std::optional<Constraint>> NextConstraint() override {
if (next_constraint >= constraints.size()) return std::nullopt;
return constraints[next_constraint++].ToDriver();
}
struct OwnedConstraintUsage {
std::optional<std::string> catalog;
std::optional<std::string> schema;
std::string table;
std::string column;
ConstraintUsage ToDriver() const {
auto catalog = this->catalog ? std::make_optional(std::string_view(*this->catalog))
: std::nullopt;
auto schema = this->schema ? std::make_optional(std::string_view(*this->schema))
: std::nullopt;
return {catalog, schema, table, column};
}
};
struct OwnedConstraint {
std::optional<std::string> name;
std::string type;
std::vector<std::string> column_names;
std::optional<std::vector<OwnedConstraintUsage>> usage;
Constraint ToDriver() const {
auto name =
this->name ? std::make_optional(std::string_view(*this->name)) : std::nullopt;
std::vector<std::string_view> column_names;
std::vector<ConstraintUsage> usages;
for (const auto& column_name : this->column_names) {
column_names.emplace_back(column_name);
}
if (this->usage) {
for (const auto& usage : *this->usage) {
usages.emplace_back(usage.ToDriver());
}
return {name, type, std::move(column_names), std::move(usages)};
}
return {name, type, std::move(column_names), std::nullopt};
}
};
sqlite3* conn = nullptr;
std::vector<std::string> catalogs;
std::vector<std::string> schemas;
std::vector<std::pair<std::string, std::string>> tables;
std::vector<OwnedConstraint> constraints;
SqliteStringBuilder columns_query;
std::optional<SqliteQuery> columns;
size_t next_catalog = 0;
size_t next_schema = 0;
size_t next_table = 0;
size_t next_constraint = 0;
};
class SqliteDatabase : public driver::Database<SqliteDatabase> {
public:
[[maybe_unused]] constexpr static std::string_view kErrorPrefix = "[SQLite]";
Result<sqlite3*> OpenConnection() {
sqlite3* conn;
int rc = sqlite3_open_v2(uri_.c_str(), &conn,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_URI,
/*zVfs=*/nullptr);
if (rc != SQLITE_OK) {
Status status;
if (conn_) {
status = status::fmt::IO("failed to open '{}': {}", uri_, sqlite3_errmsg(conn));
} else {
status = status::fmt::IO("failed to open '{}': failed to allocate memory", uri_);
}
(void)sqlite3_close(conn);
return status;
}
return conn;
}
Status InitImpl() override {
UNWRAP_RESULT(conn_, OpenConnection());
return status::Ok();
}
Status ReleaseImpl() override {
if (conn_) {
int rc = sqlite3_close_v2(conn_);
if (rc != SQLITE_OK) {
return status::fmt::IO("failed to close connection: ({}) {}", rc,
sqlite3_errmsg(conn_));
}
conn_ = nullptr;
}
return Base::ReleaseImpl();
}
Status SetOptionImpl(std::string_view key, driver::Option value) override {
if (key == "uri") {
if (lifecycle_state_ != driver::LifecycleState::kUninitialized) {
return status::InvalidState("cannot set uri after AdbcDatabaseInit");
}
UNWRAP_RESULT(auto uri, value.AsString());
uri_ = std::move(uri);
return status::Ok();
}
return Base::SetOptionImpl(key, value);
}
private:
std::string uri_{kDefaultUri};
sqlite3* conn_ = nullptr;
};
class SqliteConnection : public driver::Connection<SqliteConnection> {
public:
[[maybe_unused]] constexpr static std::string_view kErrorPrefix = "[SQLite]";
sqlite3* conn() const { return conn_; }
Status CommitImpl() {
UNWRAP_STATUS(CheckOpen());
UNWRAP_STATUS(SqliteQuery::Execute(conn_, "COMMIT"));
// begin another transaction, since we're not in autocommit
return SqliteQuery::Execute(conn_, "BEGIN");
}
Result<std::optional<std::string>> GetCurrentCatalogImpl() { return "main"; }
Result<std::unique_ptr<driver::GetObjectsHelper>> GetObjectsImpl() {
return std::make_unique<SqliteGetObjectsHelper>(conn_);
}
Status GetTableSchemaImpl(std::optional<std::string_view> catalog,
std::optional<std::string_view> db_schema,
std::string_view table_name, ArrowSchema* schema) {
if (db_schema.has_value() && !db_schema->empty()) {
return status::NotImplemented("SQLite does not support schemas");
}
SqliteStringBuilder builder;
builder.Append(R"(SELECT * FROM "%w" . "%w")", catalog.value_or("main").data(),
table_name.data());
UNWRAP_RESULT(std::string_view query, builder.GetString());
sqlite3_stmt* stmt = nullptr;
int rc =
sqlite3_prepare_v2(conn_, query.data(), static_cast<int>(query.size()), &stmt,
/*pzTail=*/nullptr);
if (rc != SQLITE_OK) {
(void)sqlite3_finalize(stmt);
return status::fmt::NotFound("GetTableSchema: {}", sqlite3_errmsg(conn_));
}
nanoarrow::UniqueArrayStream stream;
struct AdbcError error = ADBC_ERROR_INIT;
AdbcStatusCode status =
AdbcSqliteExportReader(conn_, stmt, /*binder=*/NULL,
/*batch_size=*/64, stream.get(), &error);
if (status == ADBC_STATUS_OK) {
int code = stream->get_schema(stream.get(), schema);
if (code != 0) {
(void)sqlite3_finalize(stmt);
return status::fmt::IO("failed to get schema: ({}) {}", code,
std::strerror(code));
}
}
(void)sqlite3_finalize(stmt);
return Status::FromAdbc(status, error);
}
Result<std::vector<std::string>> GetTableTypesImpl() {
return std::vector<std::string>{"table", "view"};
}
Result<std::vector<driver::InfoValue>> InfoImpl(const std::vector<uint32_t>& codes) {
static std::vector<uint32_t> kDefaultCodes{
ADBC_INFO_VENDOR_NAME, ADBC_INFO_VENDOR_VERSION, ADBC_INFO_DRIVER_NAME,
ADBC_INFO_DRIVER_VERSION, ADBC_INFO_DRIVER_ARROW_VERSION,
};
std::reference_wrapper<const std::vector<uint32_t>> codes_ref(codes);
if (codes.empty()) {
codes_ref = kDefaultCodes;
}
std::vector<driver::InfoValue> result;
for (const auto code : codes_ref.get()) {
switch (code) {
case ADBC_INFO_VENDOR_NAME:
result.emplace_back(code, "SQLite");
break;
case ADBC_INFO_VENDOR_VERSION:
result.emplace_back(code, sqlite3_libversion());
break;
case ADBC_INFO_DRIVER_NAME:
result.emplace_back(code, "ADBC SQLite Driver");
break;
case ADBC_INFO_DRIVER_VERSION:
// TODO(lidavidm): fill in driver version
result.emplace_back(code, "(unknown)");
break;
case ADBC_INFO_DRIVER_ARROW_VERSION:
result.emplace_back(code, NANOARROW_VERSION);
break;
default:
// Ignore
continue;
}
}
return result;
}
Status InitImpl(void* parent) {
auto& db = *reinterpret_cast<SqliteDatabase*>(parent);
UNWRAP_RESULT(conn_, db.OpenConnection());
return status::Ok();
}
Status ReleaseImpl() {
if (conn_) {
int rc = sqlite3_close_v2(conn_);
if (rc != SQLITE_OK) {
return status::fmt::IO("failed to close connection: ({}) {}", rc,
sqlite3_errmsg(conn_));
}
conn_ = nullptr;
}
return Connection::ReleaseImpl();
}
Status RollbackImpl() {
UNWRAP_STATUS(CheckOpen());
UNWRAP_STATUS(SqliteQuery::Execute(conn_, "ROLLBACK"));
return SqliteQuery::Execute(conn_, "BEGIN");
}
Status SetOptionImpl(std::string_view key, driver::Option value) {
if (key == kConnectionOptionEnableLoadExtension) {
if (!conn_ || lifecycle_state_ != driver::LifecycleState::kInitialized) {
return status::InvalidState(
"cannot enable extension loading before AdbcConnectionInit");
}
UNWRAP_RESULT(const bool enabled, value.AsBool());
int rc = sqlite3_db_config(conn_, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION,
enabled ? 1 : 0, nullptr);
if (rc != SQLITE_OK) {
return status::fmt::IO("cannot enable extension loading: {}",
sqlite3_errmsg(conn_));
}
return status::Ok();
} else if (key == kConnectionOptionLoadExtensionPath) {
if (!conn_ || lifecycle_state_ != driver::LifecycleState::kInitialized) {
return status::InvalidState("cannot load extension before AdbcConnectionInit");
}
UNWRAP_RESULT(extension_path_, value.AsString());
return status::Ok();
} else if (key == kConnectionOptionLoadExtensionEntrypoint) {
#if !defined(ADBC_SQLITE_WITH_NO_LOAD_EXTENSION)
if (extension_path_.empty()) {
return status::fmt::InvalidState("{} can only be set after {}",
kConnectionOptionLoadExtensionEntrypoint,
kConnectionOptionLoadExtensionPath);
}
const char* extension_entrypoint = nullptr;
if (value.has_value()) {
UNWRAP_RESULT(auto entrypoint, value.AsString());
extension_entrypoint = entrypoint.data();
}
char* message = NULL;
int rc = sqlite3_load_extension(conn_, extension_path_.c_str(),
extension_entrypoint, &message);
if (rc != SQLITE_OK) {
auto status = status::fmt::Unknown(
"failed to load extension {} (entrypoint {}): {}", extension_path_,
extension_entrypoint ? extension_entrypoint : "(NULL)",
message ? message : "(unknown error)");
if (message) sqlite3_free(message);
return status;
}
extension_path_.clear();
return status::Ok();
#else
return status::NotImplemented(
"this driver build does not support extension loading");
#endif
}
return Base::SetOptionImpl(key, value);
}
Status ToggleAutocommitImpl(bool enable_autocommit) {
UNWRAP_STATUS(CheckOpen());
if (enable_autocommit) {
// that means we have an open transaction, just commit
return SqliteQuery::Execute(conn_, "COMMIT");
}
// that means we have no open transaction, just begin
return SqliteQuery::Execute(conn_, "BEGIN");
}
private:
Status CheckOpen() const {
if (!conn_) {
return status::InvalidState("connection is not open");
}
return status::Ok();
}
sqlite3* conn_ = nullptr;
// Temporarily hold the extension path (since the path and entrypoint need
// to be set separately)
std::string extension_path_;
};
class SqliteStatement : public driver::Statement<SqliteStatement> {
public:
[[maybe_unused]] constexpr static std::string_view kErrorPrefix = "[SQLite]";
Status BindImpl() {
if (bind_parameters_.release) {
struct AdbcError error = ADBC_ERROR_INIT;
if (AdbcStatusCode code =
AdbcSqliteBinderSetArrayStream(&binder_, &bind_parameters_, &error);
code != ADBC_STATUS_OK) {
return Status::FromAdbc(code, error);
}
}
return status::Ok();
}
Result<int64_t> ExecuteIngestImpl(IngestState& state) {
UNWRAP_STATUS(BindImpl());
if (!binder_.schema.release) {
return status::InvalidState("must Bind() before bulk ingestion");
}
// Parameter validation
if (state.target_catalog && state.temporary) {
return status::fmt::InvalidState("{} Cannot set both {} and {}", kErrorPrefix,
ADBC_INGEST_OPTION_TARGET_CATALOG,
ADBC_INGEST_OPTION_TEMPORARY);
} else if (state.target_schema) {
return status::fmt::NotImplemented("{} {} not supported", kErrorPrefix,
ADBC_INGEST_OPTION_TARGET_DB_SCHEMA);
} else if (!state.target_table) {
return status::fmt::InvalidState("{} Must set {}", kErrorPrefix,
ADBC_INGEST_OPTION_TARGET_TABLE);
}
// Create statements for creating the table, inserting a row, and the table name
SqliteStringBuilder create_query, drop_query, insert_query, table_builder;
if (state.target_catalog) {
table_builder.Append(R"("%w" . "%w")", state.target_catalog->c_str(),
state.target_table->c_str());
} else if (state.temporary) {
// OK to be redundant (CREATE TEMP TABLE temp.foo)
table_builder.Append(R"(temp . "%w")", state.target_table->c_str());
} else {
// If not temporary, explicitly target the main database
table_builder.Append(R"(main . "%w")", state.target_table->c_str());
}
UNWRAP_RESULT(std::string_view table, table_builder.GetString());
switch (state.table_exists_) {
case Base::TableExists::kAppend:
if (state.temporary) {
create_query.Append("CREATE TEMPORARY TABLE IF NOT EXISTS %s (", table.data());
} else {
create_query.Append("CREATE TABLE IF NOT EXISTS %s (", table.data());
}
break;
case Base::TableExists::kFail:
case Base::TableExists::kReplace:
if (state.temporary) {
create_query.Append("CREATE TEMPORARY TABLE %s (", table.data());
} else {
create_query.Append("CREATE TABLE %s (", table.data());
}
drop_query.Append("DROP TABLE IF EXISTS %s", table.data());
break;
}
insert_query.Append("INSERT INTO %s (", table.data());
struct ArrowError arrow_error = {0};
struct ArrowSchemaView view;
std::memset(&view, 0, sizeof(view));
for (int i = 0; i < binder_.schema.n_children; i++) {
if (i > 0) {
create_query.Append(", ");
insert_query.Append(", ");
}
create_query.Append(R"("%w")", binder_.schema.children[i]->name);
insert_query.Append(R"("%w")", binder_.schema.children[i]->name);
int status = ArrowSchemaViewInit(&view, binder_.schema.children[i], &arrow_error);
if (status != 0) {
return status::fmt::Internal("failed to parse schema for column {}: {} ({}): {}",
i, std::strerror(status), status,
arrow_error.message);
}
switch (view.type) {
case NANOARROW_TYPE_BOOL:
case NANOARROW_TYPE_UINT8:
case NANOARROW_TYPE_UINT16:
case NANOARROW_TYPE_UINT32:
case NANOARROW_TYPE_UINT64:
case NANOARROW_TYPE_INT8:
case NANOARROW_TYPE_INT16:
case NANOARROW_TYPE_INT32:
case NANOARROW_TYPE_INT64:
create_query.Append(" INTEGER");
break;
case NANOARROW_TYPE_FLOAT:
case NANOARROW_TYPE_DOUBLE:
create_query.Append(" REAL");
break;
case NANOARROW_TYPE_STRING:
case NANOARROW_TYPE_LARGE_STRING:
case NANOARROW_TYPE_DATE32:
create_query.Append(" TEXT");
break;
case NANOARROW_TYPE_BINARY:
create_query.Append(" BLOB");
break;
default:
break;
}
}
create_query.Append(")");
insert_query.Append(") VALUES (");
for (int i = 0; i < binder_.schema.n_children; i++) {
insert_query.Append("%s?", (i > 0 ? ", " : ""));
}
insert_query.Append(")");
UNWRAP_RESULT(std::string_view create, create_query.GetString());
UNWRAP_RESULT(std::string_view drop, drop_query.GetString());
UNWRAP_RESULT(std::string_view insert, insert_query.GetString());
// Drop/create tables as needed
switch (state.table_exists_) {
case Base::TableExists::kAppend:
case Base::TableExists::kFail:
// Do nothing
break;
case Base::TableExists::kReplace: {
UNWRAP_STATUS(::adbc::sqlite::SqliteQuery::Execute(conn_, drop));
break;
}
}
switch (state.table_does_not_exist_) {
case Base::TableDoesNotExist::kCreate: {
UNWRAP_STATUS(::adbc::sqlite::SqliteQuery::Execute(conn_, create));
break;
}
case Base::TableDoesNotExist::kFail:
// Do nothing
break;
}
// Insert
int64_t row_count = 0;
const int is_autocommit = sqlite3_get_autocommit(conn_);
if (is_autocommit) {
UNWRAP_STATUS(::adbc::sqlite::SqliteQuery::Execute(conn_, "BEGIN"));
}
assert(!insert.empty());
sqlite3_stmt* stmt = nullptr;
{
int rc = sqlite3_prepare_v2(conn_, insert.data(), static_cast<int>(insert.size()),
&stmt, /*pzTail=*/nullptr);
if (rc != SQLITE_OK) {
std::ignore = sqlite3_finalize(stmt);
return status::fmt::Internal("failed to prepare: {}\nquery was: {}",
sqlite3_errmsg(conn_), insert);
}
}
assert(stmt != nullptr);
AdbcStatusCode status_code = ADBC_STATUS_OK;
Status status = status::Ok();
struct AdbcError error = ADBC_ERROR_INIT;
while (true) {
char finished = 0;
status_code = AdbcSqliteBinderBindNext(&binder_, conn_, stmt, &finished, &error);
if (status_code != ADBC_STATUS_OK || finished) {
status = Status::FromAdbc(status_code, error);
break;
}
int rc = 0;
do {
rc = sqlite3_step(stmt);
} while (rc == SQLITE_ROW);
if (rc != SQLITE_DONE) {
status = status::fmt::Internal("failed to execute: {}\nquery was: {}",
sqlite3_errmsg(conn_), insert.data());
status_code = ADBC_STATUS_INTERNAL;
break;
}
row_count++;
}
std::ignore = sqlite3_finalize(stmt);
if (is_autocommit) {
if (status_code == ADBC_STATUS_OK) {
UNWRAP_STATUS(::adbc::sqlite::SqliteQuery::Execute(conn_, "COMMIT"));
} else {
UNWRAP_STATUS(::adbc::sqlite::SqliteQuery::Execute(conn_, "ROLLBACK"));
}
}
if (status_code != ADBC_STATUS_OK) {
return status;
}
return row_count;
}
Result<int64_t> ExecuteQueryImpl(ArrowArrayStream* stream) {
struct AdbcError error = ADBC_ERROR_INIT;
UNWRAP_STATUS(BindImpl());
const int64_t expected = sqlite3_bind_parameter_count(stmt_);
const int64_t actual = binder_.schema.n_children;
if (actual != expected) {
return status::fmt::InvalidState(
"parameter count mismatch: expected {} but found {}", expected, actual);
}
auto status =
AdbcSqliteExportReader(conn_, stmt_, binder_.schema.release ? &binder_ : nullptr,
batch_size_, stream, &error);
if (status != ADBC_STATUS_OK) {
return Status::FromAdbc(status, error);
}
return -1;
}
Result<int64_t> ExecuteQueryImpl(PreparedState& state, ArrowArrayStream* stream) {
return ExecuteQueryImpl(stream);
}
Result<int64_t> ExecuteQueryImpl(QueryState& state, ArrowArrayStream* stream) {
UNWRAP_STATUS(PrepareImpl(state));
return ExecuteQueryImpl(stream);
}
Result<int64_t> ExecuteUpdateImpl() {
UNWRAP_STATUS(BindImpl());
const int64_t expected = sqlite3_bind_parameter_count(stmt_);
const int64_t actual = binder_.schema.n_children;
if (actual != expected) {
return status::fmt::InvalidState(
"parameter count mismatch: expected {} but found {}", expected, actual);
}
int64_t output_rows = 0;
int64_t changed_rows = 0;
SqliteMutexGuard guard(conn_);
while (true) {
if (binder_.schema.release) {
char finished = 0;
struct AdbcError error = ADBC_ERROR_INIT;
if (AdbcStatusCode code =
AdbcSqliteBinderBindNext(&binder_, conn_, stmt_, &finished, &error);
code != ADBC_STATUS_OK) {
AdbcSqliteBinderRelease(&binder_);
return Status::FromAdbc(code, error);
} else if (finished != 0) {
break;
}
}
while (sqlite3_step(stmt_) == SQLITE_ROW) {
output_rows++;
}
if (sqlite3_column_count(stmt_) == 0) {
changed_rows += sqlite3_changes(conn_);
}
if (!binder_.schema.release) break;
}
AdbcSqliteBinderRelease(&binder_);
if (sqlite3_reset(stmt_) != SQLITE_OK) {
const char* msg = sqlite3_errmsg(conn_);
return status::fmt::IO("failed to execute query: {}",
msg ? msg : "(unknown error)");
}
if (sqlite3_column_count(stmt_) == 0) {
return changed_rows;
} else {
return output_rows;
}
}
Result<int64_t> ExecuteUpdateImpl(PreparedState& state) { return ExecuteUpdateImpl(); }
Result<int64_t> ExecuteUpdateImpl(QueryState& state) {
UNWRAP_STATUS(PrepareImpl(state));
return ExecuteUpdateImpl();
}
Status GetParameterSchemaImpl(PreparedState& state, ArrowSchema* schema) {
int num_params = sqlite3_bind_parameter_count(stmt_);
if (num_params < 0) {
// Should not happen
return status::fmt::Internal("{} SQLite returned negative parameter count",
kErrorPrefix);
}
nanoarrow::UniqueSchema uschema;
ArrowSchemaInit(uschema.get());
UNWRAP_ERRNO(Internal, ArrowSchemaSetType(uschema.get(), NANOARROW_TYPE_STRUCT));
UNWRAP_ERRNO(Internal, ArrowSchemaAllocateChildren(uschema.get(), num_params));
char buffer[12];
for (int i = 0; i < num_params; i++) {
const char* name = sqlite3_bind_parameter_name(stmt_, i + 1);
if (name == NULL) {
snprintf(buffer, sizeof(buffer), "%d", i);
name = buffer;
}
ArrowSchemaInit(uschema->children[i]);
UNWRAP_ERRNO(Internal, ArrowSchemaSetType(uschema->children[i], NANOARROW_TYPE_NA));
UNWRAP_ERRNO(Internal, ArrowSchemaSetName(uschema->children[i], name));
}
uschema.move(schema);
return status::Ok();
}
Status InitImpl(void* parent) {
conn_ = reinterpret_cast<SqliteConnection*>(parent)->conn();
return Statement::InitImpl(parent);
}
Status PrepareImpl(QueryState& state) {
if (stmt_) {
int rc = sqlite3_finalize(stmt_);
stmt_ = nullptr;
if (rc != SQLITE_OK) {
return status::fmt::IO("{} Failed to finalize previous statement: ({}) {}",
kErrorPrefix, rc, sqlite3_errmsg(conn_));
}
}
int rc = sqlite3_prepare_v2(conn_, state.query.c_str(),
static_cast<int>(state.query.size()), &stmt_,
/*pzTail=*/nullptr);
if (rc != SQLITE_OK) {
std::string msg = sqlite3_errmsg(conn_);
std::ignore = sqlite3_finalize(stmt_);
stmt_ = NULL;
return status::fmt::InvalidArgument("{} Failed to prepare query: {}\nquery: {}",
kErrorPrefix, msg, state.query);
}
return status::Ok();
}
Status ReleaseImpl() {
if (stmt_) {
int rc = sqlite3_finalize(stmt_);
stmt_ = nullptr;
if (rc != SQLITE_OK) {
return status::fmt::IO("{} Failed to finalize statement: ({}) {}", kErrorPrefix,
rc, sqlite3_errmsg(conn_));
}
}
AdbcSqliteBinderRelease(&binder_);
return Statement::ReleaseImpl();
}
Status SetOptionImpl(std::string_view key, driver::Option value) {
if (key == kStatementOptionBatchRows) {
UNWRAP_RESULT(int64_t batch_size, value.AsInt());
if (batch_size >= std::numeric_limits<int>::max() || batch_size <= 0) {
return status::fmt::InvalidArgument(
"{} Invalid statement option value {}={} (value is non-positive or out of "
"range of int)",
kErrorPrefix, key, value.Format());
}
batch_size_ = static_cast<int>(batch_size);
return status::Ok();
}
return Base::SetOptionImpl(key, std::move(value));
}
int batch_size_ = 1024;
AdbcSqliteBinder binder_;
sqlite3* conn_ = nullptr;
sqlite3_stmt* stmt_ = nullptr;
};
using SqliteDriver =
adbc::driver::Driver<SqliteDatabase, SqliteConnection, SqliteStatement>;
} // namespace
} // namespace adbc::sqlite
// Public names
AdbcStatusCode AdbcDatabaseGetOption(struct AdbcDatabase* database, const char* key,
char* value, size_t* length,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CGetOption<>(database, key, value, length, error);
}
AdbcStatusCode AdbcDatabaseGetOptionBytes(struct AdbcDatabase* database, const char* key,
uint8_t* value, size_t* length,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CGetOptionBytes<>(database, key, value, length,
error);
}
AdbcStatusCode AdbcDatabaseGetOptionInt(struct AdbcDatabase* database, const char* key,
int64_t* value, struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CGetOptionInt<>(database, key, value, error);
}
AdbcStatusCode AdbcDatabaseGetOptionDouble(struct AdbcDatabase* database, const char* key,
double* value, struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CGetOptionDouble<>(database, key, value, error);
}
AdbcStatusCode AdbcDatabaseInit(struct AdbcDatabase* database, struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CDatabaseInit(database, error);
}
AdbcStatusCode AdbcDatabaseNew(struct AdbcDatabase* database, struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CNew<>(database, error);
}
AdbcStatusCode AdbcDatabaseRelease(struct AdbcDatabase* database,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CRelease<>(database, error);
}
AdbcStatusCode AdbcDatabaseSetOption(struct AdbcDatabase* database, const char* key,
const char* value, struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CSetOption<>(database, key, value, error);
}
AdbcStatusCode AdbcDatabaseSetOptionBytes(struct AdbcDatabase* database, const char* key,
const uint8_t* value, size_t length,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CSetOptionBytes<>(database, key, value, length,
error);
}
AdbcStatusCode AdbcDatabaseSetOptionInt(struct AdbcDatabase* database, const char* key,
int64_t value, struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CSetOptionInt<>(database, key, value, error);
}
AdbcStatusCode AdbcDatabaseSetOptionDouble(struct AdbcDatabase* database, const char* key,
double value, struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CSetOptionDouble<>(database, key, value, error);
}
AdbcStatusCode AdbcConnectionCancel(struct AdbcConnection* connection,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CConnectionCancel(connection, error);
}
AdbcStatusCode AdbcConnectionGetOption(struct AdbcConnection* connection, const char* key,
char* value, size_t* length,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CGetOption<>(connection, key, value, length, error);
}
AdbcStatusCode AdbcConnectionGetOptionBytes(struct AdbcConnection* connection,
const char* key, uint8_t* value,
size_t* length, struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CGetOptionBytes<>(connection, key, value, length,
error);
}
AdbcStatusCode AdbcConnectionGetOptionInt(struct AdbcConnection* connection,
const char* key, int64_t* value,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CGetOptionInt<>(connection, key, value, error);
}
AdbcStatusCode AdbcConnectionGetOptionDouble(struct AdbcConnection* connection,
const char* key, double* value,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CGetOptionDouble<>(connection, key, value, error);
}
AdbcStatusCode AdbcConnectionNew(struct AdbcConnection* connection,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CNew<>(connection, error);
}
AdbcStatusCode AdbcConnectionSetOption(struct AdbcConnection* connection, const char* key,
const char* value, struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CSetOption<>(connection, key, value, error);
}
AdbcStatusCode AdbcConnectionSetOptionBytes(struct AdbcConnection* connection,
const char* key, const uint8_t* value,
size_t length, struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CSetOptionBytes<>(connection, key, value, length,
error);
}
AdbcStatusCode AdbcConnectionSetOptionInt(struct AdbcConnection* connection,
const char* key, int64_t value,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CSetOptionInt<>(connection, key, value, error);
}
AdbcStatusCode AdbcConnectionSetOptionDouble(struct AdbcConnection* connection,
const char* key, double value,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CSetOptionDouble<>(connection, key, value, error);
}
AdbcStatusCode AdbcConnectionInit(struct AdbcConnection* connection,
struct AdbcDatabase* database,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CConnectionInit(connection, database, error);
}
AdbcStatusCode AdbcConnectionRelease(struct AdbcConnection* connection,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CRelease<>(connection, error);
}
AdbcStatusCode AdbcConnectionGetInfo(struct AdbcConnection* connection,
const uint32_t* info_codes, size_t info_codes_length,
struct ArrowArrayStream* out,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CConnectionGetInfo(connection, info_codes,
info_codes_length, out, error);
}
AdbcStatusCode AdbcConnectionGetObjects(struct AdbcConnection* connection, int depth,
const char* catalog, const char* db_schema,
const char* table_name, const char** table_type,
const char* column_name,
struct ArrowArrayStream* out,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CConnectionGetObjects(
connection, depth, catalog, db_schema, table_name, table_type, column_name, out,
error);
}
AdbcStatusCode AdbcConnectionGetStatistics(struct AdbcConnection* connection,
const char* catalog, const char* db_schema,
const char* table_name, char approximate,
struct ArrowArrayStream* out,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CConnectionGetStatistics(
connection, catalog, db_schema, table_name, approximate, out, error);
}
AdbcStatusCode AdbcConnectionGetStatisticNames(struct AdbcConnection* connection,
struct ArrowArrayStream* out,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CConnectionGetStatisticNames(connection, out, error);
}
AdbcStatusCode AdbcConnectionGetTableSchema(struct AdbcConnection* connection,
const char* catalog, const char* db_schema,
const char* table_name,
struct ArrowSchema* schema,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CConnectionGetTableSchema(
connection, catalog, db_schema, table_name, schema, error);
}
AdbcStatusCode AdbcConnectionGetTableTypes(struct AdbcConnection* connection,
struct ArrowArrayStream* out,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CConnectionGetTableTypes(connection, out, error);
}
AdbcStatusCode AdbcConnectionReadPartition(struct AdbcConnection* connection,
const uint8_t* serialized_partition,
size_t serialized_length,
struct ArrowArrayStream* out,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CConnectionReadPartition(
connection, serialized_partition, serialized_length, out, error);
}
AdbcStatusCode AdbcConnectionCommit(struct AdbcConnection* connection,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CConnectionCommit(connection, error);
}
AdbcStatusCode AdbcConnectionRollback(struct AdbcConnection* connection,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CConnectionRollback(connection, error);
}
AdbcStatusCode AdbcStatementCancel(struct AdbcStatement* statement,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CStatementCancel(statement, error);
}
AdbcStatusCode AdbcStatementNew(struct AdbcConnection* connection,
struct AdbcStatement* statement,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CStatementNew(connection, statement, error);
}
AdbcStatusCode AdbcStatementRelease(struct AdbcStatement* statement,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CRelease<>(statement, error);
}
AdbcStatusCode AdbcStatementExecuteQuery(struct AdbcStatement* statement,
struct ArrowArrayStream* out,
int64_t* rows_affected,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CStatementExecuteQuery(statement, out, rows_affected,
error);
}
AdbcStatusCode AdbcStatementExecuteSchema(struct AdbcStatement* statement,
struct ArrowSchema* schema,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CStatementExecuteSchema(statement, schema, error);
}
AdbcStatusCode AdbcStatementPrepare(struct AdbcStatement* statement,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CStatementPrepare(statement, error);
}
AdbcStatusCode AdbcStatementSetSqlQuery(struct AdbcStatement* statement,
const char* query, struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CStatementSetSqlQuery(statement, query, error);
}
AdbcStatusCode AdbcStatementSetSubstraitPlan(struct AdbcStatement* statement,
const uint8_t* plan, size_t length,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CStatementSetSubstraitPlan(statement, plan, length,
error);
}
AdbcStatusCode AdbcStatementBind(struct AdbcStatement* statement,
struct ArrowArray* values, struct ArrowSchema* schema,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CStatementBind(statement, values, schema, error);
}
AdbcStatusCode AdbcStatementBindStream(struct AdbcStatement* statement,
struct ArrowArrayStream* stream,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CStatementBindStream(statement, stream, error);
}
AdbcStatusCode AdbcStatementGetOption(struct AdbcStatement* statement, const char* key,
char* value, size_t* length,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CGetOption<>(statement, key, value, length, error);
}
AdbcStatusCode AdbcStatementGetOptionBytes(struct AdbcStatement* statement,
const char* key, uint8_t* value,
size_t* length, struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CGetOptionBytes<>(statement, key, value, length,
error);
}
AdbcStatusCode AdbcStatementGetOptionInt(struct AdbcStatement* statement, const char* key,
int64_t* value, struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CGetOptionInt<>(statement, key, value, error);
}
AdbcStatusCode AdbcStatementGetOptionDouble(struct AdbcStatement* statement,
const char* key, double* value,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CGetOptionDouble<>(statement, key, value, error);
}
AdbcStatusCode AdbcStatementGetParameterSchema(struct AdbcStatement* statement,
struct ArrowSchema* schema,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CStatementGetParameterSchema(statement, schema,
error);
}
AdbcStatusCode AdbcStatementSetOption(struct AdbcStatement* statement, const char* key,
const char* value, struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CSetOption<>(statement, key, value, error);
}
AdbcStatusCode AdbcStatementSetOptionBytes(struct AdbcStatement* statement,
const char* key, const uint8_t* value,
size_t length, struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CSetOptionBytes<>(statement, key, value, length,
error);
}
AdbcStatusCode AdbcStatementSetOptionInt(struct AdbcStatement* statement, const char* key,
int64_t value, struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CSetOptionInt<>(statement, key, value, error);
}
AdbcStatusCode AdbcStatementSetOptionDouble(struct AdbcStatement* statement,
const char* key, double value,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CSetOptionDouble<>(statement, key, value, error);
}
AdbcStatusCode AdbcStatementExecutePartitions(struct AdbcStatement* statement,
struct ArrowSchema* schema,
struct AdbcPartitions* partitions,
int64_t* rows_affected,
struct AdbcError* error) {
return adbc::sqlite::SqliteDriver::CStatementExecutePartitions(
statement, schema, partitions, rows_affected, error);
}
extern "C" {
[[maybe_unused]] ADBC_EXPORT AdbcStatusCode AdbcDriverSqliteInit(int version,
void* raw_driver,
AdbcError* error) {
return adbc::sqlite::SqliteDriver::Init(version, raw_driver, error);
}
[[maybe_unused]] ADBC_EXPORT AdbcStatusCode SqliteDriverInit(int version,
void* raw_driver,
AdbcError* error) {
return adbc::sqlite::SqliteDriver::Init(version, raw_driver, error);
}
[[maybe_unused]] ADBC_EXPORT AdbcStatusCode AdbcDriverInit(int version, void* raw_driver,
AdbcError* error) {
return adbc::sqlite::SqliteDriver::Init(version, raw_driver, error);
}
}