Packages

An Elixir DuckDB library

Current section

Files

Jump to
exduckdb c_src duckdb src catalog catalog.cpp
Raw

c_src/duckdb/src/catalog/catalog.cpp

#include "duckdb/catalog/catalog.hpp"
#include "duckdb/catalog/catalog_search_path.hpp"
#include "duckdb/catalog/catalog_entry/list.hpp"
#include "duckdb/catalog/catalog_set.hpp"
#include "duckdb/catalog/default/default_schemas.hpp"
#include "duckdb/catalog/dependency_manager.hpp"
#include "duckdb/common/exception.hpp"
#include "duckdb/main/client_context.hpp"
#include "duckdb/main/database.hpp"
#include "duckdb/parser/expression/function_expression.hpp"
#include "duckdb/parser/parsed_data/alter_table_info.hpp"
#include "duckdb/parser/parsed_data/create_aggregate_function_info.hpp"
#include "duckdb/parser/parsed_data/create_collation_info.hpp"
#include "duckdb/parser/parsed_data/create_copy_function_info.hpp"
#include "duckdb/parser/parsed_data/create_index_info.hpp"
#include "duckdb/parser/parsed_data/create_pragma_function_info.hpp"
#include "duckdb/parser/parsed_data/create_scalar_function_info.hpp"
#include "duckdb/parser/parsed_data/create_schema_info.hpp"
#include "duckdb/parser/parsed_data/create_sequence_info.hpp"
#include "duckdb/parser/parsed_data/create_table_function_info.hpp"
#include "duckdb/parser/parsed_data/create_type_info.hpp"
#include "duckdb/parser/parsed_data/create_view_info.hpp"
#include "duckdb/parser/parsed_data/drop_info.hpp"
#include "duckdb/planner/parsed_data/bound_create_table_info.hpp"
namespace duckdb {
string SimilarCatalogEntry::GetQualifiedName() const {
D_ASSERT(Found());
return schema->name + "." + name;
}
Catalog::Catalog(DatabaseInstance &db)
: db(db), schemas(make_unique<CatalogSet>(*this, make_unique<DefaultSchemaGenerator>(*this))),
dependency_manager(make_unique<DependencyManager>(*this)) {
catalog_version = 0;
}
Catalog::~Catalog() {
}
Catalog &Catalog::GetCatalog(ClientContext &context) {
return context.db->GetCatalog();
}
CatalogEntry *Catalog::CreateTable(ClientContext &context, BoundCreateTableInfo *info) {
auto schema = GetSchema(context, info->base->schema);
return CreateTable(context, schema, info);
}
CatalogEntry *Catalog::CreateTable(ClientContext &context, SchemaCatalogEntry *schema, BoundCreateTableInfo *info) {
return schema->CreateTable(context, info);
}
CatalogEntry *Catalog::CreateView(ClientContext &context, CreateViewInfo *info) {
auto schema = GetSchema(context, info->schema);
return CreateView(context, schema, info);
}
CatalogEntry *Catalog::CreateView(ClientContext &context, SchemaCatalogEntry *schema, CreateViewInfo *info) {
return schema->CreateView(context, info);
}
CatalogEntry *Catalog::CreateSequence(ClientContext &context, CreateSequenceInfo *info) {
auto schema = GetSchema(context, info->schema);
return CreateSequence(context, schema, info);
}
CatalogEntry *Catalog::CreateType(ClientContext &context, CreateTypeInfo *info) {
auto schema = GetSchema(context, info->schema);
return CreateType(context, schema, info);
}
CatalogEntry *Catalog::CreateSequence(ClientContext &context, SchemaCatalogEntry *schema, CreateSequenceInfo *info) {
return schema->CreateSequence(context, info);
}
CatalogEntry *Catalog::CreateType(ClientContext &context, SchemaCatalogEntry *schema, CreateTypeInfo *info) {
return schema->CreateType(context, info);
}
CatalogEntry *Catalog::CreateTableFunction(ClientContext &context, CreateTableFunctionInfo *info) {
auto schema = GetSchema(context, info->schema);
return CreateTableFunction(context, schema, info);
}
CatalogEntry *Catalog::CreateTableFunction(ClientContext &context, SchemaCatalogEntry *schema,
CreateTableFunctionInfo *info) {
return schema->CreateTableFunction(context, info);
}
CatalogEntry *Catalog::CreateCopyFunction(ClientContext &context, CreateCopyFunctionInfo *info) {
auto schema = GetSchema(context, info->schema);
return CreateCopyFunction(context, schema, info);
}
CatalogEntry *Catalog::CreateCopyFunction(ClientContext &context, SchemaCatalogEntry *schema,
CreateCopyFunctionInfo *info) {
return schema->CreateCopyFunction(context, info);
}
CatalogEntry *Catalog::CreatePragmaFunction(ClientContext &context, CreatePragmaFunctionInfo *info) {
auto schema = GetSchema(context, info->schema);
return CreatePragmaFunction(context, schema, info);
}
CatalogEntry *Catalog::CreatePragmaFunction(ClientContext &context, SchemaCatalogEntry *schema,
CreatePragmaFunctionInfo *info) {
return schema->CreatePragmaFunction(context, info);
}
CatalogEntry *Catalog::CreateFunction(ClientContext &context, CreateFunctionInfo *info) {
auto schema = GetSchema(context, info->schema);
return CreateFunction(context, schema, info);
}
CatalogEntry *Catalog::CreateFunction(ClientContext &context, SchemaCatalogEntry *schema, CreateFunctionInfo *info) {
return schema->CreateFunction(context, info);
}
CatalogEntry *Catalog::CreateCollation(ClientContext &context, CreateCollationInfo *info) {
auto schema = GetSchema(context, info->schema);
return CreateCollation(context, schema, info);
}
CatalogEntry *Catalog::CreateCollation(ClientContext &context, SchemaCatalogEntry *schema, CreateCollationInfo *info) {
return schema->CreateCollation(context, info);
}
CatalogEntry *Catalog::CreateSchema(ClientContext &context, CreateSchemaInfo *info) {
D_ASSERT(!info->schema.empty());
if (info->schema == TEMP_SCHEMA) {
throw CatalogException("Cannot create built-in schema \"%s\"", info->schema);
}
unordered_set<CatalogEntry *> dependencies;
auto entry = make_unique<SchemaCatalogEntry>(this, info->schema, info->internal);
auto result = entry.get();
if (!schemas->CreateEntry(context, info->schema, move(entry), dependencies)) {
if (info->on_conflict == OnCreateConflict::ERROR_ON_CONFLICT) {
throw CatalogException("Schema with name %s already exists!", info->schema);
} else {
D_ASSERT(info->on_conflict == OnCreateConflict::IGNORE_ON_CONFLICT);
}
return nullptr;
}
return result;
}
void Catalog::DropSchema(ClientContext &context, DropInfo *info) {
D_ASSERT(!info->name.empty());
ModifyCatalog();
if (!schemas->DropEntry(context, info->name, info->cascade)) {
if (!info->if_exists) {
throw CatalogException("Schema with name \"%s\" does not exist!", info->name);
}
}
}
void Catalog::DropEntry(ClientContext &context, DropInfo *info) {
ModifyCatalog();
if (info->type == CatalogType::SCHEMA_ENTRY) {
// DROP SCHEMA
DropSchema(context, info);
return;
}
auto lookup = LookupEntry(context, info->type, info->schema, info->name, info->if_exists);
if (!lookup.Found()) {
return;
}
lookup.schema->DropEntry(context, info);
}
SchemaCatalogEntry *Catalog::GetSchema(ClientContext &context, const string &schema_name, bool if_exists,
QueryErrorContext error_context) {
D_ASSERT(!schema_name.empty());
if (schema_name == TEMP_SCHEMA) {
return context.temporary_objects.get();
}
auto entry = schemas->GetEntry(context, schema_name);
if (!entry && !if_exists) {
throw CatalogException(error_context.FormatError("Schema with name %s does not exist!", schema_name));
}
return (SchemaCatalogEntry *)entry;
}
void Catalog::ScanSchemas(ClientContext &context, std::function<void(CatalogEntry *)> callback) {
// create all default schemas first
schemas->Scan(context, [&](CatalogEntry *entry) { callback(entry); });
}
SimilarCatalogEntry Catalog::SimilarEntryInSchemas(ClientContext &context, const string &entry_name, CatalogType type,
const vector<SchemaCatalogEntry *> &schemas) {
vector<CatalogSet *> sets;
std::transform(schemas.begin(), schemas.end(), std::back_inserter(sets),
[type](SchemaCatalogEntry *s) -> CatalogSet * { return &s->GetCatalogSet(type); });
pair<string, idx_t> most_similar {"", (idx_t)-1};
SchemaCatalogEntry *schema_of_most_similar = nullptr;
for (auto schema : schemas) {
auto entry = schema->GetCatalogSet(type).SimilarEntry(context, entry_name);
if (!entry.first.empty() && (most_similar.first.empty() || most_similar.second > entry.second)) {
most_similar = entry;
schema_of_most_similar = schema;
}
}
return {most_similar.first, most_similar.second, schema_of_most_similar};
}
CatalogException Catalog::CreateMissingEntryException(ClientContext &context, const string &entry_name,
CatalogType type, const vector<SchemaCatalogEntry *> &schemas,
QueryErrorContext error_context) {
auto entry = SimilarEntryInSchemas(context, entry_name, type, schemas);
vector<SchemaCatalogEntry *> unseen_schemas;
this->schemas->Scan([&schemas, &unseen_schemas](CatalogEntry *entry) {
auto schema_entry = (SchemaCatalogEntry *)entry;
if (std::find(schemas.begin(), schemas.end(), schema_entry) == schemas.end()) {
unseen_schemas.emplace_back(schema_entry);
}
});
auto unseen_entry = SimilarEntryInSchemas(context, entry_name, type, unseen_schemas);
string did_you_mean;
if (unseen_entry.Found() && unseen_entry.distance < entry.distance) {
did_you_mean = "\nDid you mean \"" + unseen_entry.GetQualifiedName() + "\"?";
} else if (entry.Found()) {
did_you_mean = "\nDid you mean \"" + entry.name + "\"?";
}
return CatalogException(error_context.FormatError("%s with name %s does not exist!%s", CatalogTypeToString(type),
entry_name, did_you_mean));
}
CatalogEntryLookup Catalog::LookupEntry(ClientContext &context, CatalogType type, const string &schema_name,
const string &name, bool if_exists, QueryErrorContext error_context) {
if (!schema_name.empty()) {
auto schema = GetSchema(context, schema_name, if_exists, error_context);
if (!schema) {
D_ASSERT(if_exists);
return {nullptr, nullptr};
}
auto entry = schema->GetCatalogSet(type).GetEntry(context, name);
if (!entry && !if_exists) {
throw CreateMissingEntryException(context, name, type, {schema}, error_context);
}
return {schema, entry};
}
const auto &paths = context.catalog_search_path->Get();
for (const auto &path : paths) {
auto lookup = LookupEntry(context, type, path, name, true, error_context);
if (lookup.Found()) {
return lookup;
}
}
if (!if_exists) {
vector<SchemaCatalogEntry *> schemas;
for (const auto &path : paths) {
auto schema = GetSchema(context, path, true);
if (schema) {
schemas.emplace_back(schema);
}
}
throw CreateMissingEntryException(context, name, type, schemas, error_context);
}
return {nullptr, nullptr};
}
CatalogEntry *Catalog::GetEntry(ClientContext &context, CatalogType type, const string &schema_name, const string &name,
bool if_exists, QueryErrorContext error_context) {
return LookupEntry(context, type, schema_name, name, if_exists, error_context).entry;
}
template <>
TableCatalogEntry *Catalog::GetEntry(ClientContext &context, const string &schema_name, const string &name,
bool if_exists, QueryErrorContext error_context) {
auto entry = GetEntry(context, CatalogType::TABLE_ENTRY, schema_name, name, if_exists);
if (!entry) {
return nullptr;
}
if (entry->type != CatalogType::TABLE_ENTRY) {
throw CatalogException(error_context.FormatError("%s is not a table", name));
}
return (TableCatalogEntry *)entry;
}
template <>
SequenceCatalogEntry *Catalog::GetEntry(ClientContext &context, const string &schema_name, const string &name,
bool if_exists, QueryErrorContext error_context) {
return (SequenceCatalogEntry *)GetEntry(context, CatalogType::SEQUENCE_ENTRY, schema_name, name, if_exists,
error_context);
}
template <>
TableFunctionCatalogEntry *Catalog::GetEntry(ClientContext &context, const string &schema_name, const string &name,
bool if_exists, QueryErrorContext error_context) {
return (TableFunctionCatalogEntry *)GetEntry(context, CatalogType::TABLE_FUNCTION_ENTRY, schema_name, name,
if_exists, error_context);
}
template <>
CopyFunctionCatalogEntry *Catalog::GetEntry(ClientContext &context, const string &schema_name, const string &name,
bool if_exists, QueryErrorContext error_context) {
return (CopyFunctionCatalogEntry *)GetEntry(context, CatalogType::COPY_FUNCTION_ENTRY, schema_name, name, if_exists,
error_context);
}
template <>
PragmaFunctionCatalogEntry *Catalog::GetEntry(ClientContext &context, const string &schema_name, const string &name,
bool if_exists, QueryErrorContext error_context) {
return (PragmaFunctionCatalogEntry *)GetEntry(context, CatalogType::PRAGMA_FUNCTION_ENTRY, schema_name, name,
if_exists, error_context);
}
template <>
AggregateFunctionCatalogEntry *Catalog::GetEntry(ClientContext &context, const string &schema_name, const string &name,
bool if_exists, QueryErrorContext error_context) {
auto entry = GetEntry(context, CatalogType::AGGREGATE_FUNCTION_ENTRY, schema_name, name, if_exists, error_context);
if (entry->type != CatalogType::AGGREGATE_FUNCTION_ENTRY) {
throw CatalogException(error_context.FormatError("%s is not an aggregate function", name));
}
return (AggregateFunctionCatalogEntry *)entry;
}
template <>
CollateCatalogEntry *Catalog::GetEntry(ClientContext &context, const string &schema_name, const string &name,
bool if_exists, QueryErrorContext error_context) {
return (CollateCatalogEntry *)GetEntry(context, CatalogType::COLLATION_ENTRY, schema_name, name, if_exists,
error_context);
}
void Catalog::Alter(ClientContext &context, AlterInfo *info) {
ModifyCatalog();
auto lookup = LookupEntry(context, info->GetCatalogType(), info->schema, info->name);
D_ASSERT(lookup.Found()); // It must have thrown otherwise.
return lookup.schema->Alter(context, info);
}
idx_t Catalog::GetCatalogVersion() {
return catalog_version;
}
idx_t Catalog::ModifyCatalog() {
return catalog_version++;
}
} // namespace duckdb