blob: bcca2f36764c1a2aff4f2cd7abae8ae6848d0476 [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 <exception>
#include <memory>
#include <string_view>
#include <sqlpp23/postgresql/postgresql.h>
#include "iceberg/catalog/sql/catalog_store.h"
#include "iceberg/catalog/sql/catalog_store_sqlpp23_internal.h"
#include "iceberg/catalog/sql/connection_uri_internal.h"
#include "iceberg/result.h"
#include "iceberg/util/macros.h"
namespace iceberg::sql {
namespace {
/// Recognizes PostgreSQL unique-constraint violations via SQLSTATE 23505
/// (unique_violation), reported on `result_exception`.
struct PostgresqlTraits {
static bool IsUniqueViolation(const std::exception& error) {
const auto* result_error =
dynamic_cast<const sqlpp::postgresql::result_exception*>(&error);
return result_error != nullptr && result_error->sql_state() == "23505";
}
static bool IsDuplicateColumn(const std::exception& error) {
const auto* result_error =
dynamic_cast<const sqlpp::postgresql::result_exception*>(&error);
return result_error != nullptr && result_error->sql_state() == "42701";
}
};
using PostgresqlSingleCatalogStore =
Sqlpp23CatalogStore<SingleConnectionSource<sqlpp::postgresql::connection>,
PostgresqlTraits>;
using PostgresqlPooledCatalogStore =
Sqlpp23CatalogStore<PooledConnectionSource<sqlpp::postgresql::connection_pool>,
PostgresqlTraits>;
} // namespace
Result<std::shared_ptr<CatalogStore>> MakePostgreSqlCatalogStore(
const CatalogStoreOptions& options) {
ICEBERG_ASSIGN_OR_RAISE(const auto parsed, ParseConnectionUri(options.uri));
auto config = std::make_shared<sqlpp::postgresql::connection_config>();
config->host = parsed.host;
if (parsed.port.has_value()) {
config->port = *parsed.port;
}
config->dbname = parsed.database;
config->user = parsed.user;
config->password = parsed.password;
try {
if (options.max_connections > 1) {
sqlpp::postgresql::connection_pool pool(
config, static_cast<std::size_t>(options.max_connections));
return std::make_shared<PostgresqlPooledCatalogStore>(
options.catalog_name, std::move(pool), options.max_connections);
}
sqlpp::postgresql::connection connection(config);
return std::make_shared<PostgresqlSingleCatalogStore>(options.catalog_name,
std::move(connection));
} catch (const std::exception& e) {
return IOError("Failed to connect to PostgreSQL '{}': {}", options.uri, e.what());
}
}
} // namespace iceberg::sql