Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 66 additions & 51 deletions src/duckdb/src/catalog/catalog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -838,17 +838,37 @@ CatalogEntryLookup Catalog::LookupEntry(CatalogEntryRetriever &retriever, const
return res;
}

static void ThrowDefaultTableAmbiguityException(CatalogEntryLookup &base_lookup, CatalogEntryLookup &default_table,
const string &name) {
auto entry_type = CatalogTypeToString(base_lookup.entry->type);
string fully_qualified_name_hint;
if (base_lookup.schema) {
fully_qualified_name_hint = StringUtil::Format(": '%s.%s.%s'", base_lookup.schema->catalog.GetName(),
base_lookup.schema->name, base_lookup.entry->name);
}
string fully_qualified_catalog_name_hint = StringUtil::Format(
": '%s.%s.%s'", default_table.schema->catalog.GetName(), default_table.schema->name, default_table.entry->name);
throw CatalogException(
"Ambiguity detected for '%s': this could either refer to the '%s' '%s', or the "
"attached catalog '%s' which has a default table. To avoid this error, either detach the catalog and "
"reattach under a different name, or use a fully qualified name for the '%s'%s or for the Catalog "
"Default Table%s.",
name, entry_type, name, name, entry_type, fully_qualified_name_hint, fully_qualified_catalog_name_hint);
}

CatalogEntryLookup Catalog::TryLookupEntry(CatalogEntryRetriever &retriever, const vector<CatalogLookup> &lookups,
const EntryLookupInfo &lookup_info, OnEntryNotFound if_not_found) {
const EntryLookupInfo &lookup_info, OnEntryNotFound if_not_found,
bool allow_default_table_lookup) {
auto &context = retriever.GetContext();
reference_set_t<SchemaCatalogEntry> schemas;
bool all_errors = true;
ErrorData error_data;
CatalogEntryLookup result;
for (auto &lookup : lookups) {
auto transaction = lookup.catalog.GetCatalogTransaction(context);
auto result = lookup.catalog.TryLookupEntryInternal(transaction, lookup.schema, lookup.lookup_info);
result = lookup.catalog.TryLookupEntryInternal(transaction, lookup.schema, lookup.lookup_info);
if (result.Found()) {
return result;
break;
}
if (result.schema) {
schemas.insert(*result.schema);
Expand All @@ -859,6 +879,29 @@ CatalogEntryLookup Catalog::TryLookupEntry(CatalogEntryRetriever &retriever, con
error_data = std::move(result.error);
}
}

// Special case for tables: we do a second lookup searching for catalogs with default tables that also match this
// lookup
if (lookup_info.GetCatalogType() == CatalogType::TABLE_ENTRY && allow_default_table_lookup) {
if (!result.Found()) {
result = TryLookupDefaultTable(retriever, lookup_info, false);
if (result.error.HasError()) {
error_data = std::move(result.error);
}
} else {
// allow_ignore_at_clause set to true to ensure `FROM <table_name> AT <version>` is considered
// ambiguous with a default table lookup in a catalog that does not support time travel
auto ambiguity_lookup = TryLookupDefaultTable(retriever, lookup_info, true);
if (ambiguity_lookup.Found()) {
ThrowDefaultTableAmbiguityException(result, ambiguity_lookup, lookup_info.GetEntryName());
}
}
}

if (result.Found()) {
return result;
}

if (all_errors && error_data.HasError()) {
error_data.Throw();
}
Expand All @@ -878,41 +921,29 @@ CatalogEntryLookup Catalog::TryLookupEntry(CatalogEntryRetriever &retriever, con
}
}

CatalogEntryLookup Catalog::TryLookupDefaultTable(CatalogEntryRetriever &retriever, const string &catalog,
const string &schema, const EntryLookupInfo &lookup_info,
OnEntryNotFound if_not_found) {
// Default tables of catalogs can only be accessed by the catalog name directly
if (!schema.empty() || !catalog.empty()) {
return {nullptr, nullptr, ErrorData()};
}

vector<CatalogLookup> catalog_by_name_lookups;
CatalogEntryLookup Catalog::TryLookupDefaultTable(CatalogEntryRetriever &retriever, const EntryLookupInfo &lookup_info,
bool allow_ignore_at_clause) {
auto catalog_by_name = GetCatalogEntry(retriever, lookup_info.GetEntryName());

if (catalog_by_name && catalog_by_name->HasDefaultTable()) {
catalog_by_name_lookups.emplace_back(*catalog_by_name, CatalogType::TABLE_ENTRY,
catalog_by_name->GetDefaultTableSchema(),
catalog_by_name->GetDefaultTable());
}
auto transaction = catalog_by_name->GetCatalogTransaction(retriever.GetContext());
QueryErrorContext context;

return TryLookupEntry(retriever, catalog_by_name_lookups, lookup_info, if_not_found);
}
string table_schema = catalog_by_name->GetDefaultTableSchema();
string table_name = catalog_by_name->GetDefaultTable();

static void ThrowDefaultTableAmbiguityException(CatalogEntryLookup &base_lookup, CatalogEntryLookup &default_table,
const string &name) {
auto entry_type = CatalogTypeToString(base_lookup.entry->type);
string fully_qualified_name_hint;
if (base_lookup.schema) {
fully_qualified_name_hint = StringUtil::Format(": '%s.%s.%s'", base_lookup.schema->catalog.GetName(),
base_lookup.schema->name, base_lookup.entry->name);
optional_ptr<BoundAtClause> at_clause;
if (!catalog_by_name->SupportsTimeTravel() && allow_ignore_at_clause) {
at_clause = nullptr;
} else {
at_clause = lookup_info.GetAtClause();
}

EntryLookupInfo info = EntryLookupInfo(CatalogType::TABLE_ENTRY, table_name, at_clause, context);
return catalog_by_name->TryLookupEntryInternal(transaction, table_schema, info);
}
string fully_qualified_catalog_name_hint = StringUtil::Format(
": '%s.%s.%s'", default_table.schema->catalog.GetName(), default_table.schema->name, default_table.entry->name);
throw CatalogException(
"Ambiguity detected for '%s': this could either refer to the '%s' '%s', or the "
"attached catalog '%s' which has a default table. To avoid this error, either detach the catalog and "
"reattach under a different name, or use a fully qualified name for the '%s'%s or for the Catalog "
"Default Table%s.",
name, entry_type, name, name, entry_type, fully_qualified_name_hint, fully_qualified_catalog_name_hint);

return {nullptr, nullptr, ErrorData()};
}

CatalogEntryLookup Catalog::TryLookupEntry(CatalogEntryRetriever &retriever, const string &catalog,
Expand Down Expand Up @@ -945,25 +976,9 @@ CatalogEntryLookup Catalog::TryLookupEntry(CatalogEntryRetriever &retriever, con
lookups.emplace_back(std::move(lookup));
}

// Do the main lookup
auto lookup_result = TryLookupEntry(retriever, lookups, lookup_info, if_not_found);

// Special case for tables: we do a second lookup searching for catalogs with default tables that also match this
// lookup
if (lookup_info.GetCatalogType() == CatalogType::TABLE_ENTRY) {
auto lookup_result_default_table =
TryLookupDefaultTable(retriever, catalog, schema, lookup_info, OnEntryNotFound::RETURN_NULL);

if (lookup_result_default_table.Found() && lookup_result.Found()) {
ThrowDefaultTableAmbiguityException(lookup_result, lookup_result_default_table, lookup_info.GetEntryName());
}

if (lookup_result_default_table.Found()) {
return lookup_result_default_table;
}
}
bool allow_default_table_lookup = catalog.empty() && schema.empty();

return lookup_result;
return TryLookupEntry(retriever, lookups, lookup_info, if_not_found, allow_default_table_lookup);
}

CatalogEntry &Catalog::GetEntry(ClientContext &context, CatalogType catalog_type, const string &catalog_name,
Expand Down
2 changes: 1 addition & 1 deletion src/duckdb/src/catalog/default/default_table_functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ FROM histogram_values(source, col_name, bin_count := bin_count, technique := tec
)"},
{DEFAULT_SCHEMA, "duckdb_logs_parsed", {"log_type"}, {}, R"(
SELECT * EXCLUDE (message), UNNEST(parse_duckdb_log_message(log_type, message))
FROM duckdb_logs
FROM duckdb_logs(denormalized_table=1)
WHERE type = log_type
)"},
{nullptr, nullptr, {nullptr}, {{nullptr, nullptr}}, nullptr}
Expand Down
2 changes: 1 addition & 1 deletion src/duckdb/src/catalog/default/default_views.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ static const DefaultView internal_views[] = {
{DEFAULT_SCHEMA, "duckdb_tables", "SELECT * FROM duckdb_tables() WHERE NOT internal"},
{DEFAULT_SCHEMA, "duckdb_types", "SELECT * FROM duckdb_types()"},
{DEFAULT_SCHEMA, "duckdb_views", "SELECT * FROM duckdb_views() WHERE NOT internal"},
{DEFAULT_SCHEMA, "duckdb_logs", "SELECT * exclude (l.rowid, l.context_id, c.context_id) FROM (SELECT row_number() OVER () AS rowid, * FROM duckdb_logs()) as l JOIN duckdb_log_contexts() as c ON l.context_id=c.context_id order by timestamp, l.rowid;"},
{DEFAULT_SCHEMA, "duckdb_logs", "SELECT * FROM duckdb_logs(denormalized_table=true)"},
{"pg_catalog", "pg_am", "SELECT 0 oid, 'art' amname, NULL amhandler, 'i' amtype"},
{"pg_catalog", "pg_prepared_statements", "SELECT name, statement, NULL prepare_time, parameter_types, result_types, NULL from_sql, NULL generic_plans, NULL custom_plans from duckdb_prepared_statements()"},
{"pg_catalog", "pg_attribute", "SELECT table_oid attrelid, column_name attname, data_type_id atttypid, 0 attstattarget, NULL attlen, column_index attnum, 0 attndims, -1 attcacheoff, case when data_type ilike '%decimal%' then numeric_precision*1000+numeric_scale else -1 end atttypmod, false attbyval, NULL attstorage, NULL attalign, NOT is_nullable attnotnull, column_default IS NOT NULL atthasdef, false atthasmissing, '' attidentity, '' attgenerated, false attisdropped, true attislocal, 0 attinhcount, 0 attcollation, NULL attcompression, NULL attacl, NULL attoptions, NULL attfdwoptions, NULL attmissingval FROM duckdb_columns()"},
Expand Down
Loading
Loading