From 4ef4537fd8be5525aeed8d48c2e32c57c5db19e4 Mon Sep 17 00:00:00 2001 From: DuckDB Labs GitHub Bot Date: Fri, 31 Jul 2026 14:35:00 +0000 Subject: [PATCH] Update vendored DuckDB sources to 2253530960 --- src/duckdb/src/catalog/catalog.cpp | 59 +++--- .../catalog_entry/duck_schema_entry.cpp | 5 +- .../catalog_entry/index_catalog_entry.cpp | 5 +- .../catalog_entry/macro_catalog_entry.cpp | 3 +- .../catalog_entry/schema_catalog_entry.cpp | 1 + .../catalog_entry/sequence_catalog_entry.cpp | 2 +- .../catalog_entry/table_catalog_entry.cpp | 1 + .../catalog_entry/trigger_catalog_entry.cpp | 6 +- .../catalog_entry/type_catalog_entry.cpp | 2 +- .../catalog_entry/view_catalog_entry.cpp | 3 +- .../src/catalog/catalog_search_path.cpp | 9 + .../common/exception/catalog_exception.cpp | 3 +- src/duckdb/src/common/hive_partitioning.cpp | 8 +- .../operator/persistent/physical_export.cpp | 14 +- .../schema/physical_create_trigger.cpp | 3 +- .../operator/schema/physical_drop.cpp | 9 +- .../src/function/pragma/pragma_queries.cpp | 17 +- .../src/function/scalar/sequence/nextval.cpp | 13 +- .../function/table/system/duckdb_indexes.cpp | 3 +- .../function/table/system/duckdb_tables.cpp | 3 +- .../function/table/version/pragma_version.cpp | 6 +- .../src/include/duckdb/catalog/catalog.hpp | 2 + .../duckdb/common/hive_partitioning.hpp | 2 +- .../function/pragma/pragma_functions.hpp | 4 +- .../include/duckdb/main/client_context.hpp | 17 +- .../src/include/duckdb/main/secret/secret.hpp | 12 ++ .../duckdb/parser/parsed_data/create_info.hpp | 3 + .../parser/parsed_data/create_table_info.hpp | 2 +- .../parser/parsed_data/create_view_info.hpp | 2 +- .../include/duckdb/parser/qualified_name.hpp | 34 +++- .../duckdb/parser/tableref/at_clause.hpp | 6 +- .../duckdb/parser/tableref/basetableref.hpp | 4 +- .../src/include/duckdb/planner/binder.hpp | 2 + .../expression_binder/index_binder.hpp | 2 + .../planner/tableref/bound_at_clause.hpp | 6 +- .../src/include/duckdb/storage/wal_entry.hpp | 132 +++++++++++-- src/duckdb/src/main/client_context.cpp | 56 +++--- src/duckdb/src/main/client_verify.cpp | 31 ++-- .../statistics/expression/propagate_cast.cpp | 22 +++ .../parsed_data/comment_on_column_info.cpp | 7 +- .../parser/parsed_data/create_index_info.cpp | 16 +- .../src/parser/parsed_data/create_info.cpp | 12 +- .../parser/parsed_data/create_table_info.cpp | 4 +- .../parser/parsed_data/create_view_info.cpp | 4 +- .../parsed_data/exported_table_data.cpp | 4 +- .../transformer/transform_create_index.cpp | 4 +- .../peg/transformer/transform_select.cpp | 2 +- src/duckdb/src/parser/qualified_name.cpp | 23 ++- src/duckdb/src/parser/tableref/at_clause.cpp | 2 +- src/duckdb/src/planner/binder.cpp | 5 + .../expression/bind_type_expression.cpp | 26 ++- .../planner/binder/statement/bind_alter.cpp | 18 +- .../binder/statement/bind_copy_database.cpp | 10 +- .../planner/binder/statement/bind_create.cpp | 13 +- .../binder/statement/bind_create_table.cpp | 14 +- .../planner/binder/statement/bind_drop.cpp | 51 +---- .../planner/binder/statement/bind_export.cpp | 6 +- .../planner/binder/tableref/bind_showref.cpp | 11 +- .../binder/tableref/bind_table_function.cpp | 12 +- src/duckdb/src/planner/column_qualifier.cpp | 7 +- .../expression_binder/index_binder.cpp | 13 +- .../planner/operator/logical_create_index.cpp | 6 +- src/duckdb/src/storage/checkpoint_manager.cpp | 4 +- src/duckdb/src/storage/data_table.cpp | 7 +- .../storage/serialization/serialize_nodes.cpp | 5 + .../serialization/serialize_parse_info.cpp | 7 + .../serialization/serialize_tableref.cpp | 11 +- .../storage/serialization/serialize_wal.cpp | 174 ++++++++++-------- src/duckdb/src/storage/table_index_list.cpp | 7 +- src/duckdb/src/storage/wal_replay.cpp | 59 ++++-- src/duckdb/src/storage/write_ahead_log.cpp | 19 +- 71 files changed, 663 insertions(+), 414 deletions(-) diff --git a/src/duckdb/src/catalog/catalog.cpp b/src/duckdb/src/catalog/catalog.cpp index 59c3966e3..906b5abfa 100644 --- a/src/duckdb/src/catalog/catalog.cpp +++ b/src/duckdb/src/catalog/catalog.cpp @@ -126,6 +126,16 @@ CatalogTransaction Catalog::GetCatalogTransaction(ClientContext &context) { return CatalogTransaction(*this, context); } +SchemaCatalogEntry &Catalog::GetEntrySchema(CatalogTransaction transaction, const QualifiedName &name) { + auto &path = name.Path(); + if (path.size() <= 3) { + return GetSchema(transaction, name.Schema()); + } + // nested entry ([catalog, schema_path..., name]): navigate the (nested) schema path + vector schema_path(path.begin() + 1, path.end() - 1); + return *GetSchema(transaction, schema_path, OnEntryNotFound::THROW_EXCEPTION); +} + //===--------------------------------------------------------------------===// // Table //===--------------------------------------------------------------------===// @@ -149,24 +159,15 @@ optional_ptr Catalog::CreateTable(CatalogTransaction transaction, } optional_ptr Catalog::CreateTable(CatalogTransaction transaction, BoundCreateTableInfo &info) { - auto &qname = info.base->GetQualifiedName(); - auto &path = qname.Path(); - optional_ptr schema; - if (path.size() > 3) { - // nested table ([catalog, schema_path..., name]): navigate the (nested) schema path - vector schema_path(path.begin() + 1, path.end() - 1); - schema = GetSchema(transaction, schema_path, OnEntryNotFound::THROW_EXCEPTION); - } else { - schema = GetSchema(transaction, qname.Schema()); - } - return CreateTable(transaction, *schema, info); + auto &schema = GetEntrySchema(transaction, info.base->GetQualifiedName()); + return CreateTable(transaction, schema, info); } //===--------------------------------------------------------------------===// // View //===--------------------------------------------------------------------===// optional_ptr Catalog::CreateView(CatalogTransaction transaction, CreateViewInfo &info) { - auto &schema = GetSchema(transaction, info.GetQualifiedName().Schema()); + auto &schema = GetEntrySchema(transaction, info.GetQualifiedName()); return CreateView(transaction, schema, info); } @@ -183,7 +184,7 @@ optional_ptr Catalog::CreateView(CatalogTransaction transaction, S // Sequence //===--------------------------------------------------------------------===// optional_ptr Catalog::CreateSequence(CatalogTransaction transaction, CreateSequenceInfo &info) { - auto &schema = GetSchema(transaction, info.GetQualifiedName().Schema()); + auto &schema = GetEntrySchema(transaction, info.GetQualifiedName()); return CreateSequence(transaction, schema, info); } @@ -200,7 +201,7 @@ optional_ptr Catalog::CreateSequence(CatalogTransaction transactio // Type //===--------------------------------------------------------------------===// optional_ptr Catalog::CreateType(CatalogTransaction transaction, CreateTypeInfo &info) { - auto &schema = GetSchema(transaction, info.GetQualifiedName().Schema()); + auto &schema = GetEntrySchema(transaction, info.GetQualifiedName()); return CreateType(transaction, schema, info); } @@ -217,7 +218,7 @@ optional_ptr Catalog::CreateType(CatalogTransaction transaction, S // Table Function //===--------------------------------------------------------------------===// optional_ptr Catalog::CreateTableFunction(CatalogTransaction transaction, CreateTableFunctionInfo &info) { - auto &schema = GetSchema(transaction, info.GetQualifiedName().Schema()); + auto &schema = GetEntrySchema(transaction, info.GetQualifiedName()); return CreateTableFunction(transaction, schema, info); } @@ -239,7 +240,7 @@ optional_ptr Catalog::CreateTableFunction(ClientContext &context, // Copy Function //===--------------------------------------------------------------------===// optional_ptr Catalog::CreateCopyFunction(CatalogTransaction transaction, CreateCopyFunctionInfo &info) { - auto &schema = GetSchema(transaction, info.GetQualifiedName().Schema()); + auto &schema = GetEntrySchema(transaction, info.GetQualifiedName()); return CreateCopyFunction(transaction, schema, info); } @@ -257,7 +258,7 @@ optional_ptr Catalog::CreateCopyFunction(CatalogTransaction transa //===--------------------------------------------------------------------===// optional_ptr Catalog::CreatePragmaFunction(CatalogTransaction transaction, CreatePragmaFunctionInfo &info) { - auto &schema = GetSchema(transaction, info.GetQualifiedName().Schema()); + auto &schema = GetEntrySchema(transaction, info.GetQualifiedName()); return CreatePragmaFunction(transaction, schema, info); } @@ -274,7 +275,7 @@ optional_ptr Catalog::CreatePragmaFunction(CatalogTransaction tran // Function //===--------------------------------------------------------------------===// optional_ptr Catalog::CreateFunction(CatalogTransaction transaction, CreateFunctionInfo &info) { - auto &schema = GetSchema(transaction, info.GetQualifiedName().Schema()); + auto &schema = GetEntrySchema(transaction, info.GetQualifiedName()); return CreateFunction(transaction, schema, info); } @@ -296,7 +297,7 @@ optional_ptr Catalog::AddFunction(ClientContext &context, CreateFu // Collation //===--------------------------------------------------------------------===// optional_ptr Catalog::CreateCollation(CatalogTransaction transaction, CreateCollationInfo &info) { - auto &schema = GetSchema(transaction, info.GetQualifiedName().Schema()); + auto &schema = GetEntrySchema(transaction, info.GetQualifiedName()); return CreateCollation(transaction, schema, info); } @@ -314,7 +315,7 @@ optional_ptr Catalog::CreateCollation(CatalogTransaction transacti //===--------------------------------------------------------------------===// optional_ptr Catalog::CreateCoordinateSystem(CatalogTransaction transaction, CreateCoordinateSystemInfo &info) { - auto &schema = GetSchema(transaction, info.GetQualifiedName().Schema()); + auto &schema = GetEntrySchema(transaction, info.GetQualifiedName()); return CreateCoordinateSystem(transaction, schema, info); } @@ -331,7 +332,7 @@ optional_ptr Catalog::CreateCoordinateSystem(CatalogTransaction tr // Index //===--------------------------------------------------------------------===// optional_ptr Catalog::CreateIndex(CatalogTransaction transaction, CreateIndexInfo &info) { - auto &schema = GetSchema(transaction, info.GetQualifiedName().Schema()); + auto &schema = GetEntrySchema(transaction, info.GetQualifiedName()); auto &table = schema.GetEntry(transaction, CatalogType::TABLE_ENTRY, info.table)->Cast(); return schema.CreateIndex(transaction, info, table); } @@ -1109,11 +1110,15 @@ CatalogEntryLookup Catalog::TryLookupEntry(CatalogEntryRetriever &retriever, con } // If we have a specific schema name and no schemas were found, the schema doesn't exist. - // Throw an error about the schema instead of the table - if (schemas.empty() && !lookups.empty() && lookup_info.GetCatalogType() == CatalogType::TABLE_ENTRY) { + // Throw an error about the schema instead of the entry. A nested schema path is unambiguous, so we report it + // for every entry type; for a single schema level we only do so for tables (the message would otherwise hide + // the search-path suggestions that are useful for e.g. functions). + auto &lookup_path = + lookups.empty() ? lookup_info.GetQualifiedName().Path() : lookups[0].lookup_info.GetQualifiedName().Path(); + bool report_missing_schema = lookup_info.GetCatalogType() == CatalogType::TABLE_ENTRY || lookup_path.size() > 3; + if (schemas.empty() && !lookups.empty() && report_missing_schema) { // the schema qualification is everything between the catalog and the entry name - for a nested schema this // is more than one component - auto &lookup_path = lookups[0].lookup_info.GetQualifiedName().Path(); vector schema_components; for (idx_t i = lookup_path.size() > 2 ? 1 : 0; i + 1 < lookup_path.size(); i++) { if (!lookup_path[i].empty()) { @@ -1125,8 +1130,8 @@ CatalogEntryLookup Catalog::TryLookupEntry(CatalogEntryRetriever &retriever, con string relation_name = schema_name + "." + lookup_info.GetEntryName(); auto except = CatalogException(lookup_info.GetErrorContext(), - "Table with name \"%s\" does not exist because schema \"%s\" does not exist.", - relation_name, schema_name); + "%s with name \"%s\" does not exist because schema \"%s\" does not exist.", + CatalogTypeToString(lookup_info.GetCatalogType()), relation_name, schema_name); return {nullptr, nullptr, ErrorData(except)}; } } @@ -1430,7 +1435,7 @@ void Catalog::Alter(CatalogTransaction transaction, AlterInfo &info) { return lookup.schema->Alter(transaction, info); } D_ASSERT(info.if_not_found == OnEntryNotFound::THROW_EXCEPTION); - auto &schema = GetSchema(transaction, info.GetQualifiedName().Schema()); + auto &schema = GetEntrySchema(transaction, info.GetQualifiedName()); return schema.Alter(transaction, info); } diff --git a/src/duckdb/src/catalog/catalog_entry/duck_schema_entry.cpp b/src/duckdb/src/catalog/catalog_entry/duck_schema_entry.cpp index e7785e7c2..4f9dcd31c 100644 --- a/src/duckdb/src/catalog/catalog_entry/duck_schema_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/duck_schema_entry.cpp @@ -48,7 +48,6 @@ namespace duckdb { static void FindForeignKeyInformation(TableCatalogEntry &table, AlterForeignKeyType alter_fk_type, vector> &fk_arrays) { auto &constraints = table.GetConstraints(); - auto &catalog = table.ParentCatalog(); auto &name = table.name; for (idx_t i = 0; i < constraints.size(); i++) { auto &cond = constraints[i]; @@ -57,8 +56,8 @@ static void FindForeignKeyInformation(TableCatalogEntry &table, AlterForeignKeyT } auto &fk = cond->Cast(); if (fk.info.type == ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE) { - AlterEntryData alter_data(QualifiedName(catalog.GetName(), fk.info.schema, fk.info.table), - OnEntryNotFound::THROW_EXCEPTION); + // the referenced table lives in the same (possibly nested) schema as this table + AlterEntryData alter_data(table.schema.GetQualifiedName(fk.info.table), OnEntryNotFound::THROW_EXCEPTION); fk_arrays.push_back(make_uniq(std::move(alter_data), name, fk.pk_columns, fk.fk_columns, fk.info.pk_keys, fk.info.fk_keys, alter_fk_type)); diff --git a/src/duckdb/src/catalog/catalog_entry/index_catalog_entry.cpp b/src/duckdb/src/catalog/catalog_entry/index_catalog_entry.cpp index e82a7d498..dc5489fc6 100644 --- a/src/duckdb/src/catalog/catalog_entry/index_catalog_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/index_catalog_entry.cpp @@ -1,5 +1,7 @@ #include "duckdb/catalog/catalog_entry/index_catalog_entry.hpp" +#include "duckdb/catalog/catalog_entry/schema_catalog_entry.hpp" + namespace duckdb { IndexCatalogEntry::IndexCatalogEntry(Catalog &catalog, SchemaCatalogEntry &schema, CreateIndexInfo &info) @@ -21,7 +23,7 @@ IndexCatalogEntry::IndexCatalogEntry(Catalog &catalog, SchemaCatalogEntry &schem unique_ptr IndexCatalogEntry::GetInfo() const { auto result = make_uniq(); - result->SetQualifiedName(QualifiedName({GetSchemaName()}, name)); + result->SetQualifiedName(schema.GetQualifiedName(name)); result->table = GetTableName(); result->temporary = temporary; @@ -47,6 +49,7 @@ unique_ptr IndexCatalogEntry::GetInfo() const { string IndexCatalogEntry::ToSQL() const { auto info = GetInfo(); + info->StripCatalogQualification(); return info->ToString(); } diff --git a/src/duckdb/src/catalog/catalog_entry/macro_catalog_entry.cpp b/src/duckdb/src/catalog/catalog_entry/macro_catalog_entry.cpp index 4b09603df..c895672b1 100644 --- a/src/duckdb/src/catalog/catalog_entry/macro_catalog_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/macro_catalog_entry.cpp @@ -41,7 +41,7 @@ unique_ptr TableMacroCatalogEntry::Copy(ClientContext &context) co unique_ptr MacroCatalogEntry::GetInfo() const { auto info = make_uniq(type); - info->SetQualifiedName(QualifiedName(catalog.GetName(), schema.name, name)); + info->SetQualifiedName(schema.GetQualifiedName(name)); for (auto &function : macros) { info->macros.push_back(function->Copy()); } @@ -54,6 +54,7 @@ unique_ptr MacroCatalogEntry::GetInfo() const { string MacroCatalogEntry::ToSQL() const { auto create_info = GetInfo(); + create_info->StripCatalogQualification(); return create_info->ToString(); } diff --git a/src/duckdb/src/catalog/catalog_entry/schema_catalog_entry.cpp b/src/duckdb/src/catalog/catalog_entry/schema_catalog_entry.cpp index 632f84454..606ef6642 100644 --- a/src/duckdb/src/catalog/catalog_entry/schema_catalog_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/schema_catalog_entry.cpp @@ -86,6 +86,7 @@ unique_ptr SchemaCatalogEntry::GetInfo() const { string SchemaCatalogEntry::ToSQL() const { auto create_schema_info = GetInfo(); + create_schema_info->StripCatalogQualification(); return create_schema_info->ToString(); } diff --git a/src/duckdb/src/catalog/catalog_entry/sequence_catalog_entry.cpp b/src/duckdb/src/catalog/catalog_entry/sequence_catalog_entry.cpp index 28bad580d..1948a7603 100644 --- a/src/duckdb/src/catalog/catalog_entry/sequence_catalog_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/sequence_catalog_entry.cpp @@ -115,7 +115,7 @@ unique_ptr SequenceCatalogEntry::GetInfo() const { auto seq_data = GetData(); auto result = make_uniq(); - result->SetQualifiedName(QualifiedName(catalog.GetName(), schema.name, name)); + result->SetQualifiedName(schema.GetQualifiedName(name)); result->usage_count = seq_data.usage_count; result->increment = seq_data.increment; result->min_value = seq_data.min_value; diff --git a/src/duckdb/src/catalog/catalog_entry/table_catalog_entry.cpp b/src/duckdb/src/catalog/catalog_entry/table_catalog_entry.cpp index 96eaf6ee9..252d2d51d 100644 --- a/src/duckdb/src/catalog/catalog_entry/table_catalog_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/table_catalog_entry.cpp @@ -207,6 +207,7 @@ string TableCatalogEntry::ColumnNamesToSQL(const ColumnList &columns) { string TableCatalogEntry::ToSQL() const { auto create_info = GetInfo(); + create_info->StripCatalogQualification(); return create_info->ToString(); } diff --git a/src/duckdb/src/catalog/catalog_entry/trigger_catalog_entry.cpp b/src/duckdb/src/catalog/catalog_entry/trigger_catalog_entry.cpp index 55ad3a0f2..8cc045811 100644 --- a/src/duckdb/src/catalog/catalog_entry/trigger_catalog_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/trigger_catalog_entry.cpp @@ -27,7 +27,7 @@ unique_ptr TriggerCatalogEntry::Copy(ClientContext &context) const unique_ptr TriggerCatalogEntry::GetInfo() const { auto result = make_uniq(); - result->SetQualifiedName(QualifiedName(catalog.GetName(), schema.name, name)); + result->SetQualifiedName(schema.GetQualifiedName(name)); result->base_table = unique_ptr_cast(base_table->Copy()); result->timing = timing; result->event_type = event_type; @@ -60,9 +60,7 @@ string TriggerCatalogEntry::ToSQL() const { } } ss << " ON "; - ss << QualifiedName(base_table->GetQualifiedName().Catalog(), base_table->GetQualifiedName().Schema(), - base_table->Table()) - .ToString(QualifiedNameToStringMode::HIDE_DEFAULT_SCHEMA); + ss << base_table->GetQualifiedName().ToString(QualifiedNameToStringMode::HIDE_DEFAULT_SCHEMA); if (!referencing_new_table.empty() || !referencing_old_table.empty()) { ss << " REFERENCING"; if (!referencing_new_table.empty()) { diff --git a/src/duckdb/src/catalog/catalog_entry/type_catalog_entry.cpp b/src/duckdb/src/catalog/catalog_entry/type_catalog_entry.cpp index 0255ba3aa..514b293e7 100644 --- a/src/duckdb/src/catalog/catalog_entry/type_catalog_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/type_catalog_entry.cpp @@ -31,7 +31,7 @@ unique_ptr TypeCatalogEntry::Copy(ClientContext &context) const { unique_ptr TypeCatalogEntry::GetInfo() const { auto result = make_uniq(); - result->SetQualifiedName(QualifiedName(catalog.GetName(), schema.name, name)); + result->SetQualifiedName(schema.GetQualifiedName(name)); result->type = user_type; result->extension_name = extension_name; result->dependencies = dependencies; diff --git a/src/duckdb/src/catalog/catalog_entry/view_catalog_entry.cpp b/src/duckdb/src/catalog/catalog_entry/view_catalog_entry.cpp index 07456852f..d1fdefd12 100644 --- a/src/duckdb/src/catalog/catalog_entry/view_catalog_entry.cpp +++ b/src/duckdb/src/catalog/catalog_entry/view_catalog_entry.cpp @@ -48,7 +48,7 @@ ViewCatalogEntry::ViewCatalogEntry(Catalog &catalog, SchemaCatalogEntry &schema, unique_ptr ViewCatalogEntry::GetInfo() const { auto result = make_uniq(); - result->SetQualifiedName(QualifiedName({schema.name}, name)); + result->SetQualifiedName(schema.GetQualifiedName(name)); result->sql = sql; result->query = query ? unique_ptr_cast(query->Copy()) : nullptr; result->aliases = aliases; @@ -197,6 +197,7 @@ string ViewCatalogEntry::ToSQL() const { return sql; } auto info = GetInfo(); + info->StripCatalogQualification(); auto result = info->ToString(); return result; } diff --git a/src/duckdb/src/catalog/catalog_search_path.cpp b/src/duckdb/src/catalog/catalog_search_path.cpp index cce8dab81..2ee1d6a76 100644 --- a/src/duckdb/src/catalog/catalog_search_path.cpp +++ b/src/duckdb/src/catalog/catalog_search_path.cpp @@ -191,6 +191,15 @@ void CatalogSearchPath::Set(vector new_paths, CatalogSetPath } } } + if (!path.GetCatalog().empty()) { + // "a.b" can also name a nested schema - give a clearer error in that case + vector nested_path {path.GetCatalog(), path.GetSchema()}; + if (Catalog::GetSchema(context, Identifier(), nested_path, OnEntryNotFound::RETURN_NULL)) { + throw NotImplementedException("%s: \"%s\" is a nested schema - nested schemas cannot be used in the " + "search path", + GetSetName(set_type), path.ToString()); + } + } throw CatalogException("%s: No catalog + schema named \"%s\" found.", GetSetName(set_type), path.ToString()); } if (set_type == CatalogSetPathType::SET_SCHEMA) { diff --git a/src/duckdb/src/common/exception/catalog_exception.cpp b/src/duckdb/src/common/exception/catalog_exception.cpp index a3aacf1a3..23042d67b 100644 --- a/src/duckdb/src/common/exception/catalog_exception.cpp +++ b/src/duckdb/src/common/exception/catalog_exception.cpp @@ -26,7 +26,8 @@ CatalogException CatalogException::MissingEntry(const EntryLookupInfo &lookup_in } string version_info; if (at_clause) { - version_info += " at " + StringUtil::Lower(at_clause->Unit()) + " " + at_clause->GetValue().ToString(); + version_info += + " at " + StringUtil::Lower(at_clause->Unit().GetIdentifierName()) + " " + at_clause->GetValue().ToString(); } auto extra_info = Exception::InitializeExtraInfo("MISSING_ENTRY", context.query_location); diff --git a/src/duckdb/src/common/hive_partitioning.cpp b/src/duckdb/src/common/hive_partitioning.cpp index e3c827f26..76ffe210e 100644 --- a/src/duckdb/src/common/hive_partitioning.cpp +++ b/src/duckdb/src/common/hive_partitioning.cpp @@ -131,14 +131,18 @@ std::map HivePartitioning::Parse(const string &filename) { Value HivePartitioning::GetValue(ClientContext &context, const string &key, const string &str_val, const LogicalType &type) { - // Handle nulls - if (IsNull(str_val)) { + // On SQLNULL, DuckDB writes "__HIVE_DEFAULT_PARTITION__", instead of string version "NULL". + if (str_val == "__HIVE_DEFAULT_PARTITION__") { return Value(type); } if (type.id() == LogicalTypeId::VARCHAR) { // for string values we can directly return the type return Value(Unescape(str_val)); } + // Handle Hive NULL markers for non-string partition types + if (StringUtil::CIEquals(str_val, "NULL")) { + return Value(type); + } if (str_val.empty()) { // empty strings are NULL for non-string types return Value(type); diff --git a/src/duckdb/src/execution/operator/persistent/physical_export.cpp b/src/duckdb/src/execution/operator/persistent/physical_export.cpp index fbc98105f..28cca0224 100644 --- a/src/duckdb/src/execution/operator/persistent/physical_export.cpp +++ b/src/duckdb/src/execution/operator/persistent/physical_export.cpp @@ -35,9 +35,8 @@ static void WriteCatalogEntries(stringstream &ss, catalog_entry_vector_t &entrie } auto create_info = entry.get().GetInfo(); try { - // Strip the catalog from the info - create_info->SetQualifiedName(QualifiedName(Identifier(), create_info->GetQualifiedName().Schema(), - create_info->GetQualifiedName().Name())); + // the catalog is implied by the database the export is imported into - keep only the schema path + create_info->StripCatalogQualification(); auto to_string = create_info->ToString(); ss << to_string; } catch (const NotImplementedException &) { @@ -60,12 +59,11 @@ static void WriteCopyStatement(FileSystem &fs, stringstream &ss, CopyInfo &info, ss << "COPY "; //! NOTE: The catalog is explicitly not set here - if (exported_table.qualified_name.Schema() != DEFAULT_SCHEMA && !exported_table.qualified_name.Schema().empty()) { - ss << SQLIdentifier(exported_table.qualified_name.Schema()) << "."; - } - + auto table_name = exported_table.qualified_name; + table_name.StripCatalog(); auto file_path = StringUtil::Replace(exported_table.file_path, "\\", "/"); - ss << StringUtil::Format("%s FROM %s (", SQLIdentifier(exported_table.qualified_name.Name()), SQLString(file_path)); + ss << StringUtil::Format("%s FROM %s (", table_name.ToString(QualifiedNameToStringMode::HIDE_DEFAULT_SCHEMA), + SQLString(file_path)); // write the copy options ss << "FORMAT '" << info.format << "'"; if (info.format == "csv") { diff --git a/src/duckdb/src/execution/operator/schema/physical_create_trigger.cpp b/src/duckdb/src/execution/operator/schema/physical_create_trigger.cpp index b7ee25313..d062bc7d7 100644 --- a/src/duckdb/src/execution/operator/schema/physical_create_trigger.cpp +++ b/src/duckdb/src/execution/operator/schema/physical_create_trigger.cpp @@ -11,8 +11,7 @@ SourceResultType PhysicalCreateTrigger::GetDataInternal(ExecutionContext &contex // reference preserves the name exactly as written, which may be a two-part `catalog.table` reference that would // otherwise be misread as `schema.table` here. auto &table = Catalog::GetEntry( - context.client, QualifiedName(info->GetQualifiedName().Catalog(), info->GetQualifiedName().Schema(), - info->base_table->GetQualifiedName().Name())); + context.client, info->GetQualifiedName().WithName(info->base_table->GetQualifiedName().Name())); auto transaction = catalog.GetCatalogTransaction(context.client); table.CreateTrigger(transaction, *info); diff --git a/src/duckdb/src/execution/operator/schema/physical_drop.cpp b/src/duckdb/src/execution/operator/schema/physical_drop.cpp index 6df703f8a..487b3a51e 100644 --- a/src/duckdb/src/execution/operator/schema/physical_drop.cpp +++ b/src/duckdb/src/execution/operator/schema/physical_drop.cpp @@ -80,12 +80,9 @@ SourceResultType PhysicalDrop::GetDataInternal(ExecutionContext &context, DataCh break; } default: { - // for a nested target the path is [catalog, schema_path..., name] and .Catalog() is empty, so read the catalog - // from the leading component; otherwise use .Catalog() (which may be empty -> the default catalog, e.g. for an - // unresolved DROP ... IF EXISTS of a missing entry) - auto &qname = info->GetQualifiedName(); - auto &catalog_name = qname.Path().size() > 3 ? qname.Path().front() : qname.Catalog(); - auto &catalog = Catalog::GetCatalog(context.client, catalog_name); + // the catalog may be empty -> the default catalog (e.g. for an unresolved DROP ... IF EXISTS of a missing + // entry) + auto &catalog = Catalog::GetCatalog(context.client, info->GetQualifiedName().Catalog()); catalog.DropEntry(context.client, *info); break; } diff --git a/src/duckdb/src/function/pragma/pragma_queries.cpp b/src/duckdb/src/function/pragma/pragma_queries.cpp index c1b65ef89..688199e4b 100644 --- a/src/duckdb/src/function/pragma/pragma_queries.cpp +++ b/src/duckdb/src/function/pragma/pragma_queries.cpp @@ -21,14 +21,19 @@ static string PragmaTableInfo(ClientContext &context, const FunctionParameters & return StringUtil::Format("SELECT * FROM pragma_table_info(%s);", SQLString(parameters.values[0].ToString())); } -string PragmaShowTables(const string &database, const string &schema) { +string PragmaShowTables(const string &database, const string &schema, optional_idx schema_oid) { string where_clause = ""; vector where_conditions; - if (!database.empty()) { - where_conditions.push_back(StringUtil::Format("lower(database_name) = lower(%s)", SQLString(database))); - } - if (!schema.empty()) { - where_conditions.push_back(StringUtil::Format("lower(schema_name) = lower(%s)", SQLString(schema))); + if (schema_oid.IsValid()) { + // the schema was resolved to a (possibly nested) schema entry - filter on its oid + where_conditions.push_back(StringUtil::Format("schema_oid = %llu", schema_oid.GetIndex())); + } else { + if (!database.empty()) { + where_conditions.push_back(StringUtil::Format("lower(database_name) = lower(%s)", SQLString(database))); + } + if (!schema.empty()) { + where_conditions.push_back(StringUtil::Format("lower(schema_name) = lower(%s)", SQLString(schema))); + } } if (where_conditions.empty()) { where_conditions.push_back("in_search_path(database_name, schema_name)"); diff --git a/src/duckdb/src/function/scalar/sequence/nextval.cpp b/src/duckdb/src/function/scalar/sequence/nextval.cpp index 0502e4058..8723002d7 100644 --- a/src/duckdb/src/function/scalar/sequence/nextval.cpp +++ b/src/duckdb/src/function/scalar/sequence/nextval.cpp @@ -35,16 +35,15 @@ struct SetValValueOperator { } }; -SequenceCatalogEntry &BindSequence(Binder &binder, QualifiedName name) { - // resolve the (optional) catalog/schema qualification and fetch the sequence from the catalog - Binder::BindSchemaOrCatalog(binder.context, name); - EntryLookupInfo sequence_lookup(CatalogType::SEQUENCE_ENTRY, name); +SequenceCatalogEntry &BindSequence(Binder &binder, const QualifiedName &name) { + // resolve the (possibly nested) catalog/schema qualification and fetch the sequence from the catalog + EntryLookupInfo sequence_lookup(CatalogType::SEQUENCE_ENTRY, Binder::BindTableName(binder.EntryRetriever(), name)); return binder.EntryRetriever().GetEntry(sequence_lookup)->Cast(); } -SequenceCatalogEntry &BindSequenceFromContext(ClientContext &context, QualifiedName name) { - Binder::BindSchemaOrCatalog(context, name); - return Catalog::GetEntry(context, name); +SequenceCatalogEntry &BindSequenceFromContext(ClientContext &context, const QualifiedName &name) { + CatalogEntryRetriever retriever(context); + return Catalog::GetEntry(context, Binder::BindTableName(retriever, name)); } SequenceCatalogEntry &BindSequence(Binder &binder, const Identifier &name) { diff --git a/src/duckdb/src/function/table/system/duckdb_indexes.cpp b/src/duckdb/src/function/table/system/duckdb_indexes.cpp index ac876745e..0d1c9f54b 100644 --- a/src/duckdb/src/function/table/system/duckdb_indexes.cpp +++ b/src/duckdb/src/function/table/system/duckdb_indexes.cpp @@ -140,8 +140,9 @@ void DuckDBIndexesFunction(ClientContext &context, TableFunctionInput &data_p, D index_name.Append(Value(index.name)); index_oid.Append(Value::BIGINT(NumericCast(index.oid))); // find the table in the catalog + // the index lives in the same (possibly nested) schema as its table auto &table_entry = index.schema.catalog.GetEntry( - context, QualifiedName(index.schema.catalog.GetName(), index.GetSchemaName(), index.GetTableName())); + context, index.schema.GetQualifiedName(index.GetTableName())); table_name.Append(Value(table_entry.name)); table_oid.Append(Value::BIGINT(NumericCast(table_entry.oid))); comment.Append(Value(index.comment)); diff --git a/src/duckdb/src/function/table/system/duckdb_tables.cpp b/src/duckdb/src/function/table/system/duckdb_tables.cpp index f2e35e3b3..965ceafb6 100644 --- a/src/duckdb/src/function/table/system/duckdb_tables.cpp +++ b/src/duckdb/src/function/table/system/duckdb_tables.cpp @@ -168,8 +168,7 @@ void DuckDBTablesFunction(ClientContext &context, TableFunctionInput &data_p, Da index_count.Append(Value::BIGINT(NumericCast(storage_info.index_info.size()))); check_constraint_count.Append(Value::BIGINT(NumericCast(CheckConstraintCount(table)))); auto table_info = table.GetInfo(); - table_info->SetQualifiedName(QualifiedName(Identifier(), table_info->GetQualifiedName().Schema(), - table_info->GetQualifiedName().Name())); + table_info->StripCatalogQualification(); sql.Append(Value(table_info->ToString())); count++; } diff --git a/src/duckdb/src/function/table/version/pragma_version.cpp b/src/duckdb/src/function/table/version/pragma_version.cpp index def464979..3d6ec4415 100644 --- a/src/duckdb/src/function/table/version/pragma_version.cpp +++ b/src/duckdb/src/function/table/version/pragma_version.cpp @@ -1,5 +1,5 @@ #ifndef DUCKDB_PATCH_VERSION -#define DUCKDB_PATCH_VERSION "0-alpha36155" +#define DUCKDB_PATCH_VERSION "0-alpha36255" #endif #ifndef DUCKDB_MINOR_VERSION #define DUCKDB_MINOR_VERSION 0 @@ -8,10 +8,10 @@ #define DUCKDB_MAJOR_VERSION 2 #endif #ifndef DUCKDB_VERSION -#define DUCKDB_VERSION "v2.0.0-alpha36155" +#define DUCKDB_VERSION "v2.0.0-alpha36255" #endif #ifndef DUCKDB_SOURCE_ID -#define DUCKDB_SOURCE_ID "76361ce4fb" +#define DUCKDB_SOURCE_ID "2253530960" #endif #include "duckdb/function/table/system_functions.hpp" #include "duckdb/main/database.hpp" diff --git a/src/duckdb/src/include/duckdb/catalog/catalog.hpp b/src/duckdb/src/include/duckdb/catalog/catalog.hpp index 78fdaf812..39810ef39 100644 --- a/src/duckdb/src/include/duckdb/catalog/catalog.hpp +++ b/src/duckdb/src/include/duckdb/catalog/catalog.hpp @@ -262,6 +262,8 @@ class Catalog { //! Look up a (possibly nested) schema by its path (outermost first) in this catalog DUCKDB_API optional_ptr GetSchema(CatalogTransaction transaction, const vector &schema_path, OnEntryNotFound if_not_found); + //! Resolve the (possibly nested) schema an entry lives in from its qualified name ([catalog, schema..., name]) + DUCKDB_API SchemaCatalogEntry &GetEntrySchema(CatalogTransaction transaction, const QualifiedName &name); [[deprecated("Fold the catalog into the EntryLookupInfo and use GetSchema(context, " "EntryLookupInfo)")]] DUCKDB_API static optional_ptr GetSchema(ClientContext &context, const Identifier &catalog_name, const EntryLookupInfo &schema_lookup, diff --git a/src/duckdb/src/include/duckdb/common/hive_partitioning.hpp b/src/duckdb/src/include/duckdb/common/hive_partitioning.hpp index 18ede7770..a217359f7 100644 --- a/src/duckdb/src/include/duckdb/common/hive_partitioning.hpp +++ b/src/duckdb/src/include/duckdb/common/hive_partitioning.hpp @@ -41,7 +41,7 @@ class HivePartitioning { DUCKDB_API static string Escape(const string &input); //! Unescape a hive partition key or value encoded using URL encoding DUCKDB_API static string Unescape(const string &input); - //! Whether the column is "NULL"/"__HIVE_DEFAULT_PARTITION" + //! Whether the value is a null marker when detecting Hive partition types DUCKDB_API static bool IsNull(const string &input); }; diff --git a/src/duckdb/src/include/duckdb/function/pragma/pragma_functions.hpp b/src/duckdb/src/include/duckdb/function/pragma/pragma_functions.hpp index a1dd91f65..8b846583a 100644 --- a/src/duckdb/src/include/duckdb/function/pragma/pragma_functions.hpp +++ b/src/duckdb/src/include/duckdb/function/pragma/pragma_functions.hpp @@ -10,6 +10,7 @@ #include "duckdb/function/pragma_function.hpp" #include "duckdb/function/built_in_functions.hpp" +#include "duckdb/common/optional_idx.hpp" namespace duckdb { @@ -21,7 +22,8 @@ struct PragmaFunctions { static void RegisterFunction(BuiltinFunctions &set); }; -string PragmaShowTables(const string &catalog = "", const string &schema = ""); +string PragmaShowTables(const string &catalog = "", const string &schema = "", + optional_idx schema_oid = optional_idx()); string PragmaShowTablesExpanded(); string PragmaShowDatabases(); string PragmaShowVariables(); diff --git a/src/duckdb/src/include/duckdb/main/client_context.hpp b/src/duckdb/src/include/duckdb/main/client_context.hpp index 631921f3d..5aaa6267c 100644 --- a/src/duckdb/src/include/duckdb/main/client_context.hpp +++ b/src/duckdb/src/include/duckdb/main/client_context.hpp @@ -288,15 +288,14 @@ class ClientContext : public enable_shared_from_this { //! Parse statements from a query vector> ParseStatementsInternal(ClientContextLock &lock, const string &query); - void StatementVerification(ClientContextLock &lock, const string &query, unique_ptr &statement, + void StatementVerification(ClientContextLock &lock, unique_ptr &statement, PendingQueryParameters query_parameters); void InitialCleanup(ClientContextLock &lock); //! Internal clean up, does not lock. Caller must hold the context_lock. void CleanupInternal(ClientContextLock &lock, BaseQueryResult *result = nullptr, bool invalidate_transaction = false); - unique_ptr PendingStatement(ClientContextLock &lock, const string &query, - unique_ptr statement, + unique_ptr PendingStatement(ClientContextLock &lock, unique_ptr statement, const PendingQueryParameters ¶meters); unique_ptr PendingPreparedStatementInternal(ClientContextLock &lock, shared_ptr statement_data_p, @@ -304,14 +303,12 @@ class ClientContext : public enable_shared_from_this { void CheckIfPreparedStatementIsExecutable(PreparedStatementData &statement); //! Internally prepare a SQL statement. Caller must hold the context_lock. - shared_ptr CreatePreparedStatement(ClientContextLock &lock, const string &query, + shared_ptr CreatePreparedStatement(ClientContextLock &lock, unique_ptr statement, PendingQueryParameters parameters); - unique_ptr PendingStatementInternal(ClientContextLock &lock, const string &query, - unique_ptr statement, + unique_ptr PendingStatementInternal(ClientContextLock &lock, unique_ptr statement, const PendingQueryParameters ¶meters); - unique_ptr RunStatementInternal(ClientContextLock &lock, const string &query, - unique_ptr statement, + unique_ptr RunStatementInternal(ClientContextLock &lock, unique_ptr statement, const PendingQueryParameters ¶meters, bool verify = true); unique_ptr PrepareInternal(ClientContextLock &lock, unique_ptr statement); void LogQueryInternal(ClientContextLock &lock, const string &query); @@ -320,7 +317,7 @@ class ClientContext : public enable_shared_from_this { unique_ptr LockContext(); - void BeginQueryInternal(ClientContextLock &lock, const string &query); + void BeginQueryInternal(ClientContextLock &lock, const SQLStatement &statement); ErrorData EndQueryInternal(ClientContextLock &lock, bool success, bool invalidate_transaction, optional_ptr previous_error); @@ -334,7 +331,7 @@ class ClientContext : public enable_shared_from_this { template unique_ptr ErrorResult(ErrorData error, const string &query = string()); - shared_ptr CreatePreparedStatementInternal(ClientContextLock &lock, const string &query, + shared_ptr CreatePreparedStatementInternal(ClientContextLock &lock, unique_ptr statement, PendingQueryParameters parameters); diff --git a/src/duckdb/src/include/duckdb/main/secret/secret.hpp b/src/duckdb/src/include/duckdb/main/secret/secret.hpp index d002387a6..6564c2d78 100644 --- a/src/duckdb/src/include/duckdb/main/secret/secret.hpp +++ b/src/duckdb/src/include/duckdb/main/secret/secret.hpp @@ -159,6 +159,18 @@ class BaseSecret { Identifier name; //! Whether the secret can be serialized/deserialized bool serializable; + +public: + template + TARGET &Cast() { + DynamicCastCheck(this); + return reinterpret_cast(*this); + } + template + const TARGET &Cast() const { + DynamicCastCheck(this); + return reinterpret_cast(*this); + } }; //! The KeyValueSecret is a class that implements a Secret as a set of key -> values. This class can be used diff --git a/src/duckdb/src/include/duckdb/parser/parsed_data/create_info.hpp b/src/duckdb/src/include/duckdb/parser/parsed_data/create_info.hpp index 46e68ef1e..893074b88 100644 --- a/src/duckdb/src/include/duckdb/parser/parsed_data/create_info.hpp +++ b/src/duckdb/src/include/duckdb/parser/parsed_data/create_info.hpp @@ -81,6 +81,9 @@ struct CreateInfo : public ParseInfo { //! Renders the qualified name for ToString - the catalog is omitted for temporary entries and the default schema is //! hidden DUCKDB_API string QualifiedNameToString() const; + //! Drop the catalog component from the qualified name, keeping the (possibly nested) schema path. Use this before + //! rendering an entry that lives in a catalog: its catalog is implied by where the statement is run. + DUCKDB_API void StripCatalogQualification(); public: void Serialize(Serializer &serializer) const override; diff --git a/src/duckdb/src/include/duckdb/parser/parsed_data/create_table_info.hpp b/src/duckdb/src/include/duckdb/parser/parsed_data/create_table_info.hpp index 9ca800464..329c0b7d7 100644 --- a/src/duckdb/src/include/duckdb/parser/parsed_data/create_table_info.hpp +++ b/src/duckdb/src/include/duckdb/parser/parsed_data/create_table_info.hpp @@ -20,7 +20,7 @@ class SchemaCatalogEntry; struct CreateTableInfo : public CreateInfo { DUCKDB_API CreateTableInfo(); DUCKDB_API explicit CreateTableInfo(QualifiedName qualified_name); - DUCKDB_API CreateTableInfo(SchemaCatalogEntry &schema, Identifier name); + DUCKDB_API CreateTableInfo(SchemaCatalogEntry &schema, const Identifier &name); //! Table name to insert to const Identifier &GetTableName() const { diff --git a/src/duckdb/src/include/duckdb/parser/parsed_data/create_view_info.hpp b/src/duckdb/src/include/duckdb/parser/parsed_data/create_view_info.hpp index 6950c0568..c1a683fa1 100644 --- a/src/duckdb/src/include/duckdb/parser/parsed_data/create_view_info.hpp +++ b/src/duckdb/src/include/duckdb/parser/parsed_data/create_view_info.hpp @@ -20,7 +20,7 @@ enum class CreateViewBindingMode { BIND_ON_CREATE, SKIP_BINDING }; struct CreateViewInfo : public CreateInfo { public: CreateViewInfo(); - CreateViewInfo(SchemaCatalogEntry &schema, Identifier view_name); + CreateViewInfo(SchemaCatalogEntry &schema, const Identifier &view_name); explicit CreateViewInfo(const QualifiedName &view_name); public: diff --git a/src/duckdb/src/include/duckdb/parser/qualified_name.hpp b/src/duckdb/src/include/duckdb/parser/qualified_name.hpp index 786e1ba85..bcea82756 100644 --- a/src/duckdb/src/include/duckdb/parser/qualified_name.hpp +++ b/src/duckdb/src/include/duckdb/parser/qualified_name.hpp @@ -51,10 +51,10 @@ struct QualifiedName { path.push_back(std::move(name_p)); } - //! The catalog is the first element of the path, but only when the path is fully qualified ([catalog, schema, - //! name]) + //! The catalog is the first element of the path, but only when the path is fully qualified ([catalog, + //! schema..., name]) const Identifier &Catalog() const { - return path.size() == 3 ? path[0] : empty; + return path.size() >= 3 ? path[0] : empty; } //! The schema is the element directly before the name (or empty if there is no schema) const Identifier &Schema() const { @@ -70,14 +70,38 @@ struct QualifiedName { return path; } - //! Return a copy of this name with the name replaced, keeping the catalog/schema qualification + //! Return a copy of this name with the name replaced, keeping the (possibly nested) catalog/schema qualification QualifiedName WithName(Identifier name) const { - return QualifiedName(Catalog(), Schema(), std::move(name)); + vector qualification; + if (!path.empty()) { + qualification.insert(qualification.end(), path.begin(), path.end() - 1); + } + return QualifiedName(std::move(qualification), std::move(name)); } //! Return a copy of this name with the catalog/schema qualification replaced by the given path, keeping the name QualifiedName WithQualification(vector schema_path) const { return QualifiedName(std::move(schema_path), Name()); } + //! Drop the catalog component (if any), keeping the (possibly nested) schema path and the name + void StripCatalog() { + if (path.size() < 3) { + return; + } + path.erase(path.begin()); + } + //! Return a copy of this name qualified with the given catalog, replacing the catalog component it already has + QualifiedName WithCatalog(Identifier catalog) const { + if (path.size() < 2) { + return QualifiedName(std::move(catalog), Identifier(), Name()); + } + vector qualification; + qualification.push_back(std::move(catalog)); + // keep the (possibly nested) schema path, skipping the catalog component when the name is fully qualified + for (idx_t i = path.size() >= 3 ? 1 : 0; i + 1 < path.size(); i++) { + qualification.push_back(path[i]); + } + return QualifiedName(std::move(qualification), Name()); + } //! Parse the (optional) schema and a name from a string in the format of e.g. "schema"."table"; if there is no dot //! the schema will be set to INVALID_SCHEMA diff --git a/src/duckdb/src/include/duckdb/parser/tableref/at_clause.hpp b/src/duckdb/src/include/duckdb/parser/tableref/at_clause.hpp index 729f37fda..e89351728 100644 --- a/src/duckdb/src/include/duckdb/parser/tableref/at_clause.hpp +++ b/src/duckdb/src/include/duckdb/parser/tableref/at_clause.hpp @@ -15,10 +15,10 @@ namespace duckdb { //! The AT clause specifies which version of a table to read class AtClause { public: - AtClause(string unit, unique_ptr expr); + AtClause(Identifier unit, unique_ptr expr); public: - const string &Unit() { + const Identifier &Unit() { return unit; } unique_ptr &ExpressionMutable() { @@ -35,7 +35,7 @@ class AtClause { private: //! The unit (e.g. TIMESTAMP or VERSION) - string unit; + Identifier unit; //! The expression that determines which value of the unit we want to read unique_ptr expr; }; diff --git a/src/duckdb/src/include/duckdb/parser/tableref/basetableref.hpp b/src/duckdb/src/include/duckdb/parser/tableref/basetableref.hpp index b3dd63523..90771417e 100644 --- a/src/duckdb/src/include/duckdb/parser/tableref/basetableref.hpp +++ b/src/duckdb/src/include/duckdb/parser/tableref/basetableref.hpp @@ -24,9 +24,7 @@ class BaseTableRef : public TableRef { BaseTableRef() : TableRef(TableReferenceType::BASE_TABLE) { } explicit BaseTableRef(const TableDescription &description) - : TableRef(TableReferenceType::BASE_TABLE), - qualified_name(description.qualified_name.Catalog(), description.qualified_name.Schema(), - description.qualified_name.Name()) { + : TableRef(TableReferenceType::BASE_TABLE), qualified_name(description.qualified_name) { } //! The timestamp/version at which to read this table entry (if any) diff --git a/src/duckdb/src/include/duckdb/planner/binder.hpp b/src/duckdb/src/include/duckdb/planner/binder.hpp index bd3252bfd..cea14e30b 100644 --- a/src/duckdb/src/include/duckdb/planner/binder.hpp +++ b/src/duckdb/src/include/duckdb/planner/binder.hpp @@ -309,6 +309,8 @@ class Binder : public enable_shared_from_this { optional_ptr GetCatalogEntry(const Identifier &catalog, const Identifier &schema, const EntryLookupInfo &lookup_info, OnEntryNotFound on_entry_not_found); + //! Look up an entry using the qualification carried in the lookup info (which can be a nested schema path) + optional_ptr GetCatalogEntry(const EntryLookupInfo &lookup_info, OnEntryNotFound on_entry_not_found); //! Find all candidate common table expression by name; returns empty vector if none exists optional_ptr GetCTEBinding(const BindingAlias &name); diff --git a/src/duckdb/src/include/duckdb/planner/expression_binder/index_binder.hpp b/src/duckdb/src/include/duckdb/planner/expression_binder/index_binder.hpp index 0abb4cd20..ca186ee69 100644 --- a/src/duckdb/src/include/duckdb/planner/expression_binder/index_binder.hpp +++ b/src/duckdb/src/include/duckdb/planner/expression_binder/index_binder.hpp @@ -30,6 +30,8 @@ class IndexBinder : public ExpressionBinder { TableCatalogEntry &table_entry, unique_ptr plan, unique_ptr alter_table_info); + static void InitCreateIndexInfo(LogicalGet &get, CreateIndexInfo &info); + //! Deprecated: the schema is derived from the table the index is created on static void InitCreateIndexInfo(LogicalGet &get, CreateIndexInfo &info, const Identifier &schema); protected: diff --git a/src/duckdb/src/include/duckdb/planner/tableref/bound_at_clause.hpp b/src/duckdb/src/include/duckdb/planner/tableref/bound_at_clause.hpp index 6851b0073..b5e27efda 100644 --- a/src/duckdb/src/include/duckdb/planner/tableref/bound_at_clause.hpp +++ b/src/duckdb/src/include/duckdb/planner/tableref/bound_at_clause.hpp @@ -15,11 +15,11 @@ namespace duckdb { //! The AT clause specifies which version of a table to read class BoundAtClause { public: - BoundAtClause(string unit_p, Value value_p) : unit(std::move(unit_p)), val(std::move(value_p)) { + BoundAtClause(Identifier unit_p, Value value_p) : unit(std::move(unit_p)), val(std::move(value_p)) { } public: - const string &Unit() const { + const Identifier &Unit() const { return unit; } const Value &GetValue() const { @@ -28,7 +28,7 @@ class BoundAtClause { private: //! The unit (e.g. TIMESTAMP or VERSION) - string unit; + Identifier unit; //! The value that is associated with the unit (e.g. TIMESTAMP '2020-01-01') Value val; }; diff --git a/src/duckdb/src/include/duckdb/storage/wal_entry.hpp b/src/duckdb/src/include/duckdb/storage/wal_entry.hpp index a8306f61b..9799cb943 100644 --- a/src/duckdb/src/include/duckdb/storage/wal_entry.hpp +++ b/src/duckdb/src/include/duckdb/storage/wal_entry.hpp @@ -88,8 +88,20 @@ struct WALCreateView { }; struct WALDropView { - Identifier schema; - Identifier name; + // the entry as a QualifiedName (the containing schema path + the entry name) + QualifiedName qualified_name; + + WALDropView() = default; + explicit WALDropView(QualifiedName qualified_name_p) : qualified_name(std::move(qualified_name_p)) { + } + + // legacy fields serialized for storage versions older than v2.0.0 (derived from the qualified name) + Identifier LegacySchema() const { + return qualified_name.Schema(); + } + Identifier LegacyName() const { + return qualified_name.Name(); + } void Serialize(Serializer &serializer) const; static WALDropView Deserialize(Deserializer &deserializer); @@ -103,22 +115,49 @@ struct WALCreateSequence { }; struct WALDropSequence { - Identifier schema; - Identifier name; + // the entry as a QualifiedName (the containing schema path + the entry name) + QualifiedName qualified_name; + + WALDropSequence() = default; + explicit WALDropSequence(QualifiedName qualified_name_p) : qualified_name(std::move(qualified_name_p)) { + } + + // legacy fields serialized for storage versions older than v2.0.0 (derived from the qualified name) + Identifier LegacySchema() const { + return qualified_name.Schema(); + } + Identifier LegacyName() const { + return qualified_name.Name(); + } void Serialize(Serializer &serializer) const; static WALDropSequence Deserialize(Deserializer &deserializer); }; struct WALSequenceValue { - Identifier schema; - Identifier name; + // the sequence as a QualifiedName (the containing schema path + the sequence name) + QualifiedName qualified_name; uint64_t usage_count; int64_t counter; // the last value produced by the sequence; only serialized from storage version v2.0.0 onwards, and omitted when // unset (so older readers can still replay sequence values that do not carry a last_value) optional last_value; + WALSequenceValue() = default; + WALSequenceValue(QualifiedName qualified_name_p, uint64_t usage_count, int64_t counter, + optional last_value) + : qualified_name(std::move(qualified_name_p)), usage_count(usage_count), counter(counter), + last_value(last_value) { + } + + // legacy fields serialized for storage versions older than v2.0.0 (derived from the qualified name) + Identifier LegacySchema() const { + return qualified_name.Schema(); + } + Identifier LegacyName() const { + return qualified_name.Name(); + } + void Serialize(Serializer &serializer) const; static WALSequenceValue Deserialize(Deserializer &deserializer); }; @@ -131,8 +170,20 @@ struct WALCreateMacro { }; struct WALDropMacro { - Identifier schema; - Identifier name; + // the entry as a QualifiedName (the containing schema path + the entry name) + QualifiedName qualified_name; + + WALDropMacro() = default; + explicit WALDropMacro(QualifiedName qualified_name_p) : qualified_name(std::move(qualified_name_p)) { + } + + // legacy fields serialized for storage versions older than v2.0.0 (derived from the qualified name) + Identifier LegacySchema() const { + return qualified_name.Schema(); + } + Identifier LegacyName() const { + return qualified_name.Name(); + } void Serialize(Serializer &serializer) const; static WALDropMacro Deserialize(Deserializer &deserializer); @@ -146,8 +197,20 @@ struct WALCreateTableMacro { }; struct WALDropTableMacro { - Identifier schema; - Identifier name; + // the entry as a QualifiedName (the containing schema path + the entry name) + QualifiedName qualified_name; + + WALDropTableMacro() = default; + explicit WALDropTableMacro(QualifiedName qualified_name_p) : qualified_name(std::move(qualified_name_p)) { + } + + // legacy fields serialized for storage versions older than v2.0.0 (derived from the qualified name) + Identifier LegacySchema() const { + return qualified_name.Schema(); + } + Identifier LegacyName() const { + return qualified_name.Name(); + } void Serialize(Serializer &serializer) const; static WALDropTableMacro Deserialize(Deserializer &deserializer); @@ -161,8 +224,20 @@ struct WALCreateType { }; struct WALDropType { - Identifier schema; - Identifier name; + // the entry as a QualifiedName (the containing schema path + the entry name) + QualifiedName qualified_name; + + WALDropType() = default; + explicit WALDropType(QualifiedName qualified_name_p) : qualified_name(std::move(qualified_name_p)) { + } + + // legacy fields serialized for storage versions older than v2.0.0 (derived from the qualified name) + Identifier LegacySchema() const { + return qualified_name.Schema(); + } + Identifier LegacyName() const { + return qualified_name.Name(); + } void Serialize(Serializer &serializer) const; static WALDropType Deserialize(Deserializer &deserializer); @@ -176,17 +251,42 @@ struct WALCreateTrigger { }; struct WALDropTrigger { - Identifier schema; - Identifier name; + // the entry as a QualifiedName (the containing schema path + the entry name) + QualifiedName qualified_name; Identifier table; + WALDropTrigger() = default; + WALDropTrigger(QualifiedName qualified_name_p, Identifier table_p) + : qualified_name(std::move(qualified_name_p)), table(std::move(table_p)) { + } + + // legacy fields serialized for storage versions older than v2.0.0 (derived from the qualified name) + Identifier LegacySchema() const { + return qualified_name.Schema(); + } + Identifier LegacyName() const { + return qualified_name.Name(); + } + void Serialize(Serializer &serializer) const; static WALDropTrigger Deserialize(Deserializer &deserializer); }; struct WALDropIndex { - Identifier schema; - Identifier name; + // the entry as a QualifiedName (the containing schema path + the entry name) + QualifiedName qualified_name; + + WALDropIndex() = default; + explicit WALDropIndex(QualifiedName qualified_name_p) : qualified_name(std::move(qualified_name_p)) { + } + + // legacy fields serialized for storage versions older than v2.0.0 (derived from the qualified name) + Identifier LegacySchema() const { + return qualified_name.Schema(); + } + Identifier LegacyName() const { + return qualified_name.Name(); + } void Serialize(Serializer &serializer) const; static WALDropIndex Deserialize(Deserializer &deserializer); diff --git a/src/duckdb/src/main/client_context.cpp b/src/duckdb/src/main/client_context.cpp index 1ca2ee84c..544d8e9d6 100644 --- a/src/duckdb/src/main/client_context.cpp +++ b/src/duckdb/src/main/client_context.cpp @@ -294,7 +294,7 @@ unique_ptr ClientContext::ErrorResult(ErrorData error, const string &query) { return make_uniq(std::move(error)); } -void ClientContext::BeginQueryInternal(ClientContextLock &lock, const string &query) { +void ClientContext::BeginQueryInternal(ClientContextLock &lock, const SQLStatement &statement) { // check if we are on AutoCommit. In this case we should start a transaction D_ASSERT(!active_query); auto &db_inst = DatabaseInstance::GetDatabase(*this); @@ -307,6 +307,7 @@ void ClientContext::BeginQueryInternal(ClientContextLock &lock, const string &qu } transaction.SetActiveQuery(db->GetDatabaseManager().GetNewQueryNumber()); + auto &query = statement.query; LogQueryInternal(lock, query); active_query->query = query; @@ -468,14 +469,13 @@ static bool IsExplainAnalyze(SQLStatement *statement) { } shared_ptr ClientContext::CreatePreparedStatementInternal(ClientContextLock &lock, - const string &query, unique_ptr statement, PendingQueryParameters parameters) { StatementType statement_type = statement->type; auto result = make_shared_ptr(statement_type); auto &profiler = QueryProfiler::Get(*this); - profiler.StartQuery(query, IsExplainAnalyze(statement.get())); + profiler.StartQuery(statement->query, IsExplainAnalyze(statement.get())); Planner logical_planner(*this); if (parameters.parameters) { auto ¶meter_values = *parameters.parameters; @@ -532,7 +532,7 @@ shared_ptr ClientContext::CreatePreparedStatementInternal return result; } -shared_ptr ClientContext::CreatePreparedStatement(ClientContextLock &lock, const string &query, +shared_ptr ClientContext::CreatePreparedStatement(ClientContextLock &lock, unique_ptr statement, PendingQueryParameters parameters) { // check if any client context state could request a rebind @@ -547,7 +547,7 @@ shared_ptr ClientContext::CreatePreparedStatement(ClientC // if any registered state can request a rebind we do the binding on a copy first shared_ptr result; try { - result = CreatePreparedStatementInternal(lock, query, statement->Copy(), parameters); + result = CreatePreparedStatementInternal(lock, statement->Copy(), parameters); } catch (std::exception &ex) { ErrorData error(ex); // check if any registered client context state wants to try a rebind @@ -576,7 +576,7 @@ shared_ptr ClientContext::CreatePreparedStatement(ClientC // an extension wants to do a rebind - do it once } - return CreatePreparedStatementInternal(lock, query, std::move(statement), parameters); + return CreatePreparedStatementInternal(lock, std::move(statement), parameters); } QueryProgress ClientContext::GetQueryProgress() { @@ -845,7 +845,7 @@ unique_ptr ClientContext::PrepareInternal(ClientContextLock & PendingQueryParameters parameters; parameters.query_parameters.output_type = QueryResultOutputType::FORCE_MATERIALIZED; - auto result = RunStatementInternal(lock, statement_query, std::move(prepare), parameters, false); + auto result = RunStatementInternal(lock, std::move(prepare), parameters, false); if (result->HasError()) { result->ThrowError(); } @@ -940,7 +940,7 @@ unique_ptr ClientContext::Prepare(const string &query) { } } -unique_ptr ClientContext::PendingStatementInternal(ClientContextLock &lock, const string &query, +unique_ptr ClientContext::PendingStatementInternal(ClientContextLock &lock, unique_ptr statement, const PendingQueryParameters ¶meters) { // prepare the query for execution @@ -951,18 +951,17 @@ unique_ptr ClientContext::PendingStatementInternal(ClientCon PreparedStatement::VerifyParameters(empty_parameters, statement->named_param_map, this); } - auto prepared = CreatePreparedStatement(lock, query, std::move(statement), parameters); + auto prepared = CreatePreparedStatement(lock, std::move(statement), parameters); if (!prepared->properties.bound_all_parameters) { - return ErrorResult(InvalidInputException("Not all parameters were bound"), query); + return ErrorResult(InvalidInputException("Not all parameters were bound")); } // execute the prepared statement CheckIfPreparedStatementIsExecutable(*prepared); return PendingPreparedStatementInternal(lock, std::move(prepared), parameters); } -unique_ptr ClientContext::RunStatementInternal(ClientContextLock &lock, const string &query, - unique_ptr statement, +unique_ptr ClientContext::RunStatementInternal(ClientContextLock &lock, unique_ptr statement, const PendingQueryParameters ¶meters, bool verify) { auto pending = PendingQueryInternal(lock, std::move(statement), parameters, verify); if (pending->HasError()) { @@ -986,7 +985,7 @@ static bool HasBoundParameterValues(const SQLStatement &statement) { return !statement.Cast().bound_values.empty(); } -unique_ptr ClientContext::PendingStatement(ClientContextLock &lock, const string &query, +unique_ptr ClientContext::PendingStatement(ClientContextLock &lock, unique_ptr statement, const PendingQueryParameters ¶meters) { // CONNECT chokepoint: when connected, non-control SQL is rewritten in place and falls through to @@ -1000,7 +999,7 @@ unique_ptr ClientContext::PendingStatement(ClientContextLock ErrorData(InvalidInputException("Parameterized prepared statements cannot be executed while " "CONNECT-ed; DISCONNECT first, or run the SQL as a fresh " "statement to route through the CONNECT binding")), - query); + statement->query); } auto live = TryGetConnectedCatalog(); if (!live) { @@ -1009,19 +1008,24 @@ unique_ptr ClientContext::PendingStatement(ClientContextLock ErrorData(InvalidInputException( "The connected database has been detached out from under this connection. Issue " "DISCONNECT to clear the connection before running further SQL.")), - query); + statement->query); } // Dispatch via the catalog — Supports(CONNECT) was validated at CONNECT time, so RemoteExecute // is contracted to be implemented. Wrap the returned TableRef into a SelectStatement. - auto remote_ref = live->GetCatalog().RemoteExecute(*this, query); - statement = WrapAsSelect(std::move(remote_ref)); + auto remote_ref = live->GetCatalog().RemoteExecute(*this, statement->query); + auto rewritten = WrapAsSelect(std::move(remote_ref)); + // the rewrite is invisible to the user - keep reporting the SQL they issued + rewritten->query = std::move(statement->query); + statement = std::move(rewritten); AttachedDatabase::InvokeCloseIfLastReference(live, *this); // statement is now SELECT * FROM ; fall through. } } + // the statement is moved into PendingStatementInternal - keep the source text for error reporting + auto query = statement->query; unique_ptr pending; try { - BeginQueryInternal(lock, query); + BeginQueryInternal(lock, *statement); } catch (std::exception &ex) { ErrorData error(ex); if (Exception::InvalidatesDatabase(error.Type())) { @@ -1034,7 +1038,7 @@ unique_ptr ClientContext::PendingStatement(ClientContextLock bool invalidate_query = true; try { - pending = PendingStatementInternal(lock, query, std::move(statement), parameters); + pending = PendingStatementInternal(lock, std::move(statement), parameters); } catch (std::exception &ex) { ErrorData error(ex); if (!ErrorInvalidatesTransaction(error.Type())) { @@ -1239,7 +1243,6 @@ unique_ptr ClientContext::PendingQuery(unique_ptr &values, QueryParameters parameters) { auto lock = LockContext(); - auto query = statement->query; try { InitialCleanup(*lock); @@ -1256,11 +1259,10 @@ unique_ptr ClientContext::PendingQuery(unique_ptr ClientContext::RunInternalStatement(unique_ptr statement, const PendingQueryParameters ¶meters) { auto lock = LockContext(); - auto query = statement->query; try { InitialCleanup(*lock); } catch (std::exception &ex) { - return ErrorResult(ErrorData(ex), query); + return ErrorResult(ErrorData(ex), statement->query); } auto pending = PendingQueryInternal(*lock, std::move(statement), parameters, false); if (pending->HasError()) { @@ -1272,11 +1274,10 @@ unique_ptr ClientContext::RunInternalStatement(unique_ptr ClientContext::PendingInternalStatement(unique_ptr statement, const PendingQueryParameters ¶meters) { auto lock = LockContext(); - auto query = statement->query; try { InitialCleanup(*lock); } catch (std::exception &ex) { - return ErrorResult(ErrorData(ex), query); + return ErrorResult(ErrorData(ex), statement->query); } return PendingQueryInternal(*lock, std::move(statement), parameters, false); } @@ -1285,16 +1286,15 @@ unique_ptr ClientContext::PendingQueryInternal(ClientContext unique_ptr statement, const PendingQueryParameters ¶meters, bool verify) { - auto query = statement->query; if (verify) { try { - StatementVerification(lock, query, statement, parameters); + StatementVerification(lock, statement, parameters); } catch (std::exception &ex) { // preserve extra error data (like query location) - return ErrorResult(ErrorData(ex), query); + return ErrorResult(ErrorData(ex), statement->query); } } - return PendingStatement(lock, query, std::move(statement), parameters); + return PendingStatement(lock, std::move(statement), parameters); } unique_ptr ClientContext::ExecutePendingQueryInternal(ClientContextLock &lock, PendingQueryResult &query) { diff --git a/src/duckdb/src/main/client_verify.cpp b/src/duckdb/src/main/client_verify.cpp index 5944c5ab2..c7f6a4bfa 100644 --- a/src/duckdb/src/main/client_verify.cpp +++ b/src/duckdb/src/main/client_verify.cpp @@ -66,8 +66,14 @@ void PreparedStatementVerification::ConvertConstants(unique_ptr &child) { ConvertConstants(child); }); } -void ClientContext::StatementVerification(ClientContextLock &lock, const string &query, - unique_ptr &statement, +//! Swap in a statement produced by verification, carrying over the source text of the statement the user +//! issued - the rewrite is an internal detail, so errors and logging must keep reporting the original query +static void ReplaceStatement(unique_ptr &statement, unique_ptr replacement) { + replacement->query = statement->query; + statement = std::move(replacement); +} + +void ClientContext::StatementVerification(ClientContextLock &lock, unique_ptr &statement, PendingQueryParameters query_parameters) { auto verification = Settings::Get(*this); if (verification == DebugStatementVerification::COPY_STATEMENT) { @@ -75,7 +81,7 @@ void ClientContext::StatementVerification(ClientContextLock &lock, const string // COPY verification not supported for plan statements return; } - statement = statement->Copy(); + ReplaceStatement(statement, statement->Copy()); } else if (verification == DebugStatementVerification::REPARSE_STATEMENT) { if (statement->type == StatementType::RELATION_STATEMENT) { // reparsing not supported for relation statements @@ -97,7 +103,7 @@ void ClientContext::StatementVerification(ClientContextLock &lock, const string // re-apply auto rollback reparsed_transaction_stmt.info->auto_rollback = statement->Cast().info->auto_rollback; } - statement = std::move(parser.statements[0]); + ReplaceStatement(statement, std::move(parser.statements[0])); } else if (verification == DebugStatementVerification::SERIALIZE_STATEMENT) { switch (statement->type) { case StatementType::SELECT_STATEMENT: @@ -146,24 +152,24 @@ void ClientContext::StatementVerification(ClientContextLock &lock, const string switch (statement->type) { case StatementType::SELECT_STATEMENT: - statement = std::move(deserialized_stmt); + ReplaceStatement(statement, std::move(deserialized_stmt)); break; case StatementType::INSERT_STATEMENT: { auto result = make_uniq(); result->node = unique_ptr_cast(std::move(deserialized_node)); - statement = std::move(result); + ReplaceStatement(statement, std::move(result)); break; } case StatementType::DELETE_STATEMENT: { auto result = make_uniq(); result->node = unique_ptr_cast(std::move(deserialized_node)); - statement = std::move(result); + ReplaceStatement(statement, std::move(result)); break; } case StatementType::UPDATE_STATEMENT: { auto result = make_uniq(); result->node = unique_ptr_cast(std::move(deserialized_node)); - statement = std::move(result); + ReplaceStatement(statement, std::move(result)); break; } default: @@ -207,7 +213,7 @@ void ClientContext::StatementVerification(ClientContextLock &lock, const string // execute the PREPARE ErrorData error; try { - auto prepare_result = RunStatementInternal(lock, string(), std::move(prepare), query_parameters); + auto prepare_result = RunStatementInternal(lock, std::move(prepare), query_parameters); if (prepare_result->HasError()) { error = prepare_result->GetErrorObject(); } @@ -228,7 +234,7 @@ void ClientContext::StatementVerification(ClientContextLock &lock, const string execute->name = Identifier(name); execute->named_values = std::move(prep_verifier.values); - statement = std::move(execute); + ReplaceStatement(statement, std::move(execute)); } else if (verification == DebugStatementVerification::EXPLAIN_STATEMENT) { if (statement->type == StatementType::EXPLAIN_STATEMENT) { // don't explain explain... @@ -242,7 +248,8 @@ void ClientContext::StatementVerification(ClientContextLock &lock, const string // not supported for statements that already have parameters return; } - auto explain_q = "EXPLAIN " + query; + // deliberately left without source text: the EXPLAIN runs as a nested statement, and giving it a + // query would let it consume the error location that belongs to the statement we are verifying auto explain_stmt = make_uniq(statement->Copy()); // Disable the profiler during the verification EXPLAIN to prevent it from consuming the profiler context // (which would lose parser timing captured before StatementVerification was called) and from @@ -252,7 +259,7 @@ void ClientContext::StatementVerification(ClientContextLock &lock, const string ScopedConfigSetting suppress_profiling( client_config, [](ClientConfig &config) { config.enable_profiler = false; }, [saved_profiler](ClientConfig &config) { config.enable_profiler = saved_profiler; }); - auto explain_result = RunStatementInternal(lock, explain_q, std::move(explain_stmt), query_parameters); + auto explain_result = RunStatementInternal(lock, std::move(explain_stmt), query_parameters); if (explain_result->HasError()) { explain_result->ThrowError(); } diff --git a/src/duckdb/src/optimizer/statistics/expression/propagate_cast.cpp b/src/duckdb/src/optimizer/statistics/expression/propagate_cast.cpp index 90f189239..16aba989f 100644 --- a/src/duckdb/src/optimizer/statistics/expression/propagate_cast.cpp +++ b/src/duckdb/src/optimizer/statistics/expression/propagate_cast.cpp @@ -1,5 +1,7 @@ #include "duckdb/optimizer/statistics_propagator.hpp" #include "duckdb/planner/expression/bound_cast_expression.hpp" +#include "duckdb/storage/statistics/array_stats.hpp" +#include "duckdb/storage/statistics/list_stats.hpp" #include "duckdb/storage/statistics/struct_stats.hpp" #include "duckdb/storage/statistics/variant_stats.hpp" @@ -218,6 +220,23 @@ static unique_ptr StatisticsPropagateVariant(const BaseStatistic return StatisticsPropagator::TryPropagateCast(typed_stats, structured_type, target); } +static unique_ptr StatisticsPropagateArrayToList(const BaseStatistics &input, const LogicalType &source, + const LogicalType &target) { + D_ASSERT(source.id() == LogicalTypeId::ARRAY); + D_ASSERT(target.id() == LogicalTypeId::LIST); + + auto &source_child_type = ArrayType::GetChildType(source); + auto &target_child_type = ListType::GetChildType(target); + if (source_child_type != target_child_type || input.GetStatsType() != StatisticsType::ARRAY_STATS) { + return nullptr; + } + + auto result = ListStats::CreateEmpty(target); + result.CopyBase(input); + ListStats::GetChildStats(result).Copy(ArrayStats::GetChildStats(input)); + return result.ToUnique(); +} + unique_ptr StatisticsPropagator::TryPropagateCast(const BaseStatistics &stats, const LogicalType &source, const LogicalType &target) { @@ -233,6 +252,9 @@ unique_ptr StatisticsPropagator::TryPropagateCast(const BaseStat // type set and null-ness are unchanged: propagate the statistics as-is. return stats.Copy().ToUnique(); } + if (source.id() == LogicalTypeId::ARRAY && target.id() == LogicalTypeId::LIST) { + return StatisticsPropagateArrayToList(stats, source, target); + } if (!CanPropagateCast(source, target)) { return nullptr; } diff --git a/src/duckdb/src/parser/parsed_data/comment_on_column_info.cpp b/src/duckdb/src/parser/parsed_data/comment_on_column_info.cpp index 05a4980d7..c85ae1275 100644 --- a/src/duckdb/src/parser/parsed_data/comment_on_column_info.cpp +++ b/src/duckdb/src/parser/parsed_data/comment_on_column_info.cpp @@ -40,11 +40,8 @@ string SetColumnCommentInfo::ToString() const { } optional_ptr SetColumnCommentInfo::TryResolveCatalogEntry(CatalogEntryRetriever &retriever) { - EntryLookupInfo lookup_info(CatalogType::TABLE_ENTRY, QualifiedName(GetQualifiedName().Name())); - auto entry = retriever.GetEntry( - EntryLookupInfo(lookup_info, QualifiedName(GetQualifiedName().Catalog(), GetQualifiedName().Schema(), - lookup_info.GetEntryIdentifier())), - if_not_found); + EntryLookupInfo lookup_info(CatalogType::TABLE_ENTRY, GetQualifiedName()); + auto entry = retriever.GetEntry(lookup_info, if_not_found); if (entry) { catalog_entry_type = entry->type; diff --git a/src/duckdb/src/parser/parsed_data/create_index_info.cpp b/src/duckdb/src/parser/parsed_data/create_index_info.cpp index cc404f0e5..9e3c4593e 100644 --- a/src/duckdb/src/parser/parsed_data/create_index_info.cpp +++ b/src/duckdb/src/parser/parsed_data/create_index_info.cpp @@ -9,10 +9,10 @@ CreateIndexInfo::CreateIndexInfo() : CreateInfo(CatalogType::INDEX_ENTRY, Identi } CreateIndexInfo::CreateIndexInfo(const duckdb::CreateIndexInfo &info) - : CreateInfo(CatalogType::INDEX_ENTRY, info.GetQualifiedName().Schema()), table(info.table), options(info.options), - index_type(info.index_type), constraint_type(info.constraint_type), column_ids(info.column_ids), - scan_types(info.scan_types), names(info.names) { - SetIndexName(info.GetIndexName()); + : CreateInfo(CatalogType::INDEX_ENTRY), table(info.table), options(info.options), index_type(info.index_type), + constraint_type(info.constraint_type), column_ids(info.column_ids), scan_types(info.scan_types), + names(info.names) { + SetQualifiedName(info.GetQualifiedName()); } static void RemoveTableQualificationRecursive(unique_ptr &root_expr, const Identifier &table_name) { @@ -73,8 +73,12 @@ string CreateIndexInfo::ToString() const { } result += SQLIdentifier(GetIndexName()); result += " ON "; - result += QualifiedName(temporary ? Identifier() : GetQualifiedName().Catalog(), GetQualifiedName().Schema(), table) - .ToString(QualifiedNameToStringMode::HIDE_DEFAULT_SCHEMA); + // the index lives in the same (possibly nested) schema as the table it is created on + auto table_name = GetQualifiedName().WithName(table); + if (temporary) { + table_name.StripCatalog(); + } + result += table_name.ToString(QualifiedNameToStringMode::HIDE_DEFAULT_SCHEMA); if (index_type != "ART") { result += " USING "; result += SQLIdentifier(index_type); diff --git a/src/duckdb/src/parser/parsed_data/create_info.cpp b/src/duckdb/src/parser/parsed_data/create_info.cpp index 4331cb76c..ecc1b980f 100644 --- a/src/duckdb/src/parser/parsed_data/create_info.cpp +++ b/src/duckdb/src/parser/parsed_data/create_info.cpp @@ -23,10 +23,18 @@ unique_ptr CreateInfo::GetAlterInfo() const { throw NotImplementedException("GetAlterInfo not implemented for this type"); } +void CreateInfo::StripCatalogQualification() { + qualified_name.StripCatalog(); +} + string CreateInfo::QualifiedNameToString() const { + if (!temporary) { + return qualified_name.ToString(QualifiedNameToStringMode::HIDE_DEFAULT_SCHEMA); + } // for temporary entries the catalog is implied, so it is omitted from the rendered name - auto catalog = temporary ? Identifier() : qualified_name.Catalog(); - return QualifiedName(std::move(catalog), qualified_name.Schema(), qualified_name.Name()) + auto &path = qualified_name.Path(); + vector schema_path(path.begin() + (path.size() >= 3 ? 1 : 0), path.end() - 1); + return QualifiedName(std::move(schema_path), qualified_name.Name()) .ToString(QualifiedNameToStringMode::HIDE_DEFAULT_SCHEMA); } diff --git a/src/duckdb/src/parser/parsed_data/create_table_info.cpp b/src/duckdb/src/parser/parsed_data/create_table_info.cpp index 3170b9759..d27c0f179 100644 --- a/src/duckdb/src/parser/parsed_data/create_table_info.cpp +++ b/src/duckdb/src/parser/parsed_data/create_table_info.cpp @@ -12,8 +12,8 @@ CreateTableInfo::CreateTableInfo(QualifiedName qualified_name_p) : CreateInfo(Ca SetQualifiedName(std::move(qualified_name_p)); } -CreateTableInfo::CreateTableInfo(SchemaCatalogEntry &schema, Identifier name_p) - : CreateTableInfo(QualifiedName(schema.catalog.GetName(), schema.name, std::move(name_p))) { +CreateTableInfo::CreateTableInfo(SchemaCatalogEntry &schema, const Identifier &name_p) + : CreateTableInfo(schema.GetQualifiedName(name_p)) { } unique_ptr CreateTableInfo::Copy() const { diff --git a/src/duckdb/src/parser/parsed_data/create_view_info.cpp b/src/duckdb/src/parser/parsed_data/create_view_info.cpp index 420020372..b986bffc3 100644 --- a/src/duckdb/src/parser/parsed_data/create_view_info.cpp +++ b/src/duckdb/src/parser/parsed_data/create_view_info.cpp @@ -15,8 +15,8 @@ CreateViewInfo::CreateViewInfo(const QualifiedName &view_name) SetViewName(view_name.Name()); } -CreateViewInfo::CreateViewInfo(SchemaCatalogEntry &schema, Identifier view_name) - : CreateViewInfo(QualifiedName(schema.catalog.GetName(), schema.name, std::move(view_name))) { +CreateViewInfo::CreateViewInfo(SchemaCatalogEntry &schema, const Identifier &view_name) + : CreateViewInfo(schema.GetQualifiedName(view_name)) { } string CreateViewInfo::ToString() const { diff --git a/src/duckdb/src/parser/parsed_data/exported_table_data.cpp b/src/duckdb/src/parser/parsed_data/exported_table_data.cpp index 5487a70c6..de397c247 100644 --- a/src/duckdb/src/parser/parsed_data/exported_table_data.cpp +++ b/src/duckdb/src/parser/parsed_data/exported_table_data.cpp @@ -15,9 +15,7 @@ ExportedTableInfo::ExportedTableInfo(ClientContext &context, ExportedTableData t } TableCatalogEntry &ExportedTableInfo::GetEntry(ClientContext &context, const ExportedTableData &table_data) { - return Catalog::GetEntry(context, QualifiedName(table_data.qualified_name.Catalog(), - table_data.qualified_name.Schema(), - table_data.qualified_name.Name())); + return Catalog::GetEntry(context, table_data.qualified_name); } } // namespace duckdb diff --git a/src/duckdb/src/parser/peg/transformer/transform_create_index.cpp b/src/duckdb/src/parser/peg/transformer/transform_create_index.cpp index 50418472a..b4165227d 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_create_index.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_create_index.cpp @@ -19,8 +19,8 @@ unique_ptr PEGTransformerFactory::TransformCreateIndexStmt( throw NotImplementedException("Please provide an index name, e.g., CREATE INDEX my_name ..."); } index_info->table = base_table_name->Table(); - index_info->SetQualifiedName(QualifiedName(base_table_name->GetQualifiedName().Catalog(), - base_table_name->GetQualifiedName().Schema(), *index_name)); + // the index lives in the same (possibly nested) schema as the table it is created on + index_info->SetQualifiedName(base_table_name->GetQualifiedName().WithName(*index_name)); index_info->index_type = index_type ? index_type->GetIdentifierName() : "ART"; if (insert_column_list) { for (auto &column : *insert_column_list) { diff --git a/src/duckdb/src/parser/peg/transformer/transform_select.cpp b/src/duckdb/src/parser/peg/transformer/transform_select.cpp index e2112abe5..a6c00f08b 100644 --- a/src/duckdb/src/parser/peg/transformer/transform_select.cpp +++ b/src/duckdb/src/parser/peg/transformer/transform_select.cpp @@ -1734,7 +1734,7 @@ unique_ptr PEGTransformerFactory::TransformAtClause(PEGTransformer &tr unique_ptr PEGTransformerFactory::TransformAtSpecifier(PEGTransformer &transformer, const string &at_unit, unique_ptr expression) { - return make_uniq(at_unit, std::move(expression)); + return make_uniq(Identifier(at_unit), std::move(expression)); } unique_ptr PEGTransformerFactory::TransformJoinWithoutOnClause(PEGTransformer &transformer, diff --git a/src/duckdb/src/parser/qualified_name.cpp b/src/duckdb/src/parser/qualified_name.cpp index 9687bce65..12f531060 100644 --- a/src/duckdb/src/parser/qualified_name.cpp +++ b/src/duckdb/src/parser/qualified_name.cpp @@ -19,17 +19,22 @@ QualifiedName QualifiedName::Deserialize(Deserializer &deserializer) { } string QualifiedName::ToString(QualifiedNameToStringMode mode) const { - const auto &catalog = Catalog(); - const auto &schema = Schema(); + if (path.empty()) { + return string(); + } string result; - if (!catalog.empty()) { - result += SQLIdentifier(catalog) + "."; - if (!schema.empty()) { - result += SQLIdentifier(schema) + "."; + // render every qualification component (the path can hold a nested schema chain) + for (idx_t i = 0; i + 1 < path.size(); i++) { + auto &component = path[i]; + if (component.empty()) { + continue; } - } else if (!schema.empty() && - !(mode == QualifiedNameToStringMode::HIDE_DEFAULT_SCHEMA && schema == DEFAULT_SCHEMA)) { - result += SQLIdentifier(schema) + "."; + if (mode == QualifiedNameToStringMode::HIDE_DEFAULT_SCHEMA && result.empty() && i + 2 == path.size() && + component == DEFAULT_SCHEMA) { + // the only qualification is the default schema - hide it + continue; + } + result += SQLIdentifier(component) + "."; } result += SQLIdentifier(Name()); return result; diff --git a/src/duckdb/src/parser/tableref/at_clause.cpp b/src/duckdb/src/parser/tableref/at_clause.cpp index cde10c57e..123d318a1 100644 --- a/src/duckdb/src/parser/tableref/at_clause.cpp +++ b/src/duckdb/src/parser/tableref/at_clause.cpp @@ -2,7 +2,7 @@ namespace duckdb { -AtClause::AtClause(string unit_p, unique_ptr expr_p) +AtClause::AtClause(Identifier unit_p, unique_ptr expr_p) : unit(std::move(unit_p)), expr(std::move(expr_p)) { } diff --git a/src/duckdb/src/planner/binder.cpp b/src/duckdb/src/planner/binder.cpp index f87a03051..e0ac460de 100644 --- a/src/duckdb/src/planner/binder.cpp +++ b/src/duckdb/src/planner/binder.cpp @@ -602,6 +602,11 @@ optional_ptr Binder::GetCatalogEntry(const Identifier &catalog, co on_entry_not_found); } +optional_ptr Binder::GetCatalogEntry(const EntryLookupInfo &lookup_info, + OnEntryNotFound on_entry_not_found) { + return entry_retriever.GetEntry(lookup_info, on_entry_not_found); +} + //! Create a binder whose catalog search path is anchored to the table's catalog+schema shared_ptr Binder::CreateBinderWithSearchPath(const Identifier &catalog_name, const Identifier &schema_name) { shared_ptr new_binder = Binder::CreateBinder(context, this); diff --git a/src/duckdb/src/planner/binder/expression/bind_type_expression.cpp b/src/duckdb/src/planner/binder/expression/bind_type_expression.cpp index 0ca589c35..5cacca363 100644 --- a/src/duckdb/src/planner/binder/expression/bind_type_expression.cpp +++ b/src/duckdb/src/planner/binder/expression/bind_type_expression.cpp @@ -19,36 +19,34 @@ static bool IsValidTypeLookup(optional_ptr entry) { BindResult ExpressionBinder::BindExpression(TypeExpression &type_expr, idx_t depth) { auto &type_name = type_expr.GetTypeName(); - auto type_schema = type_expr.GetSchema(); - auto type_catalog = type_expr.GetCatalog(); QueryErrorContext error_context(type_expr); EntryLookupInfo type_lookup(CatalogType::TYPE_ENTRY, QualifiedName(type_name), error_context); optional_ptr entry = nullptr; - binder.BindSchemaOrCatalog(context, type_catalog, type_schema); + // Resolve the qualification the same way a table reference is resolved: a leading component is the catalog when + // it names an attached database, and otherwise the outermost schema of a (possibly nested) schema path. + auto bound_name = Binder::BindTableName(binder.EntryRetriever(), type_expr.GetQualifiedName()); + auto &type_catalog = bound_name.Catalog(); + bool is_qualified = bound_name.Path().size() > 1; - // Required for WAL lookup to work if (type_catalog.empty() && !DatabaseManager::Get(context).HasDefaultDatabase()) { // Look in the system catalog if no catalog was specified - entry = binder.entry_retriever.GetEntry(EntryLookupInfo( - type_lookup, QualifiedName(Identifier::SystemCatalog(), type_schema, type_lookup.GetEntryIdentifier()))); + entry = binder.entry_retriever.GetEntry( + EntryLookupInfo(type_lookup, bound_name.WithCatalog(Identifier::SystemCatalog()))); } else { // Try to search from most specific to least specific // The search path should already have been set to the correct catalog/schema, // in case we are looking for a type in the same schema as a table we are creating - entry = binder.entry_retriever.GetEntry( - EntryLookupInfo(type_lookup, QualifiedName(type_catalog, type_schema, type_lookup.GetEntryIdentifier())), - OnEntryNotFound::RETURN_NULL); + entry = binder.entry_retriever.GetEntry(EntryLookupInfo(type_lookup, bound_name), OnEntryNotFound::RETURN_NULL); if (!IsValidTypeLookup(entry)) { - if (!type_catalog.empty() || !type_schema.empty()) { - entry = binder.entry_retriever.GetEntry( - EntryLookupInfo(type_lookup, - QualifiedName(type_catalog, type_schema, type_lookup.GetEntryIdentifier())), - OnEntryNotFound::THROW_EXCEPTION); + if (is_qualified) { + // re-run the lookup to report the qualification that was given + entry = binder.entry_retriever.GetEntry(EntryLookupInfo(type_lookup, bound_name), + OnEntryNotFound::THROW_EXCEPTION); } entry = binder.entry_retriever.GetEntry( EntryLookupInfo(type_lookup, QualifiedName(type_catalog, Identifier::InvalidSchema(), diff --git a/src/duckdb/src/planner/binder/statement/bind_alter.cpp b/src/duckdb/src/planner/binder/statement/bind_alter.cpp index 95f890c2e..d524b44c5 100644 --- a/src/duckdb/src/planner/binder/statement/bind_alter.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_alter.cpp @@ -62,9 +62,7 @@ BoundStatement Binder::BindAlterAddIndex(BoundStatement &result, CatalogEntry &e D_ASSERT(!create_index_info->GetIndexName().empty()); // Plan the table scan. - TableDescription table_description(QualifiedName(table_info.GetQualifiedName().Catalog(), - table_info.GetQualifiedName().Schema(), - table_info.GetQualifiedName().Name())); + TableDescription table_description(table_info.GetQualifiedName()); auto table_ref = make_uniq(table_description); auto bound_table = Bind(*table_ref); if (bound_table.plan->type != LogicalOperatorType::LOGICAL_GET) { @@ -115,7 +113,8 @@ BoundStatement Binder::Bind(AlterStatement &stmt) { return result; } - BindSchemaOrCatalog(stmt.info->GetQualifiedNameMutable()); + // resolve the (possibly nested) catalog/schema qualification of the altered entry + stmt.info->SetQualifiedName(BindTableName(stmt.info->GetQualifiedName())); optional_ptr entry; if (stmt.info->type == AlterType::SET_COLUMN_COMMENT) { @@ -129,12 +128,8 @@ BoundStatement Binder::Bind(AlterStatement &stmt) { } } else { // For any other ALTER, we retrieve the catalog entry directly. - EntryLookupInfo lookup_info(stmt.info->GetCatalogType(), QualifiedName(stmt.info->GetQualifiedName().Name())); - entry = - entry_retriever.GetEntry(EntryLookupInfo(lookup_info, QualifiedName(stmt.info->GetQualifiedName().Catalog(), - stmt.info->GetQualifiedName().Schema(), - lookup_info.GetEntryIdentifier())), - stmt.info->if_not_found); + EntryLookupInfo lookup_info(stmt.info->GetCatalogType(), stmt.info->GetQualifiedName()); + entry = entry_retriever.GetEntry(lookup_info, stmt.info->if_not_found); } auto &properties = GetStatementProperties(); @@ -163,8 +158,7 @@ BoundStatement Binder::Bind(AlterStatement &stmt) { // We can only alter temporary tables and views in read-only mode. properties.RegisterDBModify(catalog, context, DatabaseModificationType::ALTER_TABLE); } - stmt.info->SetQualifiedName( - QualifiedName(catalog.GetName(), entry->ParentSchema().name, stmt.info->GetQualifiedName().Name())); + stmt.info->SetQualifiedName(entry->ParentSchema().GetQualifiedName(stmt.info->GetQualifiedName().Name())); if (!stmt.info->IsAddPrimaryKey()) { result.plan = make_uniq(std::move(stmt.info)); diff --git a/src/duckdb/src/planner/binder/statement/bind_copy_database.cpp b/src/duckdb/src/planner/binder/statement/bind_copy_database.cpp index dfd45b7fb..0dc294923 100644 --- a/src/duckdb/src/planner/binder/statement/bind_copy_database.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_copy_database.cpp @@ -29,8 +29,8 @@ unique_ptr Binder::BindCopyDatabaseSchema(Catalog &from_databas auto info = make_uniq(target_database_name); for (auto &entry : catalog_entries) { auto create_info = entry.get().GetInfo(); - create_info->SetQualifiedName(QualifiedName(target_database_name, create_info->GetQualifiedName().Schema(), - create_info->GetQualifiedName().Name())); + // re-root the entry (keeping its possibly nested schema path) in the target database + create_info->SetQualifiedName(create_info->GetQualifiedName().WithCatalog(target_database_name)); auto on_conflict = create_info->type == CatalogType::SCHEMA_ENTRY ? OnCreateConflict::IGNORE_ON_CONFLICT : OnCreateConflict::ERROR_ON_CONFLICT; // Update all the dependencies of the entry to point to the newly created entries on the target database @@ -62,10 +62,12 @@ unique_ptr Binder::BindCopyDatabaseData(Catalog &source_catalog // generate the insert statement InsertStatement insert_stmt; auto &insert_node = *insert_stmt.node; - insert_node.qualified_name = QualifiedName(target_database_name, table.ParentSchema().name, table.name); + // the table can live in a nested schema - carry the full schema path on both sides + auto source_name = table.ParentSchema().GetQualifiedName(table.name); + insert_node.qualified_name = source_name.WithCatalog(target_database_name); auto from_tbl = make_uniq(); - from_tbl->SetQualifiedName(QualifiedName(source_catalog.GetName(), table.ParentSchema().name, table.name)); + from_tbl->SetQualifiedName(source_name.WithCatalog(source_catalog.GetName())); auto select_node = make_uniq(); auto &select_list = select_node->select_list; diff --git a/src/duckdb/src/planner/binder/statement/bind_create.cpp b/src/duckdb/src/planner/binder/statement/bind_create.cpp index 3b5cf804f..448214390 100644 --- a/src/duckdb/src/planner/binder/statement/bind_create.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_create.cpp @@ -59,7 +59,7 @@ static unique_ptr MakeTriggerValidationCTE(const Tabl auto alias_select = make_uniq(); alias_select->select_list.push_back(make_uniq()); auto alias_table_ref = make_uniq(); - alias_table_ref->SetQualifiedName(QualifiedName(table.catalog.GetName(), table.schema.name, table.name)); + alias_table_ref->SetQualifiedName(table.schema.GetQualifiedName(table.name)); alias_select->from_table = std::move(alias_table_ref); auto alias_cte = make_uniq(); alias_cte->query_node = std::move(alias_select); @@ -634,9 +634,8 @@ SchemaCatalogEntry &Binder::BindCreateTriggerInfo(CreateTriggerInfo &create_trig } auto &table = *table_ptr; - // Trigger inherits catalog/schema from the base table - create_trigger_info.SetQualifiedName( - QualifiedName(table.catalog.GetName(), table.schema.name, create_trigger_info.GetQualifiedName().Name())); + // Trigger inherits the catalog and the (possibly nested) schema from the base table + create_trigger_info.SetQualifiedName(table.schema.GetQualifiedName(create_trigger_info.GetQualifiedName().Name())); auto &schema = BindCreateSchema(create_trigger_info); @@ -836,10 +835,8 @@ BoundStatement Binder::Bind(CreateStatement &stmt) { case CatalogType::INDEX_ENTRY: { auto &create_index_info = stmt.info->Cast(); - // Plan the table scan. - TableDescription table_description(QualifiedName(create_index_info.GetQualifiedName().Catalog(), - create_index_info.GetQualifiedName().Schema(), - create_index_info.table)); + // Plan the table scan - the table lives in the same (possibly nested) schema as the index. + TableDescription table_description(create_index_info.GetQualifiedName().WithName(create_index_info.table)); auto table_ref = make_uniq(table_description); auto bound_table = Bind(*table_ref); auto plan = std::move(bound_table.plan); diff --git a/src/duckdb/src/planner/binder/statement/bind_create_table.cpp b/src/duckdb/src/planner/binder/statement/bind_create_table.cpp index 99b012f8c..50672302e 100644 --- a/src/duckdb/src/planner/binder/statement/bind_create_table.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_create_table.cpp @@ -569,13 +569,15 @@ static void BindCreateTableConstraints(CreateTableInfo &create_info, CatalogEntr // Resolve the table reference in the same catalog/schema as the table being // created, so FK references work for external catalogs (not just the default). - Identifier fk_catalog = - fk.info.schema.empty() ? schema.ParentCatalog().GetName() : Identifier::InvalidCatalog(); - string fk_schema = - fk.info.schema.empty() ? schema.name.GetIdentifierName() : fk.info.schema.GetIdentifierName(); EntryLookupInfo table_lookup(CatalogType::TABLE_ENTRY, QualifiedName(fk.info.table)); - auto table_entry = entry_retriever.GetEntry(EntryLookupInfo( - table_lookup, QualifiedName(fk_catalog, Identifier(fk_schema), table_lookup.GetEntryIdentifier()))); + QualifiedName fk_name; + if (fk.info.schema.empty() || fk.info.schema == schema.name) { + // a foreign key can only reference a table in the same (possibly nested) schema + fk_name = schema.GetQualifiedName(fk.info.table); + } else { + fk_name = QualifiedName(Identifier::InvalidCatalog(), fk.info.schema, fk.info.table); + } + auto table_entry = entry_retriever.GetEntry(EntryLookupInfo(table_lookup, fk_name)); if (table_entry->type == CatalogType::VIEW_ENTRY) { throw BinderException("cannot reference a VIEW with a FOREIGN KEY"); } diff --git a/src/duckdb/src/planner/binder/statement/bind_drop.cpp b/src/duckdb/src/planner/binder/statement/bind_drop.cpp index 170f5f1ee..b24fdfc34 100644 --- a/src/duckdb/src/planner/binder/statement/bind_drop.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_drop.cpp @@ -22,11 +22,12 @@ void Binder::BindDropTrigger(DropStatement &stmt, StatementProperties &propertie throw BinderException("DROP TRIGGER requires an ON clause specifying the table"); } auto &base_table_ref = trigger_extra.base_table->Cast(); - BindSchemaOrCatalog(base_table_ref.GetQualifiedNameMutable()); + // resolve the (possibly nested) catalog/schema qualification of the base table + base_table_ref.SetQualifiedName(BindTableName(base_table_ref.GetQualifiedName())); // IF EXISTS only guards the trigger, not the table (PostgreSQL-compatible behavior). auto &table_entry = Catalog::GetEntry(context, base_table_ref.GetQualifiedName()); - stmt.info->SetQualifiedName(QualifiedName(table_entry.ParentCatalog().GetName(), table_entry.ParentSchema().name, - stmt.info->GetQualifiedName().Name())); + // the trigger lives in the same (possibly nested) schema as its base table + stmt.info->SetQualifiedName(table_entry.ParentSchema().GetQualifiedName(stmt.info->GetQualifiedName().Name())); properties.RegisterDBModify(table_entry.ParentCatalog(), context, DatabaseModificationType::DROP_CATALOG_ENTRY); } @@ -57,42 +58,10 @@ BoundStatement Binder::Bind(DropStatement &stmt) { case CatalogType::INDEX_ENTRY: case CatalogType::TABLE_ENTRY: case CatalogType::TYPE_ENTRY: { - // resolve the catalog + (possibly nested) schema path. ResolveCatalog (via BindSchemaOrCatalog) decides which - // leading components form the catalog; default_catalog=false leaves the catalog empty when none is given. - auto resolved = ResolveCatalog(context, stmt.info->GetQualifiedName(), false); - auto &rpath = resolved.Path(); - vector schema_path(rpath.begin() + 1, rpath.end() - 1); - if (schema_path.size() > 1) { - // a nested schema was given - navigate to it and drop from it. Keep the full resolved path so execution - // navigates the same nested schema (and no-ops for IF EXISTS). - auto catalog_name = rpath.front(); - auto target_name = resolved.Name(); - stmt.info->SetQualifiedName(std::move(resolved)); - auto schema = Catalog::GetSchema(context, catalog_name, schema_path, stmt.info->if_not_found); - optional_ptr entry; - if (schema) { - entry = schema->GetEntry(schema->catalog.GetCatalogTransaction(context), stmt.info->type, target_name); - } - if (!entry) { - if (stmt.info->if_not_found == OnEntryNotFound::THROW_EXCEPTION) { - throw CatalogException("%s with name \"%s\" does not exist!", CatalogTypeToString(stmt.info->type), - target_name.GetIdentifierName()); - } - break; - } - if (entry->internal) { - throw CatalogException("Cannot drop internal catalog entry \"%s\"!", entry->name.GetIdentifierName()); - } - properties.RegisterDBRead(schema->catalog, context); - if (!entry->temporary) { - properties.RegisterDBModify(schema->catalog, context, DatabaseModificationType::DROP_CATALOG_ENTRY); - } - break; - } - // unqualified or single-level: look the entry up through the search path. Feed the resolved (catalog, schema, - // name) to the lookup - no second BindSchemaOrCatalog call is needed. - stmt.info->SetQualifiedName( - QualifiedName(rpath.front(), schema_path.empty() ? Identifier() : schema_path[0], resolved.Name())); + // Resolve the catalog + (possibly nested) schema path. A leading component is the catalog when it names an + // attached database, and otherwise the outermost schema of a nested schema path. The entry lookup below + // navigates whatever qualification comes out of this. + stmt.info->SetQualifiedName(BindTableName(stmt.info->GetQualifiedName())); auto catalog = Catalog::GetCatalogEntry(context, stmt.info->GetQualifiedName().Catalog()); if (catalog) { // mark catalog as accessed @@ -128,8 +97,8 @@ BoundStatement Binder::Bind(DropStatement &stmt) { if (entry->internal) { throw CatalogException("Cannot drop internal catalog entry \"%s\"!", entry->name.GetIdentifierName()); } - stmt.info->SetQualifiedName(QualifiedName(entry->ParentCatalog().GetName(), entry->ParentSchema().name, - stmt.info->GetQualifiedName().Name())); + // keep the entry's full (possibly nested) schema path so execution navigates the same schema + stmt.info->SetQualifiedName(entry->ParentSchema().GetQualifiedName(stmt.info->GetQualifiedName().Name())); if (!entry->temporary) { // we can only drop temporary schema entries in read-only mode properties.RegisterDBModify(entry->ParentCatalog(), context, DatabaseModificationType::DROP_CATALOG_ENTRY); diff --git a/src/duckdb/src/planner/binder/statement/bind_export.cpp b/src/duckdb/src/planner/binder/statement/bind_export.cpp index 60a5e1c2d..98461e8c7 100644 --- a/src/duckdb/src/planner/binder/statement/bind_export.cpp +++ b/src/duckdb/src/planner/binder/statement/bind_export.cpp @@ -228,7 +228,8 @@ BoundStatement Binder::Bind(ExportStatement &stmt) { id++; } info->is_from = false; - info->SetQualifiedName(QualifiedName(Identifier(catalog), table.schema.name, table.name)); + // carry the full (possibly nested) schema path of the exported table + info->SetQualifiedName(table.schema.GetQualifiedName(table.name)); // We can not export generated columns child_list_t select_list; @@ -245,8 +246,7 @@ BoundStatement Binder::Bind(ExportStatement &stmt) { } ExportedTableData exported_data; - exported_data.qualified_name = - QualifiedName(Identifier(catalog), info->GetQualifiedName().Schema(), info->Table()); + exported_data.qualified_name = info->GetQualifiedName(); exported_data.file_path = info->file_path; diff --git a/src/duckdb/src/planner/binder/tableref/bind_showref.cpp b/src/duckdb/src/planner/binder/tableref/bind_showref.cpp index 2351a5f88..a62d15866 100644 --- a/src/duckdb/src/planner/binder/tableref/bind_showref.cpp +++ b/src/duckdb/src/planner/binder/tableref/bind_showref.cpp @@ -175,9 +175,18 @@ BoundStatement Binder::BindShowTable(ShowRef &ref) { // Check for unqualified name, promote schema to catalog if unambiguous, and set schema_name to empty if so Binder::BindSchemaOrCatalog(catalog_name, schema_name); + optional_idx schema_oid; // If fully qualified, check if the schema exists if (!catalog_name.empty() && !schema_name.empty()) { auto schema_entry = Catalog::GetSchema(context, catalog_name, schema_name, OnEntryNotFound::RETURN_NULL); + if (!schema_entry) { + // "a.b" can also name a nested schema + vector nested_path {catalog_name, schema_name}; + schema_entry = Catalog::GetSchema(context, Identifier(), nested_path, OnEntryNotFound::RETURN_NULL); + if (schema_entry) { + schema_oid = schema_entry->oid; + } + } if (!schema_entry) { throw CatalogException("SHOW TABLES FROM: No catalog + schema named \"%s.%s\" found.", catalog_name.GetIdentifierName(), schema_name.GetIdentifierName()); @@ -193,7 +202,7 @@ BoundStatement Binder::BindShowTable(ShowRef &ref) { catalog_name.GetIdentifierName(), schema_name.GetIdentifierName()); } } - sql = PragmaShowTables(catalog_name.GetIdentifierName(), schema_name.GetIdentifierName()); + sql = PragmaShowTables(catalog_name.GetIdentifierName(), schema_name.GetIdentifierName(), schema_oid); } else if (lname == "\"variables\"") { sql = PragmaShowVariables(); } else if (lname == "__show_tables_expanded") { diff --git a/src/duckdb/src/planner/binder/tableref/bind_table_function.cpp b/src/duckdb/src/planner/binder/tableref/bind_table_function.cpp index 7d9e54a52..2c27d617b 100644 --- a/src/duckdb/src/planner/binder/tableref/bind_table_function.cpp +++ b/src/duckdb/src/planner/binder/tableref/bind_table_function.cpp @@ -372,15 +372,13 @@ BoundStatement Binder::Bind(TableFunctionRef &ref) { D_ASSERT(ref.function->GetExpressionType() == ExpressionType::FUNCTION); auto &fexpr = ref.function->Cast(); - Identifier catalog = fexpr.GetQualifiedName().Catalog(); - Identifier schema = fexpr.GetQualifiedName().Schema(); - Binder::BindSchemaOrCatalog(context, catalog, schema); - - // fetch the function from the catalog - + // fetch the function from the catalog. Resolve the qualification first: a leading component is the catalog when + // it names an attached database, and otherwise the outermost schema of a (possibly nested) schema path. EntryLookupInfo table_function_lookup(CatalogType::TABLE_FUNCTION_ENTRY, QualifiedName(fexpr.FunctionName()), error_context); - auto &func_catalog = *GetCatalogEntry(catalog, schema, table_function_lookup, OnEntryNotFound::THROW_EXCEPTION); + auto bound_name = BindTableName(fexpr.GetQualifiedName()); + auto &func_catalog = + *GetCatalogEntry(EntryLookupInfo(table_function_lookup, bound_name), OnEntryNotFound::THROW_EXCEPTION); if (func_catalog.type == CatalogType::TABLE_MACRO_ENTRY) { auto ¯o_func = func_catalog.Cast(); diff --git a/src/duckdb/src/planner/column_qualifier.cpp b/src/duckdb/src/planner/column_qualifier.cpp index 64e96aa08..b6090304c 100644 --- a/src/duckdb/src/planner/column_qualifier.cpp +++ b/src/duckdb/src/planner/column_qualifier.cpp @@ -265,8 +265,11 @@ optional_ptr ColumnQualifier::QualifyFunction(FunctionExpression & EntryLookupInfo function_lookup(CatalogType::SCALAR_FUNCTION_ENTRY, QualifiedName(function.FunctionName()), error_context); - auto func = binder.GetCatalogEntry(function.GetQualifiedName().Catalog(), function.GetQualifiedName().Schema(), - function_lookup, OnEntryNotFound::RETURN_NULL); + // resolve the qualification: this decides whether a leading component (e.g. "s1" in "s1.s2.my_macro()") is a + // catalog or the outermost schema of a nested schema path. The name as written is left alone - the dot-call + // rewrite below turns the qualification into a column reference and needs it unresolved. + auto bound_name = binder.BindTableName(function.GetQualifiedName()); + auto func = binder.GetCatalogEntry(EntryLookupInfo(function_lookup, bound_name), OnEntryNotFound::RETURN_NULL); if (func) { // found the function - we are done return func; diff --git a/src/duckdb/src/planner/expression_binder/index_binder.cpp b/src/duckdb/src/planner/expression_binder/index_binder.cpp index 534329a79..344068cd9 100644 --- a/src/duckdb/src/planner/expression_binder/index_binder.cpp +++ b/src/duckdb/src/planner/expression_binder/index_binder.cpp @@ -49,6 +49,11 @@ unique_ptr IndexBinder::BindIndex(const UnboundIndex &unbound_index) } void IndexBinder::InitCreateIndexInfo(LogicalGet &get, CreateIndexInfo &info, const Identifier &schema) { + // the schema is taken from the table the index is created on - it can be a nested schema + InitCreateIndexInfo(get, info); +} + +void IndexBinder::InitCreateIndexInfo(LogicalGet &get, CreateIndexInfo &info) { auto &column_ids = get.GetColumnIds(); for (auto &column_id : column_ids) { if (column_id.IsRowIdColumn()) { @@ -61,8 +66,8 @@ void IndexBinder::InitCreateIndexInfo(LogicalGet &get, CreateIndexInfo &info, co info.scan_types.emplace_back(LogicalType::ROW_TYPE); info.names = get.names; - info.SetQualifiedName( - QualifiedName(get.GetTable()->catalog.GetName(), Identifier(schema), info.GetQualifiedName().Name())); + // the index lives in the same (possibly nested) schema as the table it is created on + info.SetQualifiedName(get.GetTable()->schema.GetQualifiedName(info.GetQualifiedName().Name())); get.AddColumnId(COLUMN_IDENTIFIER_ROW_ID); } @@ -73,7 +78,7 @@ unique_ptr IndexBinder::BindCreateIndex(ClientContext &context, unique_ptr alter_table_info) { // Add the dependencies. auto &dependencies = create_index_info->dependencies; - auto &catalog = Catalog::GetCatalog(context, create_index_info->GetQualifiedName().Catalog()); + auto &catalog = table_entry.ParentCatalog(); SetCatalogLookupCallback([&dependencies, &catalog](CatalogEntry &entry) { if (&catalog != &entry.ParentCatalog()) { return; @@ -88,7 +93,7 @@ unique_ptr IndexBinder::BindCreateIndex(ClientContext &context, } auto &get = plan->Cast(); - InitCreateIndexInfo(get, *create_index_info, table_entry.schema.name); + InitCreateIndexInfo(get, *create_index_info); auto &bind_data = get.bind_data->Cast(); bind_data.is_create_index = true; diff --git a/src/duckdb/src/planner/operator/logical_create_index.cpp b/src/duckdb/src/planner/operator/logical_create_index.cpp index 1787536d7..a6b19a443 100644 --- a/src/duckdb/src/planner/operator/logical_create_index.cpp +++ b/src/duckdb/src/planner/operator/logical_create_index.cpp @@ -37,10 +37,8 @@ void LogicalCreateIndex::ResolveTypes() { } TableCatalogEntry &LogicalCreateIndex::BindTable(ClientContext &context, CreateIndexInfo &info_p) { - auto &catalog = info_p.GetQualifiedName().Catalog(); - auto &schema = info_p.GetQualifiedName().Schema(); - auto &table_name = info_p.table; - return Catalog::GetEntry(context, QualifiedName(catalog, schema, table_name)); + // the table lives in the same (possibly nested) schema as the index + return Catalog::GetEntry(context, info_p.GetQualifiedName().WithName(info_p.table)); } } // namespace duckdb diff --git a/src/duckdb/src/storage/checkpoint_manager.cpp b/src/duckdb/src/storage/checkpoint_manager.cpp index 9e07c9c4b..8fb165f02 100644 --- a/src/duckdb/src/storage/checkpoint_manager.cpp +++ b/src/duckdb/src/storage/checkpoint_manager.cpp @@ -560,7 +560,7 @@ void CheckpointReader::ReadTrigger(CatalogTransaction transaction, Deserializer auto info = ReadCreateInfo(deserializer, CatalogType::TRIGGER_ENTRY, "trigger"); auto &trigger_info = info->Cast(); trigger_info.on_conflict = OnCreateConflict::IGNORE_ON_CONFLICT; - auto &schema = catalog.GetSchema(transaction, trigger_info.GetQualifiedName().Schema()); + auto &schema = catalog.GetEntrySchema(transaction, trigger_info.GetQualifiedName()); auto table_entry = schema.GetEntry(transaction, CatalogType::TABLE_ENTRY, trigger_info.base_table->Table()); if (!table_entry) { throw DataCorruptionException("corrupt database file - trigger entry without table entry"); @@ -607,7 +607,7 @@ void CheckpointReader::ReadIndex(CatalogTransaction transaction, Deserializer &d // create the index in the catalog // look for the table in the catalog - auto &schema = catalog.GetSchema(transaction, create_info->GetQualifiedName().Schema()); + auto &schema = catalog.GetEntrySchema(transaction, create_info->GetQualifiedName()); auto catalog_table = schema.GetEntry(transaction, CatalogType::TABLE_ENTRY, info.table); if (!catalog_table) { // See internal issue 3663. diff --git a/src/duckdb/src/storage/data_table.cpp b/src/duckdb/src/storage/data_table.cpp index b6aa6efc9..8f8117729 100644 --- a/src/duckdb/src/storage/data_table.cpp +++ b/src/duckdb/src/storage/data_table.cpp @@ -688,9 +688,12 @@ void DataTable::VerifyForeignKeyConstraint(optional_ptr stora dst_keys_ptr = bound_foreign_key.info.fk_keys; } - // Get the column types in their physical order. + // Get the column types in their physical order. A foreign key always references a table in the same (possibly + // nested) schema, so we qualify it with this table's schema path. + auto schema_path = info->GetSchemaPath(); + schema_path.insert(schema_path.begin(), db.GetName()); auto &table_entry = Catalog::GetEntry( - context, QualifiedName(db.GetName(), bound_foreign_key.info.schema, bound_foreign_key.info.table)); + context, QualifiedName(std::move(schema_path), bound_foreign_key.info.table)); vector types; for (auto &col : table_entry.GetColumns().Physical()) { types.emplace_back(col.Type()); diff --git a/src/duckdb/src/storage/serialization/serialize_nodes.cpp b/src/duckdb/src/storage/serialization/serialize_nodes.cpp index ee2ccc7b8..85fb3712a 100644 --- a/src/duckdb/src/storage/serialization/serialize_nodes.cpp +++ b/src/duckdb/src/storage/serialization/serialize_nodes.cpp @@ -323,6 +323,7 @@ void ExportedTableData::Serialize(Serializer &serializer) const { serializer.WritePropertyWithDefault(3, "database_name", qualified_name.Catalog()); serializer.WritePropertyWithDefault(4, "file_path", file_path); serializer.WritePropertyWithDefault>(5, "not_null_columns", not_null_columns); + serializer.WritePropertyWithDefault(6, "qualified_name", qualified_name, QualifiedName()); } ExportedTableData ExportedTableData::Deserialize(Deserializer &deserializer) { @@ -332,7 +333,11 @@ ExportedTableData ExportedTableData::Deserialize(Deserializer &deserializer) { auto database_name = deserializer.ReadPropertyWithDefault(3, "database_name"); deserializer.ReadPropertyWithDefault(4, "file_path", result.file_path); deserializer.ReadPropertyWithDefault>(5, "not_null_columns", result.not_null_columns); + auto qualified_name = deserializer.ReadPropertyWithExplicitDefault(6, "qualified_name", QualifiedName()); result.SetQualifiedName(std::move(database_name), std::move(schema_name), std::move(table_name)); + if (!qualified_name.Path().empty()) { + result.qualified_name = std::move(qualified_name); + } return result; } diff --git a/src/duckdb/src/storage/serialization/serialize_parse_info.cpp b/src/duckdb/src/storage/serialization/serialize_parse_info.cpp index a2c57dbf9..28e4f4bdd 100644 --- a/src/duckdb/src/storage/serialization/serialize_parse_info.cpp +++ b/src/duckdb/src/storage/serialization/serialize_parse_info.cpp @@ -90,6 +90,9 @@ void AlterInfo::Serialize(Serializer &serializer) const { serializer.WritePropertyWithDefault(203, "name", qualified_name.Name()); serializer.WriteProperty(204, "if_not_found", if_not_found); serializer.WritePropertyWithDefault(205, "allow_internal", allow_internal); + if (serializer.ShouldSerialize(StorageVersion::V2_0_0) || (qualified_name.Path().size() > 3)) { + serializer.WriteProperty(206, "qualified_name", qualified_name); + } } unique_ptr AlterInfo::Deserialize(Deserializer &deserializer) { @@ -99,6 +102,7 @@ unique_ptr AlterInfo::Deserialize(Deserializer &deserializer) { auto name = deserializer.ReadPropertyWithDefault(203, "name"); auto if_not_found = deserializer.ReadProperty(204, "if_not_found"); auto allow_internal = deserializer.ReadPropertyWithDefault(205, "allow_internal"); + auto qualified_name = deserializer.ReadPropertyWithExplicitDefault(206, "qualified_name", QualifiedName()); unique_ptr result; switch (type) { case AlterType::ALTER_DATABASE: @@ -125,6 +129,9 @@ unique_ptr AlterInfo::Deserialize(Deserializer &deserializer) { result->if_not_found = if_not_found; result->allow_internal = allow_internal; result->SetQualifiedName(std::move(catalog), std::move(schema), std::move(name)); + if (!qualified_name.Path().empty()) { + result->SetQualifiedName(std::move(qualified_name)); + } return std::move(result); } diff --git a/src/duckdb/src/storage/serialization/serialize_tableref.cpp b/src/duckdb/src/storage/serialization/serialize_tableref.cpp index a7b159376..9890fb746 100644 --- a/src/duckdb/src/storage/serialization/serialize_tableref.cpp +++ b/src/duckdb/src/storage/serialization/serialize_tableref.cpp @@ -66,12 +66,12 @@ unique_ptr TableRef::Deserialize(Deserializer &deserializer) { } void AtClause::Serialize(Serializer &serializer) const { - serializer.WritePropertyWithDefault(1, "unit", unit); + serializer.WritePropertyWithDefault(1, "unit", unit); serializer.WritePropertyWithDefault>(2, "expr", expr); } unique_ptr AtClause::Deserialize(Deserializer &deserializer) { - auto unit = deserializer.ReadPropertyWithDefault(1, "unit"); + auto unit = deserializer.ReadPropertyWithDefault(1, "unit"); auto expr = deserializer.ReadPropertyWithDefault>(2, "expr"); auto result = duckdb::unique_ptr(new AtClause(std::move(unit), std::move(expr))); return result; @@ -84,6 +84,9 @@ void BaseTableRef::Serialize(Serializer &serializer) const { serializer.WritePropertyWithDefault>(202, "column_name_alias", column_name_alias); serializer.WritePropertyWithDefault(203, "catalog_name", qualified_name.Catalog()); serializer.WritePropertyWithDefault>(204, "at_clause", at_clause); + if (serializer.ShouldSerialize(StorageVersion::V2_0_0) || (qualified_name.Path().size() > 3)) { + serializer.WriteProperty(205, "qualified_name", qualified_name); + } } unique_ptr BaseTableRef::Deserialize(Deserializer &deserializer) { @@ -93,7 +96,11 @@ unique_ptr BaseTableRef::Deserialize(Deserializer &deserializer) { deserializer.ReadPropertyWithDefault>(202, "column_name_alias", result->column_name_alias); auto catalog_name = deserializer.ReadPropertyWithDefault(203, "catalog_name"); deserializer.ReadPropertyWithDefault>(204, "at_clause", result->at_clause); + auto qualified_name = deserializer.ReadPropertyWithExplicitDefault(205, "qualified_name", QualifiedName()); result->SetQualifiedName(std::move(catalog_name), std::move(schema_name), std::move(table_name)); + if (!qualified_name.Path().empty()) { + result->SetQualifiedName(std::move(qualified_name)); + } return std::move(result); } diff --git a/src/duckdb/src/storage/serialization/serialize_wal.cpp b/src/duckdb/src/storage/serialization/serialize_wal.cpp index ea62cfa22..c004db675 100644 --- a/src/duckdb/src/storage/serialization/serialize_wal.cpp +++ b/src/duckdb/src/storage/serialization/serialize_wal.cpp @@ -134,42 +134,48 @@ WALCreateView WALCreateView::Deserialize(Deserializer &deserializer) { } void WALDropIndex::Serialize(Serializer &serializer) const { - if (serializer.ShouldSerialize(StorageVersion::V2_0_0)) { - serializer.WritePropertyWithDefault(101, "schema", schema); - } else { - serializer.WriteProperty(101, "schema", schema); + if (!serializer.ShouldSerialize(StorageVersion::V2_0_0)) { + serializer.WritePropertyWithDefault(101, "schema", LegacySchema()); + } + if (!serializer.ShouldSerialize(StorageVersion::V2_0_0)) { + serializer.WritePropertyWithDefault(102, "name", LegacyName()); } if (serializer.ShouldSerialize(StorageVersion::V2_0_0)) { - serializer.WritePropertyWithDefault(102, "name", name); - } else { - serializer.WriteProperty(102, "name", name); + serializer.WritePropertyWithDefault(103, "qualified_name", qualified_name, QualifiedName()); } } WALDropIndex WALDropIndex::Deserialize(Deserializer &deserializer) { WALDropIndex result; - deserializer.ReadPropertyWithDefault(101, "schema", result.schema); - deserializer.ReadPropertyWithDefault(102, "name", result.name); + auto schema = deserializer.ReadPropertyWithDefault(101, "schema"); + auto name = deserializer.ReadPropertyWithDefault(102, "name"); + deserializer.ReadPropertyWithExplicitDefault(103, "qualified_name", result.qualified_name, QualifiedName()); + if (result.qualified_name.Path().empty()) { + result.qualified_name = QualifiedName(vector {std::move(schema)}, std::move(name)); + } return result; } void WALDropMacro::Serialize(Serializer &serializer) const { - if (serializer.ShouldSerialize(StorageVersion::V2_0_0)) { - serializer.WritePropertyWithDefault(101, "schema", schema); - } else { - serializer.WriteProperty(101, "schema", schema); + if (!serializer.ShouldSerialize(StorageVersion::V2_0_0)) { + serializer.WritePropertyWithDefault(101, "schema", LegacySchema()); + } + if (!serializer.ShouldSerialize(StorageVersion::V2_0_0)) { + serializer.WritePropertyWithDefault(102, "name", LegacyName()); } if (serializer.ShouldSerialize(StorageVersion::V2_0_0)) { - serializer.WritePropertyWithDefault(102, "name", name); - } else { - serializer.WriteProperty(102, "name", name); + serializer.WritePropertyWithDefault(103, "qualified_name", qualified_name, QualifiedName()); } } WALDropMacro WALDropMacro::Deserialize(Deserializer &deserializer) { WALDropMacro result; - deserializer.ReadPropertyWithDefault(101, "schema", result.schema); - deserializer.ReadPropertyWithDefault(102, "name", result.name); + auto schema = deserializer.ReadPropertyWithDefault(101, "schema"); + auto name = deserializer.ReadPropertyWithDefault(102, "name"); + deserializer.ReadPropertyWithExplicitDefault(103, "qualified_name", result.qualified_name, QualifiedName()); + if (result.qualified_name.Path().empty()) { + result.qualified_name = QualifiedName(vector {std::move(schema)}, std::move(name)); + } return result; } @@ -190,22 +196,25 @@ WALDropSchema WALDropSchema::Deserialize(Deserializer &deserializer) { } void WALDropSequence::Serialize(Serializer &serializer) const { - if (serializer.ShouldSerialize(StorageVersion::V2_0_0)) { - serializer.WritePropertyWithDefault(101, "schema", schema); - } else { - serializer.WriteProperty(101, "schema", schema); + if (!serializer.ShouldSerialize(StorageVersion::V2_0_0)) { + serializer.WritePropertyWithDefault(101, "schema", LegacySchema()); + } + if (!serializer.ShouldSerialize(StorageVersion::V2_0_0)) { + serializer.WritePropertyWithDefault(102, "name", LegacyName()); } if (serializer.ShouldSerialize(StorageVersion::V2_0_0)) { - serializer.WritePropertyWithDefault(102, "name", name); - } else { - serializer.WriteProperty(102, "name", name); + serializer.WritePropertyWithDefault(103, "qualified_name", qualified_name, QualifiedName()); } } WALDropSequence WALDropSequence::Deserialize(Deserializer &deserializer) { WALDropSequence result; - deserializer.ReadPropertyWithDefault(101, "schema", result.schema); - deserializer.ReadPropertyWithDefault(102, "name", result.name); + auto schema = deserializer.ReadPropertyWithDefault(101, "schema"); + auto name = deserializer.ReadPropertyWithDefault(102, "name"); + deserializer.ReadPropertyWithExplicitDefault(103, "qualified_name", result.qualified_name, QualifiedName()); + if (result.qualified_name.Path().empty()) { + result.qualified_name = QualifiedName(vector {std::move(schema)}, std::move(name)); + } return result; } @@ -233,101 +242,105 @@ WALDropTable WALDropTable::Deserialize(Deserializer &deserializer) { } void WALDropTableMacro::Serialize(Serializer &serializer) const { - if (serializer.ShouldSerialize(StorageVersion::V2_0_0)) { - serializer.WritePropertyWithDefault(101, "schema", schema); - } else { - serializer.WriteProperty(101, "schema", schema); + if (!serializer.ShouldSerialize(StorageVersion::V2_0_0)) { + serializer.WritePropertyWithDefault(101, "schema", LegacySchema()); + } + if (!serializer.ShouldSerialize(StorageVersion::V2_0_0)) { + serializer.WritePropertyWithDefault(102, "name", LegacyName()); } if (serializer.ShouldSerialize(StorageVersion::V2_0_0)) { - serializer.WritePropertyWithDefault(102, "name", name); - } else { - serializer.WriteProperty(102, "name", name); + serializer.WritePropertyWithDefault(103, "qualified_name", qualified_name, QualifiedName()); } } WALDropTableMacro WALDropTableMacro::Deserialize(Deserializer &deserializer) { WALDropTableMacro result; - deserializer.ReadPropertyWithDefault(101, "schema", result.schema); - deserializer.ReadPropertyWithDefault(102, "name", result.name); + auto schema = deserializer.ReadPropertyWithDefault(101, "schema"); + auto name = deserializer.ReadPropertyWithDefault(102, "name"); + deserializer.ReadPropertyWithExplicitDefault(103, "qualified_name", result.qualified_name, QualifiedName()); + if (result.qualified_name.Path().empty()) { + result.qualified_name = QualifiedName(vector {std::move(schema)}, std::move(name)); + } return result; } void WALDropTrigger::Serialize(Serializer &serializer) const { - if (serializer.ShouldSerialize(StorageVersion::V2_0_0)) { - serializer.WritePropertyWithDefault(101, "schema", schema); - } else { - serializer.WriteProperty(101, "schema", schema); + if (!serializer.ShouldSerialize(StorageVersion::V2_0_0)) { + serializer.WritePropertyWithDefault(101, "schema", LegacySchema()); } - if (serializer.ShouldSerialize(StorageVersion::V2_0_0)) { - serializer.WritePropertyWithDefault(102, "name", name); - } else { - serializer.WriteProperty(102, "name", name); + if (!serializer.ShouldSerialize(StorageVersion::V2_0_0)) { + serializer.WritePropertyWithDefault(102, "name", LegacyName()); } + serializer.WritePropertyWithDefault(103, "table", table); if (serializer.ShouldSerialize(StorageVersion::V2_0_0)) { - serializer.WritePropertyWithDefault(103, "table", table); - } else { - serializer.WriteProperty(103, "table", table); + serializer.WritePropertyWithDefault(104, "qualified_name", qualified_name, QualifiedName()); } } WALDropTrigger WALDropTrigger::Deserialize(Deserializer &deserializer) { WALDropTrigger result; - deserializer.ReadPropertyWithDefault(101, "schema", result.schema); - deserializer.ReadPropertyWithDefault(102, "name", result.name); + auto schema = deserializer.ReadPropertyWithDefault(101, "schema"); + auto name = deserializer.ReadPropertyWithDefault(102, "name"); deserializer.ReadPropertyWithDefault(103, "table", result.table); + deserializer.ReadPropertyWithExplicitDefault(104, "qualified_name", result.qualified_name, QualifiedName()); + if (result.qualified_name.Path().empty()) { + result.qualified_name = QualifiedName(vector {std::move(schema)}, std::move(name)); + } return result; } void WALDropType::Serialize(Serializer &serializer) const { - if (serializer.ShouldSerialize(StorageVersion::V2_0_0)) { - serializer.WritePropertyWithDefault(101, "schema", schema); - } else { - serializer.WriteProperty(101, "schema", schema); + if (!serializer.ShouldSerialize(StorageVersion::V2_0_0)) { + serializer.WritePropertyWithDefault(101, "schema", LegacySchema()); + } + if (!serializer.ShouldSerialize(StorageVersion::V2_0_0)) { + serializer.WritePropertyWithDefault(102, "name", LegacyName()); } if (serializer.ShouldSerialize(StorageVersion::V2_0_0)) { - serializer.WritePropertyWithDefault(102, "name", name); - } else { - serializer.WriteProperty(102, "name", name); + serializer.WritePropertyWithDefault(103, "qualified_name", qualified_name, QualifiedName()); } } WALDropType WALDropType::Deserialize(Deserializer &deserializer) { WALDropType result; - deserializer.ReadPropertyWithDefault(101, "schema", result.schema); - deserializer.ReadPropertyWithDefault(102, "name", result.name); + auto schema = deserializer.ReadPropertyWithDefault(101, "schema"); + auto name = deserializer.ReadPropertyWithDefault(102, "name"); + deserializer.ReadPropertyWithExplicitDefault(103, "qualified_name", result.qualified_name, QualifiedName()); + if (result.qualified_name.Path().empty()) { + result.qualified_name = QualifiedName(vector {std::move(schema)}, std::move(name)); + } return result; } void WALDropView::Serialize(Serializer &serializer) const { - if (serializer.ShouldSerialize(StorageVersion::V2_0_0)) { - serializer.WritePropertyWithDefault(101, "schema", schema); - } else { - serializer.WriteProperty(101, "schema", schema); + if (!serializer.ShouldSerialize(StorageVersion::V2_0_0)) { + serializer.WritePropertyWithDefault(101, "schema", LegacySchema()); + } + if (!serializer.ShouldSerialize(StorageVersion::V2_0_0)) { + serializer.WritePropertyWithDefault(102, "name", LegacyName()); } if (serializer.ShouldSerialize(StorageVersion::V2_0_0)) { - serializer.WritePropertyWithDefault(102, "name", name); - } else { - serializer.WriteProperty(102, "name", name); + serializer.WritePropertyWithDefault(103, "qualified_name", qualified_name, QualifiedName()); } } WALDropView WALDropView::Deserialize(Deserializer &deserializer) { WALDropView result; - deserializer.ReadPropertyWithDefault(101, "schema", result.schema); - deserializer.ReadPropertyWithDefault(102, "name", result.name); + auto schema = deserializer.ReadPropertyWithDefault(101, "schema"); + auto name = deserializer.ReadPropertyWithDefault(102, "name"); + deserializer.ReadPropertyWithExplicitDefault(103, "qualified_name", result.qualified_name, QualifiedName()); + if (result.qualified_name.Path().empty()) { + result.qualified_name = QualifiedName(vector {std::move(schema)}, std::move(name)); + } return result; } void WALSequenceValue::Serialize(Serializer &serializer) const { - if (serializer.ShouldSerialize(StorageVersion::V2_0_0)) { - serializer.WritePropertyWithDefault(101, "schema", schema); - } else { - serializer.WriteProperty(101, "schema", schema); + if (!serializer.ShouldSerialize(StorageVersion::V2_0_0)) { + serializer.WritePropertyWithDefault(101, "schema", LegacySchema()); } - if (serializer.ShouldSerialize(StorageVersion::V2_0_0)) { - serializer.WritePropertyWithDefault(102, "name", name); - } else { - serializer.WriteProperty(102, "name", name); + if (!serializer.ShouldSerialize(StorageVersion::V2_0_0)) { + serializer.WritePropertyWithDefault(102, "name", LegacyName()); } if (serializer.ShouldSerialize(StorageVersion::V2_0_0)) { serializer.WritePropertyWithDefault(103, "usage_count", usage_count); @@ -342,15 +355,22 @@ void WALSequenceValue::Serialize(Serializer &serializer) const { if (serializer.ShouldSerialize(StorageVersion::V2_0_0)) { serializer.WritePropertyWithDefault>(105, "last_value", last_value); } + if (serializer.ShouldSerialize(StorageVersion::V2_0_0)) { + serializer.WritePropertyWithDefault(106, "qualified_name", qualified_name, QualifiedName()); + } } WALSequenceValue WALSequenceValue::Deserialize(Deserializer &deserializer) { WALSequenceValue result; - deserializer.ReadPropertyWithDefault(101, "schema", result.schema); - deserializer.ReadPropertyWithDefault(102, "name", result.name); + auto schema = deserializer.ReadPropertyWithDefault(101, "schema"); + auto name = deserializer.ReadPropertyWithDefault(102, "name"); deserializer.ReadPropertyWithDefault(103, "usage_count", result.usage_count); deserializer.ReadPropertyWithDefault(104, "counter", result.counter); deserializer.ReadPropertyWithDefault>(105, "last_value", result.last_value); + deserializer.ReadPropertyWithExplicitDefault(106, "qualified_name", result.qualified_name, QualifiedName()); + if (result.qualified_name.Path().empty()) { + result.qualified_name = QualifiedName(vector {std::move(schema)}, std::move(name)); + } return result; } diff --git a/src/duckdb/src/storage/table_index_list.cpp b/src/duckdb/src/storage/table_index_list.cpp index d944677a3..e280d3809 100644 --- a/src/duckdb/src/storage/table_index_list.cpp +++ b/src/duckdb/src/storage/table_index_list.cpp @@ -169,10 +169,11 @@ void TableIndexList::Bind(ClientContext &context, DataTableInfo &table_info, con // Get the table from the catalog, so we can add it to the binder. auto &catalog = table_info.GetDB().GetCatalog(); - auto schema = table_info.GetSchemaName(); - auto table_name = table_info.GetTableName(); + // the table can live in a nested schema - qualify it with the full schema path + auto schema_path = table_info.GetSchemaPath(); + schema_path.insert(schema_path.begin(), catalog.GetName()); auto &table_entry = - catalog.GetEntry(context, QualifiedName(catalog.GetName(), schema, table_name)); + catalog.GetEntry(context, QualifiedName(std::move(schema_path), table_info.GetTableName())); auto &table = table_entry.Cast(); vector column_types; diff --git a/src/duckdb/src/storage/wal_replay.cpp b/src/duckdb/src/storage/wal_replay.cpp index 00f321c43..11ddcbcab 100644 --- a/src/duckdb/src/storage/wal_replay.cpp +++ b/src/duckdb/src/storage/wal_replay.cpp @@ -719,6 +719,24 @@ void WriteAheadLogDeserializer::ReplayVersion() { } } +//! Qualify a name stored in the WAL as [schema_path..., name] (i.e. without a catalog component) with the catalog we +//! are replaying into, so nested schemas can be navigated. Do not use WithCatalog() here: that treats the leading +//! component of a 3-element path as a catalog, which would drop the outermost schema of a nested path. +static QualifiedName ReplayEntryName(Catalog &catalog, const QualifiedName &entry_name) { + vector path; + path.push_back(catalog.GetName()); + for (idx_t i = 0; i + 1 < entry_name.Path().size(); i++) { + path.push_back(entry_name.Path()[i]); + } + return QualifiedName(std::move(path), entry_name.Name()); +} + +//! Re-qualify a serialized [catalog, schema_path..., name] entry name (as carried by a CreateInfo) for the catalog it +//! is replayed into: the (possibly nested) schema path is kept, the serialized catalog component is replaced +static QualifiedName ReplayQualifiedName(Catalog &catalog, const QualifiedName &entry_name, const Identifier &name) { + return entry_name.WithCatalog(catalog.GetName()).WithName(name); +} + //===--------------------------------------------------------------------===// // Replay Table //===--------------------------------------------------------------------===// @@ -775,6 +793,9 @@ void ReplayWithoutIndex(ClientContext &context, Catalog &catalog, AlterInfo &inf if (only_deserialize) { return; } + // the WAL carries the catalog name the entry had when it was written - re-root it in the catalog we replay into + // (the database can be attached under a different name) + info.SetQualifiedName(info.GetQualifiedName().WithCatalog(catalog.GetName())); catalog.Alter(context, info); } @@ -837,9 +858,8 @@ void WriteAheadLogDeserializer::ReplayAlter() { auto &unique_info = constraint_info.constraint->Cast(); auto &table = catalog - .GetEntry(context, QualifiedName(catalog.GetName(), - table_info.GetQualifiedName().Schema(), - table_info.GetQualifiedName().Name())) + .GetEntry(context, ReplayQualifiedName(catalog, table_info.GetQualifiedName(), + table_info.GetQualifiedName().Name())) .Cast(); auto &column_list = table.GetColumns(); @@ -904,7 +924,7 @@ void WriteAheadLogDeserializer::ReplayDropView() { auto entry = WALDropView::Deserialize(deserializer); DropInfo info; info.type = CatalogType::VIEW_ENTRY; - info.SetQualifiedName(QualifiedName({std::move(entry.schema)}, std::move(entry.name))); + info.SetQualifiedName(ReplayEntryName(catalog, entry.qualified_name)); if (DeserializeOnly()) { return; } @@ -982,7 +1002,7 @@ void WriteAheadLogDeserializer::ReplayDropType() { DropInfo info; info.type = CatalogType::TYPE_ENTRY; - info.SetQualifiedName(QualifiedName({std::move(entry.schema)}, std::move(entry.name))); + info.SetQualifiedName(ReplayEntryName(catalog, entry.qualified_name)); if (DeserializeOnly()) { return; } @@ -1001,9 +1021,9 @@ void WriteAheadLogDeserializer::ReplayCreateTrigger() { return; } auto &trigger_info = info->Cast(); - auto &table = Catalog::GetEntry(context, QualifiedName(trigger_info.GetQualifiedName().Catalog(), - trigger_info.GetQualifiedName().Schema(), - trigger_info.base_table->Table())); + // the trigger lives in the same (possibly nested) schema as its base table + auto &table = Catalog::GetEntry( + context, ReplayQualifiedName(catalog, trigger_info.GetQualifiedName(), trigger_info.base_table->Table())); auto &duck_table = table.Cast(); auto transaction = catalog.GetCatalogTransaction(context); duck_table.CreateTrigger(transaction, trigger_info); @@ -1014,7 +1034,7 @@ void WriteAheadLogDeserializer::ReplayDropTrigger() { DropInfo info; info.type = CatalogType::TRIGGER_ENTRY; auto table_name = std::move(entry.table); - info.SetQualifiedName(QualifiedName({std::move(entry.schema)}, std::move(entry.name))); + info.SetQualifiedName(ReplayEntryName(catalog, entry.qualified_name)); if (DeserializeOnly()) { return; } @@ -1022,8 +1042,9 @@ void WriteAheadLogDeserializer::ReplayDropTrigger() { throw InternalException("WAL replay: DROP TRIGGER entry has an empty table name for trigger \"%s\"", info.GetQualifiedName().Name()); } - auto &table = Catalog::GetEntry( - context, QualifiedName(catalog.GetName(), info.GetQualifiedName().Schema(), table_name)); + // the trigger lives in the same (possibly nested) schema as its base table + auto &table = + Catalog::GetEntry(context, info.GetQualifiedName().WithName(std::move(table_name))); auto &duck_table = table.Cast(); auto transaction = catalog.GetCatalogTransaction(context); duck_table.DropTrigger(transaction, info.GetQualifiedName().Name(), info.cascade); @@ -1046,7 +1067,7 @@ void WriteAheadLogDeserializer::ReplayDropSequence() { auto entry = WALDropSequence::Deserialize(deserializer); DropInfo info; info.type = CatalogType::SEQUENCE_ENTRY; - info.SetQualifiedName(QualifiedName({std::move(entry.schema)}, std::move(entry.name))); + info.SetQualifiedName(ReplayEntryName(catalog, entry.qualified_name)); if (DeserializeOnly()) { return; } @@ -1062,8 +1083,7 @@ void WriteAheadLogDeserializer::ReplaySequenceValue() { } // fetch the sequence from the catalog - auto &seq = catalog.GetEntry( - context, QualifiedName(catalog.GetName(), std::move(entry.schema), std::move(entry.name))); + auto &seq = catalog.GetEntry(context, ReplayEntryName(catalog, entry.qualified_name)); seq.ReplayValue(entry.usage_count, entry.counter, entry.last_value); } @@ -1084,7 +1104,7 @@ void WriteAheadLogDeserializer::ReplayDropMacro() { auto entry = WALDropMacro::Deserialize(deserializer); DropInfo info; info.type = CatalogType::MACRO_ENTRY; - info.SetQualifiedName(QualifiedName({std::move(entry.schema)}, std::move(entry.name))); + info.SetQualifiedName(ReplayEntryName(catalog, entry.qualified_name)); if (DeserializeOnly()) { return; } @@ -1108,7 +1128,7 @@ void WriteAheadLogDeserializer::ReplayDropTableMacro() { auto entry = WALDropTableMacro::Deserialize(deserializer); DropInfo info; info.type = CatalogType::TABLE_MACRO_ENTRY; - info.SetQualifiedName(QualifiedName({std::move(entry.schema)}, std::move(entry.name))); + info.SetQualifiedName(ReplayEntryName(catalog, entry.qualified_name)); if (DeserializeOnly()) { return; } @@ -1137,8 +1157,9 @@ void WriteAheadLogDeserializer::ReplayCreateIndex() { const auto schema_name = create_info->GetQualifiedName().Schema(); const auto table_name = info.table; - auto &entry = - catalog.GetEntry(context, QualifiedName(catalog.GetName(), schema_name, table_name)); + // the table lives in the same (possibly nested) schema as the index + auto &entry = catalog.GetEntry( + context, ReplayQualifiedName(catalog, create_info->GetQualifiedName(), table_name)); auto &table = entry.Cast(); auto &storage = table.GetStorage(); auto &io_manager = TableIOManager::Get(storage); @@ -1157,7 +1178,7 @@ void WriteAheadLogDeserializer::ReplayDropIndex() { auto entry = WALDropIndex::Deserialize(deserializer); DropInfo info; info.type = CatalogType::INDEX_ENTRY; - info.SetQualifiedName(QualifiedName({std::move(entry.schema)}, std::move(entry.name))); + info.SetQualifiedName(ReplayEntryName(catalog, entry.qualified_name)); if (DeserializeOnly()) { return; } diff --git a/src/duckdb/src/storage/write_ahead_log.cpp b/src/duckdb/src/storage/write_ahead_log.cpp index d87fd0f64..de6cc0538 100644 --- a/src/duckdb/src/storage/write_ahead_log.cpp +++ b/src/duckdb/src/storage/write_ahead_log.cpp @@ -343,7 +343,7 @@ void WriteAheadLog::WriteCreateSequence(const SequenceCatalogEntry &entry) { void WriteAheadLog::WriteDropSequence(const SequenceCatalogEntry &entry) { WriteAheadLogSerializer serializer(*this, WALType::DROP_SEQUENCE); - serializer.WriteEntry(WALDropSequence {entry.schema.name, entry.name}); + serializer.WriteEntry(WALDropSequence(QualifiedName(entry.schema.GetSchemaPath(), entry.name))); serializer.End(); } @@ -351,8 +351,8 @@ void WriteAheadLog::WriteSequenceValue(SequenceValue val) { auto &sequence = *val.entry; WriteAheadLogSerializer serializer(*this, WALType::SEQUENCE_VALUE); // last_value (id 105) is only serialized from storage version v2.0.0 onwards, and is omitted when unset - serializer.WriteEntry(WALSequenceValue {sequence.schema.name, sequence.name, val.usage_count, val.counter, - val.entry->GetData().last_value}); + serializer.WriteEntry(WALSequenceValue(QualifiedName(sequence.schema.GetSchemaPath(), sequence.name), + val.usage_count, val.counter, val.entry->GetData().last_value)); serializer.End(); } @@ -367,7 +367,7 @@ void WriteAheadLog::WriteCreateMacro(const ScalarMacroCatalogEntry &entry) { void WriteAheadLog::WriteDropMacro(const ScalarMacroCatalogEntry &entry) { WriteAheadLogSerializer serializer(*this, WALType::DROP_MACRO); - serializer.WriteEntry(WALDropMacro {entry.schema.name, entry.name}); + serializer.WriteEntry(WALDropMacro(QualifiedName(entry.schema.GetSchemaPath(), entry.name))); serializer.End(); } @@ -379,7 +379,7 @@ void WriteAheadLog::WriteCreateTableMacro(const TableMacroCatalogEntry &entry) { void WriteAheadLog::WriteDropTableMacro(const TableMacroCatalogEntry &entry) { WriteAheadLogSerializer serializer(*this, WALType::DROP_TABLE_MACRO); - serializer.WriteEntry(WALDropTableMacro {entry.schema.name, entry.name}); + serializer.WriteEntry(WALDropTableMacro(QualifiedName(entry.schema.GetSchemaPath(), entry.name))); serializer.End(); } @@ -428,7 +428,7 @@ void WriteAheadLog::WriteCreateIndex(const IndexCatalogEntry &entry) { void WriteAheadLog::WriteDropIndex(const IndexCatalogEntry &entry) { WriteAheadLogSerializer serializer(*this, WALType::DROP_INDEX); - serializer.WriteEntry(WALDropIndex {entry.schema.name, entry.name}); + serializer.WriteEntry(WALDropIndex(QualifiedName(entry.schema.GetSchemaPath(), entry.name))); serializer.End(); } @@ -443,7 +443,7 @@ void WriteAheadLog::WriteCreateType(const TypeCatalogEntry &entry) { void WriteAheadLog::WriteDropType(const TypeCatalogEntry &entry) { WriteAheadLogSerializer serializer(*this, WALType::DROP_TYPE); - serializer.WriteEntry(WALDropType {entry.schema.name, entry.name}); + serializer.WriteEntry(WALDropType(QualifiedName(entry.schema.GetSchemaPath(), entry.name))); serializer.End(); } @@ -458,7 +458,8 @@ void WriteAheadLog::WriteCreateTrigger(const TriggerCatalogEntry &entry) { void WriteAheadLog::WriteDropTrigger(const TriggerCatalogEntry &entry) { WriteAheadLogSerializer serializer(*this, WALType::DROP_TRIGGER); - serializer.WriteEntry(WALDropTrigger {entry.schema.name, entry.name, entry.base_table->Table()}); + serializer.WriteEntry( + WALDropTrigger(QualifiedName(entry.schema.GetSchemaPath(), entry.name), entry.base_table->Table())); serializer.End(); } @@ -473,7 +474,7 @@ void WriteAheadLog::WriteCreateView(const ViewCatalogEntry &entry) { void WriteAheadLog::WriteDropView(const ViewCatalogEntry &entry) { WriteAheadLogSerializer serializer(*this, WALType::DROP_VIEW); - serializer.WriteEntry(WALDropView {entry.schema.name, entry.name}); + serializer.WriteEntry(WALDropView(QualifiedName(entry.schema.GetSchemaPath(), entry.name))); serializer.End(); }