blob: ca14f8af3ccf2161ccd77b8ce2b2f74c43922cdb [file]
/*
* 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 <sqlite3.h>
#include <exception>
#include <memory>
#include <string_view>
#include <sqlpp23/sqlite3/sqlite3.h>
#include "iceberg/catalog/sql/catalog_store.h"
#include "iceberg/catalog/sql/catalog_store_sqlpp23_internal.h"
#include "iceberg/result.h"
namespace iceberg::sql {
namespace {
/// Recognizes SQLite primary-key / unique-constraint violations. With extended
/// result codes enabled the connection reports SQLITE_CONSTRAINT_PRIMARYKEY /
/// SQLITE_CONSTRAINT_UNIQUE; the primary SQLITE_CONSTRAINT is matched too as a
/// safety net.
struct SqliteTraits {
static bool IsUniqueViolation(const std::exception& error) {
const auto* sqlite_error = dynamic_cast<const sqlpp::sqlite3::exception*>(&error);
if (sqlite_error == nullptr) {
return false;
}
const int code = sqlite_error->error_code();
return code == SQLITE_CONSTRAINT || code == SQLITE_CONSTRAINT_PRIMARYKEY ||
code == SQLITE_CONSTRAINT_UNIQUE;
}
static bool IsDuplicateColumn(const std::exception& error) {
const auto* sqlite_error = dynamic_cast<const sqlpp::sqlite3::exception*>(&error);
return sqlite_error != nullptr &&
std::string_view(sqlite_error->what()).find("duplicate column name") !=
std::string_view::npos;
}
};
using SqliteSingleCatalogStore =
Sqlpp23CatalogStore<SingleConnectionSource<sqlpp::sqlite3::connection>, SqliteTraits>;
} // namespace
Result<std::shared_ptr<CatalogStore>> MakeSqliteCatalogStore(
const CatalogStoreOptions& options) {
auto config = std::make_shared<sqlpp::sqlite3::connection_config>();
config->path_to_database = options.uri.empty() ? ":memory:" : options.uri;
config->flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
// Required so unique/primary-key violations surface their extended codes.
config->use_extended_result_codes = true;
// SQLite always uses a single connection regardless of `max_connections`: a
// `:memory:` database is private to each connection, and a file database only
// allows one writer, so a pool of write connections would just hit
// SQLITE_BUSY. The single connection serializes access internally.
try {
sqlpp::sqlite3::connection connection(config);
return std::make_shared<SqliteSingleCatalogStore>(options.catalog_name,
std::move(connection));
} catch (const std::exception& e) {
return IOError("Failed to open SQLite database '{}': {}", config->path_to_database,
e.what());
}
}
} // namespace iceberg::sql