1#include "duckdb/catalog/catalog.hpp"
2
3#include "duckdb/catalog/catalog_search_path.hpp"
4#include "duckdb/catalog/catalog_entry/list.hpp"
5#include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp"
6#include "duckdb/catalog/catalog_set.hpp"
7#include "duckdb/catalog/default/default_schemas.hpp"
8#include "duckdb/catalog/catalog_entry/type_catalog_entry.hpp"
9#include "duckdb/common/exception.hpp"
10#include "duckdb/main/client_context.hpp"
11#include "duckdb/main/client_data.hpp"
12#include "duckdb/main/database.hpp"
13#include "duckdb/parser/expression/function_expression.hpp"
14#include "duckdb/parser/parsed_data/alter_table_info.hpp"
15#include "duckdb/parser/parsed_data/create_aggregate_function_info.hpp"
16#include "duckdb/parser/parsed_data/create_collation_info.hpp"
17#include "duckdb/parser/parsed_data/create_copy_function_info.hpp"
18#include "duckdb/parser/parsed_data/create_index_info.hpp"
19#include "duckdb/parser/parsed_data/create_pragma_function_info.hpp"
20#include "duckdb/parser/parsed_data/create_scalar_function_info.hpp"
21#include "duckdb/parser/parsed_data/create_schema_info.hpp"
22#include "duckdb/parser/parsed_data/create_sequence_info.hpp"
23#include "duckdb/parser/parsed_data/create_table_function_info.hpp"
24#include "duckdb/parser/parsed_data/create_type_info.hpp"
25#include "duckdb/parser/parsed_data/create_view_info.hpp"
26#include "duckdb/parser/parsed_data/drop_info.hpp"
27#include "duckdb/planner/parsed_data/bound_create_table_info.hpp"
28#include "duckdb/planner/binder.hpp"
29#include "duckdb/catalog/default/default_types.hpp"
30#include "duckdb/main/extension_entries.hpp"
31#include "duckdb/main/connection.hpp"
32#include "duckdb/main/attached_database.hpp"
33#include "duckdb/main/database_manager.hpp"
34#include "duckdb/function/built_in_functions.hpp"
35#include "duckdb/catalog/similar_catalog_entry.hpp"
36#include <algorithm>
37
38namespace duckdb {
39
40Catalog::Catalog(AttachedDatabase &db) : db(db) {
41}
42
43Catalog::~Catalog() {
44}
45
46DatabaseInstance &Catalog::GetDatabase() {
47 return db.GetDatabase();
48}
49
50AttachedDatabase &Catalog::GetAttached() {
51 return db;
52}
53
54const string &Catalog::GetName() {
55 return GetAttached().GetName();
56}
57
58idx_t Catalog::GetOid() {
59 return GetAttached().oid;
60}
61
62Catalog &Catalog::GetSystemCatalog(ClientContext &context) {
63 return Catalog::GetSystemCatalog(db&: *context.db);
64}
65
66optional_ptr<Catalog> Catalog::GetCatalogEntry(ClientContext &context, const string &catalog_name) {
67 auto &db_manager = DatabaseManager::Get(db&: context);
68 if (catalog_name == TEMP_CATALOG) {
69 return &ClientData::Get(context).temporary_objects->GetCatalog();
70 }
71 if (catalog_name == SYSTEM_CATALOG) {
72 return &GetSystemCatalog(context);
73 }
74 auto entry = db_manager.GetDatabase(
75 context, name: IsInvalidCatalog(str: catalog_name) ? DatabaseManager::GetDefaultDatabase(context) : catalog_name);
76 if (!entry) {
77 return nullptr;
78 }
79 return &entry->GetCatalog();
80}
81
82Catalog &Catalog::GetCatalog(ClientContext &context, const string &catalog_name) {
83 auto catalog = Catalog::GetCatalogEntry(context, catalog_name);
84 if (!catalog) {
85 throw BinderException("Catalog \"%s\" does not exist!", catalog_name);
86 }
87 return *catalog;
88}
89
90//===--------------------------------------------------------------------===//
91// Schema
92//===--------------------------------------------------------------------===//
93optional_ptr<CatalogEntry> Catalog::CreateSchema(ClientContext &context, CreateSchemaInfo &info) {
94 return CreateSchema(transaction: GetCatalogTransaction(context), info);
95}
96
97CatalogTransaction Catalog::GetCatalogTransaction(ClientContext &context) {
98 return CatalogTransaction(*this, context);
99}
100
101//===--------------------------------------------------------------------===//
102// Table
103//===--------------------------------------------------------------------===//
104optional_ptr<CatalogEntry> Catalog::CreateTable(ClientContext &context, BoundCreateTableInfo &info) {
105 return CreateTable(transaction: GetCatalogTransaction(context), info);
106}
107
108optional_ptr<CatalogEntry> Catalog::CreateTable(ClientContext &context, unique_ptr<CreateTableInfo> info) {
109 auto binder = Binder::CreateBinder(context);
110 auto bound_info = binder->BindCreateTableInfo(info: std::move(info));
111 return CreateTable(context, info&: *bound_info);
112}
113
114optional_ptr<CatalogEntry> Catalog::CreateTable(CatalogTransaction transaction, SchemaCatalogEntry &schema,
115 BoundCreateTableInfo &info) {
116 return schema.CreateTable(transaction, info);
117}
118
119optional_ptr<CatalogEntry> Catalog::CreateTable(CatalogTransaction transaction, BoundCreateTableInfo &info) {
120 auto &schema = GetSchema(transaction, name: info.base->schema);
121 return CreateTable(transaction, schema, info);
122}
123
124//===--------------------------------------------------------------------===//
125// View
126//===--------------------------------------------------------------------===//
127optional_ptr<CatalogEntry> Catalog::CreateView(CatalogTransaction transaction, CreateViewInfo &info) {
128 auto &schema = GetSchema(transaction, name: info.schema);
129 return CreateView(transaction, schema, info);
130}
131
132optional_ptr<CatalogEntry> Catalog::CreateView(ClientContext &context, CreateViewInfo &info) {
133 return CreateView(transaction: GetCatalogTransaction(context), info);
134}
135
136optional_ptr<CatalogEntry> Catalog::CreateView(CatalogTransaction transaction, SchemaCatalogEntry &schema,
137 CreateViewInfo &info) {
138 return schema.CreateView(transaction, info);
139}
140
141//===--------------------------------------------------------------------===//
142// Sequence
143//===--------------------------------------------------------------------===//
144optional_ptr<CatalogEntry> Catalog::CreateSequence(CatalogTransaction transaction, CreateSequenceInfo &info) {
145 auto &schema = GetSchema(transaction, name: info.schema);
146 return CreateSequence(transaction, schema, info);
147}
148
149optional_ptr<CatalogEntry> Catalog::CreateSequence(ClientContext &context, CreateSequenceInfo &info) {
150 return CreateSequence(transaction: GetCatalogTransaction(context), info);
151}
152
153optional_ptr<CatalogEntry> Catalog::CreateSequence(CatalogTransaction transaction, SchemaCatalogEntry &schema,
154 CreateSequenceInfo &info) {
155 return schema.CreateSequence(transaction, info);
156}
157
158//===--------------------------------------------------------------------===//
159// Type
160//===--------------------------------------------------------------------===//
161optional_ptr<CatalogEntry> Catalog::CreateType(CatalogTransaction transaction, CreateTypeInfo &info) {
162 auto &schema = GetSchema(transaction, name: info.schema);
163 return CreateType(transaction, schema, info);
164}
165
166optional_ptr<CatalogEntry> Catalog::CreateType(ClientContext &context, CreateTypeInfo &info) {
167 return CreateType(transaction: GetCatalogTransaction(context), info);
168}
169
170optional_ptr<CatalogEntry> Catalog::CreateType(CatalogTransaction transaction, SchemaCatalogEntry &schema,
171 CreateTypeInfo &info) {
172 return schema.CreateType(transaction, info);
173}
174
175//===--------------------------------------------------------------------===//
176// Table Function
177//===--------------------------------------------------------------------===//
178optional_ptr<CatalogEntry> Catalog::CreateTableFunction(CatalogTransaction transaction, CreateTableFunctionInfo &info) {
179 auto &schema = GetSchema(transaction, name: info.schema);
180 return CreateTableFunction(transaction, schema, info);
181}
182
183optional_ptr<CatalogEntry> Catalog::CreateTableFunction(ClientContext &context, CreateTableFunctionInfo &info) {
184 return CreateTableFunction(transaction: GetCatalogTransaction(context), info);
185}
186
187optional_ptr<CatalogEntry> Catalog::CreateTableFunction(CatalogTransaction transaction, SchemaCatalogEntry &schema,
188 CreateTableFunctionInfo &info) {
189 return schema.CreateTableFunction(transaction, info);
190}
191
192optional_ptr<CatalogEntry> Catalog::CreateTableFunction(ClientContext &context,
193 optional_ptr<CreateTableFunctionInfo> info) {
194 return CreateTableFunction(context, info&: *info);
195}
196
197//===--------------------------------------------------------------------===//
198// Copy Function
199//===--------------------------------------------------------------------===//
200optional_ptr<CatalogEntry> Catalog::CreateCopyFunction(CatalogTransaction transaction, CreateCopyFunctionInfo &info) {
201 auto &schema = GetSchema(transaction, name: info.schema);
202 return CreateCopyFunction(transaction, schema, info);
203}
204
205optional_ptr<CatalogEntry> Catalog::CreateCopyFunction(ClientContext &context, CreateCopyFunctionInfo &info) {
206 return CreateCopyFunction(transaction: GetCatalogTransaction(context), info);
207}
208
209optional_ptr<CatalogEntry> Catalog::CreateCopyFunction(CatalogTransaction transaction, SchemaCatalogEntry &schema,
210 CreateCopyFunctionInfo &info) {
211 return schema.CreateCopyFunction(transaction, info);
212}
213
214//===--------------------------------------------------------------------===//
215// Pragma Function
216//===--------------------------------------------------------------------===//
217optional_ptr<CatalogEntry> Catalog::CreatePragmaFunction(CatalogTransaction transaction,
218 CreatePragmaFunctionInfo &info) {
219 auto &schema = GetSchema(transaction, name: info.schema);
220 return CreatePragmaFunction(transaction, schema, info);
221}
222
223optional_ptr<CatalogEntry> Catalog::CreatePragmaFunction(ClientContext &context, CreatePragmaFunctionInfo &info) {
224 return CreatePragmaFunction(transaction: GetCatalogTransaction(context), info);
225}
226
227optional_ptr<CatalogEntry> Catalog::CreatePragmaFunction(CatalogTransaction transaction, SchemaCatalogEntry &schema,
228 CreatePragmaFunctionInfo &info) {
229 return schema.CreatePragmaFunction(transaction, info);
230}
231
232//===--------------------------------------------------------------------===//
233// Function
234//===--------------------------------------------------------------------===//
235optional_ptr<CatalogEntry> Catalog::CreateFunction(CatalogTransaction transaction, CreateFunctionInfo &info) {
236 auto &schema = GetSchema(transaction, name: info.schema);
237 return CreateFunction(transaction, schema, info);
238}
239
240optional_ptr<CatalogEntry> Catalog::CreateFunction(ClientContext &context, CreateFunctionInfo &info) {
241 return CreateFunction(transaction: GetCatalogTransaction(context), info);
242}
243
244optional_ptr<CatalogEntry> Catalog::CreateFunction(CatalogTransaction transaction, SchemaCatalogEntry &schema,
245 CreateFunctionInfo &info) {
246 return schema.CreateFunction(transaction, info);
247}
248
249optional_ptr<CatalogEntry> Catalog::AddFunction(ClientContext &context, CreateFunctionInfo &info) {
250 info.on_conflict = OnCreateConflict::ALTER_ON_CONFLICT;
251 return CreateFunction(context, info);
252}
253
254//===--------------------------------------------------------------------===//
255// Collation
256//===--------------------------------------------------------------------===//
257optional_ptr<CatalogEntry> Catalog::CreateCollation(CatalogTransaction transaction, CreateCollationInfo &info) {
258 auto &schema = GetSchema(transaction, name: info.schema);
259 return CreateCollation(transaction, schema, info);
260}
261
262optional_ptr<CatalogEntry> Catalog::CreateCollation(ClientContext &context, CreateCollationInfo &info) {
263 return CreateCollation(transaction: GetCatalogTransaction(context), info);
264}
265
266optional_ptr<CatalogEntry> Catalog::CreateCollation(CatalogTransaction transaction, SchemaCatalogEntry &schema,
267 CreateCollationInfo &info) {
268 return schema.CreateCollation(transaction, info);
269}
270
271//===--------------------------------------------------------------------===//
272// Index
273//===--------------------------------------------------------------------===//
274optional_ptr<CatalogEntry> Catalog::CreateIndex(CatalogTransaction transaction, CreateIndexInfo &info) {
275 auto &context = transaction.GetContext();
276 return CreateIndex(context, info);
277}
278
279optional_ptr<CatalogEntry> Catalog::CreateIndex(ClientContext &context, CreateIndexInfo &info) {
280 auto &schema = GetSchema(context, name: info.schema);
281 auto &table = GetEntry<TableCatalogEntry>(context, schema_name: schema.name, name: info.table->table_name);
282 return schema.CreateIndex(context, info, table);
283}
284
285//===--------------------------------------------------------------------===//
286// Lookup Structures
287//===--------------------------------------------------------------------===//
288struct CatalogLookup {
289 CatalogLookup(Catalog &catalog, string schema_p) : catalog(catalog), schema(std::move(schema_p)) {
290 }
291
292 Catalog &catalog;
293 string schema;
294};
295
296//! Return value of Catalog::LookupEntry
297struct CatalogEntryLookup {
298 optional_ptr<SchemaCatalogEntry> schema;
299 optional_ptr<CatalogEntry> entry;
300
301 DUCKDB_API bool Found() const {
302 return entry;
303 }
304};
305
306//===--------------------------------------------------------------------===//
307// Generic
308//===--------------------------------------------------------------------===//
309void Catalog::DropEntry(ClientContext &context, DropInfo &info) {
310 ModifyCatalog();
311 if (info.type == CatalogType::SCHEMA_ENTRY) {
312 // DROP SCHEMA
313 DropSchema(context, info);
314 return;
315 }
316
317 auto lookup = LookupEntry(context, type: info.type, schema: info.schema, name: info.name, if_not_found: info.if_not_found);
318 if (!lookup.Found()) {
319 return;
320 }
321
322 lookup.schema->DropEntry(context, info);
323}
324
325SchemaCatalogEntry &Catalog::GetSchema(ClientContext &context, const string &name, QueryErrorContext error_context) {
326 return *Catalog::GetSchema(context, name, if_not_found: OnEntryNotFound::THROW_EXCEPTION, error_context);
327}
328
329optional_ptr<SchemaCatalogEntry> Catalog::GetSchema(ClientContext &context, const string &schema_name,
330 OnEntryNotFound if_not_found, QueryErrorContext error_context) {
331 return GetSchema(transaction: GetCatalogTransaction(context), schema_name, if_not_found, error_context);
332}
333
334SchemaCatalogEntry &Catalog::GetSchema(ClientContext &context, const string &catalog_name, const string &schema_name,
335 QueryErrorContext error_context) {
336 return *Catalog::GetSchema(context, catalog_name, schema_name, if_not_found: OnEntryNotFound::THROW_EXCEPTION, error_context);
337}
338
339SchemaCatalogEntry &Catalog::GetSchema(CatalogTransaction transaction, const string &name,
340 QueryErrorContext error_context) {
341 return *GetSchema(transaction, schema_name: name, if_not_found: OnEntryNotFound::THROW_EXCEPTION, error_context);
342}
343
344//===--------------------------------------------------------------------===//
345// Lookup
346//===--------------------------------------------------------------------===//
347SimilarCatalogEntry Catalog::SimilarEntryInSchemas(ClientContext &context, const string &entry_name, CatalogType type,
348 const reference_set_t<SchemaCatalogEntry> &schemas) {
349 SimilarCatalogEntry result;
350 for (auto schema_ref : schemas) {
351 auto &schema = schema_ref.get();
352 auto transaction = schema.catalog.GetCatalogTransaction(context);
353 auto entry = schema.GetSimilarEntry(transaction, type, name: entry_name);
354 if (!entry.Found()) {
355 // no similar entry found
356 continue;
357 }
358 if (!result.Found() || result.distance > entry.distance) {
359 result = entry;
360 result.schema = &schema;
361 }
362 }
363 return result;
364}
365
366string FindExtensionGeneric(const string &name, const ExtensionEntry entries[], idx_t size) {
367 auto lcase = StringUtil::Lower(str: name);
368 auto it = std::lower_bound(first: entries, last: entries + size, val: lcase,
369 comp: [](const ExtensionEntry &element, const string &value) { return element.name < value; });
370 if (it != entries + size && it->name == lcase) {
371 return it->extension;
372 }
373 return "";
374}
375
376string FindExtensionForFunction(const string &name) {
377 idx_t size = sizeof(EXTENSION_FUNCTIONS) / sizeof(ExtensionEntry);
378 return FindExtensionGeneric(name, entries: EXTENSION_FUNCTIONS, size);
379}
380
381string FindExtensionForSetting(const string &name) {
382 idx_t size = sizeof(EXTENSION_SETTINGS) / sizeof(ExtensionEntry);
383 return FindExtensionGeneric(name, entries: EXTENSION_SETTINGS, size);
384}
385
386vector<CatalogSearchEntry> GetCatalogEntries(ClientContext &context, const string &catalog, const string &schema) {
387 vector<CatalogSearchEntry> entries;
388 auto &search_path = *context.client_data->catalog_search_path;
389 if (IsInvalidCatalog(str: catalog) && IsInvalidSchema(str: schema)) {
390 // no catalog or schema provided - scan the entire search path
391 entries = search_path.Get();
392 } else if (IsInvalidCatalog(str: catalog)) {
393 auto catalogs = search_path.GetCatalogsForSchema(schema);
394 for (auto &catalog_name : catalogs) {
395 entries.emplace_back(args&: catalog_name, args: schema);
396 }
397 if (entries.empty()) {
398 entries.emplace_back(args: DatabaseManager::GetDefaultDatabase(context), args: schema);
399 }
400 } else if (IsInvalidSchema(str: schema)) {
401 auto schemas = search_path.GetSchemasForCatalog(catalog);
402 for (auto &schema_name : schemas) {
403 entries.emplace_back(args: catalog, args&: schema_name);
404 }
405 if (entries.empty()) {
406 entries.emplace_back(args: catalog, DEFAULT_SCHEMA);
407 }
408 } else {
409 // specific catalog and schema provided
410 entries.emplace_back(args: catalog, args: schema);
411 }
412 return entries;
413}
414
415void FindMinimalQualification(ClientContext &context, const string &catalog_name, const string &schema_name,
416 bool &qualify_database, bool &qualify_schema) {
417 // check if we can we qualify ONLY the schema
418 bool found = false;
419 auto entries = GetCatalogEntries(context, INVALID_CATALOG, schema: schema_name);
420 for (auto &entry : entries) {
421 if (entry.catalog == catalog_name && entry.schema == schema_name) {
422 found = true;
423 break;
424 }
425 }
426 if (found) {
427 qualify_database = false;
428 qualify_schema = true;
429 return;
430 }
431 // check if we can qualify ONLY the catalog
432 found = false;
433 entries = GetCatalogEntries(context, catalog: catalog_name, INVALID_SCHEMA);
434 for (auto &entry : entries) {
435 if (entry.catalog == catalog_name && entry.schema == schema_name) {
436 found = true;
437 break;
438 }
439 }
440 if (found) {
441 qualify_database = true;
442 qualify_schema = false;
443 return;
444 }
445 // need to qualify both catalog and schema
446 qualify_database = true;
447 qualify_schema = true;
448}
449
450CatalogException Catalog::UnrecognizedConfigurationError(ClientContext &context, const string &name) {
451 // check if the setting exists in any extensions
452 auto extension_name = FindExtensionForSetting(name);
453 if (!extension_name.empty()) {
454 return CatalogException(
455 "Setting with name \"%s\" is not in the catalog, but it exists in the %s extension.\n\nTo "
456 "install and load the extension, run:\nINSTALL %s;\nLOAD %s;",
457 name, extension_name, extension_name, extension_name);
458 }
459 // the setting is not in an extension
460 // get a list of all options
461 vector<string> potential_names = DBConfig::GetOptionNames();
462 for (auto &entry : DBConfig::GetConfig(context).extension_parameters) {
463 potential_names.push_back(x: entry.first);
464 }
465
466 throw CatalogException("unrecognized configuration parameter \"%s\"\n%s", name,
467 StringUtil::CandidatesErrorMessage(strings: potential_names, target: name, message_prefix: "Did you mean"));
468}
469
470CatalogException Catalog::CreateMissingEntryException(ClientContext &context, const string &entry_name,
471 CatalogType type,
472 const reference_set_t<SchemaCatalogEntry> &schemas,
473 QueryErrorContext error_context) {
474 auto entry = SimilarEntryInSchemas(context, entry_name, type, schemas);
475
476 reference_set_t<SchemaCatalogEntry> unseen_schemas;
477 auto &db_manager = DatabaseManager::Get(db&: context);
478 auto databases = db_manager.GetDatabases(context);
479 for (auto database : databases) {
480 auto &catalog = database.get().GetCatalog();
481 auto current_schemas = catalog.GetAllSchemas(context);
482 for (auto &current_schema : current_schemas) {
483 unseen_schemas.insert(x: current_schema.get());
484 }
485 }
486 // check if the entry exists in any extension
487 if (type == CatalogType::TABLE_FUNCTION_ENTRY || type == CatalogType::SCALAR_FUNCTION_ENTRY ||
488 type == CatalogType::AGGREGATE_FUNCTION_ENTRY) {
489 auto extension_name = FindExtensionForFunction(name: entry_name);
490 if (!extension_name.empty()) {
491 return CatalogException(
492 "Function with name \"%s\" is not in the catalog, but it exists in the %s extension.\n\nTo "
493 "install and load the extension, run:\nINSTALL %s;\nLOAD %s;",
494 entry_name, extension_name, extension_name, extension_name);
495 }
496 }
497 auto unseen_entry = SimilarEntryInSchemas(context, entry_name, type, schemas: unseen_schemas);
498 string did_you_mean;
499 if (unseen_entry.Found() && unseen_entry.distance < entry.distance) {
500 // the closest matching entry requires qualification as it is not in the default search path
501 // check how to minimally qualify this entry
502 auto catalog_name = unseen_entry.schema->catalog.GetName();
503 auto schema_name = unseen_entry.schema->name;
504 bool qualify_database;
505 bool qualify_schema;
506 FindMinimalQualification(context, catalog_name, schema_name, qualify_database, qualify_schema);
507 did_you_mean = "\nDid you mean \"" + unseen_entry.GetQualifiedName(qualify_catalog: qualify_database, qualify_schema) + "\"?";
508 } else if (entry.Found()) {
509 did_you_mean = "\nDid you mean \"" + entry.name + "\"?";
510 }
511
512 return CatalogException(error_context.FormatError(msg: "%s with name %s does not exist!%s", params: CatalogTypeToString(type),
513 params: entry_name, params: did_you_mean));
514}
515
516CatalogEntryLookup Catalog::LookupEntryInternal(CatalogTransaction transaction, CatalogType type, const string &schema,
517 const string &name) {
518 auto schema_entry = GetSchema(transaction, schema_name: schema, if_not_found: OnEntryNotFound::RETURN_NULL);
519 if (!schema_entry) {
520 return {.schema: nullptr, .entry: nullptr};
521 }
522 auto entry = schema_entry->GetEntry(transaction, type, name);
523 if (!entry) {
524 return {.schema: schema_entry, .entry: nullptr};
525 }
526 return {.schema: schema_entry, .entry: entry};
527}
528
529CatalogEntryLookup Catalog::LookupEntry(ClientContext &context, CatalogType type, const string &schema,
530 const string &name, OnEntryNotFound if_not_found,
531 QueryErrorContext error_context) {
532 reference_set_t<SchemaCatalogEntry> schemas;
533 if (IsInvalidSchema(str: schema)) {
534 // try all schemas for this catalog
535 auto entries = GetCatalogEntries(context, catalog: GetName(), INVALID_SCHEMA);
536 for (auto &entry : entries) {
537 auto &candidate_schema = entry.schema;
538 auto transaction = GetCatalogTransaction(context);
539 auto result = LookupEntryInternal(transaction, type, schema: candidate_schema, name);
540 if (result.Found()) {
541 return result;
542 }
543 if (result.schema) {
544 schemas.insert(x: *result.schema);
545 }
546 }
547 } else {
548 auto transaction = GetCatalogTransaction(context);
549 auto result = LookupEntryInternal(transaction, type, schema, name);
550 if (result.Found()) {
551 return result;
552 }
553 if (result.schema) {
554 schemas.insert(x: *result.schema);
555 }
556 }
557 if (if_not_found == OnEntryNotFound::RETURN_NULL) {
558 return {.schema: nullptr, .entry: nullptr};
559 }
560 throw CreateMissingEntryException(context, entry_name: name, type, schemas, error_context);
561}
562
563CatalogEntryLookup Catalog::LookupEntry(ClientContext &context, vector<CatalogLookup> &lookups, CatalogType type,
564 const string &name, OnEntryNotFound if_not_found,
565 QueryErrorContext error_context) {
566 reference_set_t<SchemaCatalogEntry> schemas;
567 for (auto &lookup : lookups) {
568 auto transaction = lookup.catalog.GetCatalogTransaction(context);
569 auto result = lookup.catalog.LookupEntryInternal(transaction, type, schema: lookup.schema, name);
570 if (result.Found()) {
571 return result;
572 }
573 if (result.schema) {
574 schemas.insert(x: *result.schema);
575 }
576 }
577 if (if_not_found == OnEntryNotFound::RETURN_NULL) {
578 return {.schema: nullptr, .entry: nullptr};
579 }
580 throw CreateMissingEntryException(context, entry_name: name, type, schemas, error_context);
581}
582
583CatalogEntry &Catalog::GetEntry(ClientContext &context, const string &schema, const string &name) {
584 vector<CatalogType> entry_types {CatalogType::TABLE_ENTRY, CatalogType::SEQUENCE_ENTRY};
585
586 for (auto entry_type : entry_types) {
587 auto result = GetEntry(context, type: entry_type, schema, name, if_not_found: OnEntryNotFound::RETURN_NULL);
588 if (result) {
589 return *result;
590 }
591 }
592
593 throw CatalogException("CatalogElement \"%s.%s\" does not exist!", schema, name);
594}
595
596optional_ptr<CatalogEntry> Catalog::GetEntry(ClientContext &context, CatalogType type, const string &schema_name,
597 const string &name, OnEntryNotFound if_not_found,
598 QueryErrorContext error_context) {
599 return LookupEntry(context, type, schema: schema_name, name, if_not_found, error_context).entry.get();
600}
601
602CatalogEntry &Catalog::GetEntry(ClientContext &context, CatalogType type, const string &schema, const string &name,
603 QueryErrorContext error_context) {
604 return *Catalog::GetEntry(context, type, schema_name: schema, name, if_not_found: OnEntryNotFound::THROW_EXCEPTION, error_context);
605}
606
607optional_ptr<CatalogEntry> Catalog::GetEntry(ClientContext &context, CatalogType type, const string &catalog,
608 const string &schema, const string &name, OnEntryNotFound if_not_found,
609 QueryErrorContext error_context) {
610 auto entries = GetCatalogEntries(context, catalog, schema);
611 vector<CatalogLookup> lookups;
612 lookups.reserve(n: entries.size());
613 for (auto &entry : entries) {
614 if (if_not_found == OnEntryNotFound::RETURN_NULL) {
615 auto catalog_entry = Catalog::GetCatalogEntry(context, catalog_name: entry.catalog);
616 if (!catalog_entry) {
617 return nullptr;
618 }
619 lookups.emplace_back(args&: *catalog_entry, args&: entry.schema);
620 } else {
621 lookups.emplace_back(args&: Catalog::GetCatalog(context, catalog_name: entry.catalog), args&: entry.schema);
622 }
623 }
624 auto result = LookupEntry(context, lookups, type, name, if_not_found, error_context);
625 if (!result.Found()) {
626 D_ASSERT(if_not_found == OnEntryNotFound::RETURN_NULL);
627 return nullptr;
628 }
629 return result.entry.get();
630}
631
632CatalogEntry &Catalog::GetEntry(ClientContext &context, CatalogType type, const string &catalog, const string &schema,
633 const string &name, QueryErrorContext error_context) {
634 return *Catalog::GetEntry(context, type, catalog, schema, name, if_not_found: OnEntryNotFound::THROW_EXCEPTION, error_context);
635}
636
637optional_ptr<SchemaCatalogEntry> Catalog::GetSchema(ClientContext &context, const string &catalog_name,
638 const string &schema_name, OnEntryNotFound if_not_found,
639 QueryErrorContext error_context) {
640 auto entries = GetCatalogEntries(context, catalog: catalog_name, schema: schema_name);
641 for (idx_t i = 0; i < entries.size(); i++) {
642 auto on_not_found = i + 1 == entries.size() ? if_not_found : OnEntryNotFound::RETURN_NULL;
643 auto &catalog = Catalog::GetCatalog(context, catalog_name: entries[i].catalog);
644 auto result = catalog.GetSchema(context, schema_name, if_not_found: on_not_found, error_context);
645 if (result) {
646 return result;
647 }
648 }
649 return nullptr;
650}
651
652LogicalType Catalog::GetType(ClientContext &context, const string &schema, const string &name,
653 OnEntryNotFound if_not_found) {
654 auto type_entry = GetEntry<TypeCatalogEntry>(context, schema_name: schema, name, if_not_found);
655 if (!type_entry) {
656 return LogicalType::INVALID;
657 }
658 auto result_type = type_entry->user_type;
659 EnumType::SetCatalog(type&: result_type, catalog_entry: type_entry.get());
660 return result_type;
661}
662
663LogicalType Catalog::GetType(ClientContext &context, const string &catalog_name, const string &schema,
664 const string &name) {
665 auto &type_entry = Catalog::GetEntry<TypeCatalogEntry>(context, catalog_name, schema_name: schema, name);
666 auto result_type = type_entry.user_type;
667 EnumType::SetCatalog(type&: result_type, catalog_entry: &type_entry);
668 return result_type;
669}
670
671vector<reference<SchemaCatalogEntry>> Catalog::GetSchemas(ClientContext &context) {
672 vector<reference<SchemaCatalogEntry>> schemas;
673 ScanSchemas(context, callback: [&](SchemaCatalogEntry &entry) { schemas.push_back(x: entry); });
674 return schemas;
675}
676
677vector<reference<SchemaCatalogEntry>> Catalog::GetSchemas(ClientContext &context, const string &catalog_name) {
678 vector<reference<Catalog>> catalogs;
679 if (IsInvalidCatalog(str: catalog_name)) {
680 reference_set_t<Catalog> inserted_catalogs;
681
682 auto &search_path = *context.client_data->catalog_search_path;
683 for (auto &entry : search_path.Get()) {
684 auto &catalog = Catalog::GetCatalog(context, catalog_name: entry.catalog);
685 if (inserted_catalogs.find(x: catalog) != inserted_catalogs.end()) {
686 continue;
687 }
688 inserted_catalogs.insert(x: catalog);
689 catalogs.push_back(x: catalog);
690 }
691 } else {
692 catalogs.push_back(x: Catalog::GetCatalog(context, catalog_name));
693 }
694 vector<reference<SchemaCatalogEntry>> result;
695 for (auto catalog : catalogs) {
696 auto schemas = catalog.get().GetSchemas(context);
697 result.insert(position: result.end(), first: schemas.begin(), last: schemas.end());
698 }
699 return result;
700}
701
702vector<reference<SchemaCatalogEntry>> Catalog::GetAllSchemas(ClientContext &context) {
703 vector<reference<SchemaCatalogEntry>> result;
704
705 auto &db_manager = DatabaseManager::Get(db&: context);
706 auto databases = db_manager.GetDatabases(context);
707 for (auto database : databases) {
708 auto &catalog = database.get().GetCatalog();
709 auto new_schemas = catalog.GetSchemas(context);
710 result.insert(position: result.end(), first: new_schemas.begin(), last: new_schemas.end());
711 }
712 sort(first: result.begin(), last: result.end(),
713 comp: [&](reference<SchemaCatalogEntry> left_p, reference<SchemaCatalogEntry> right_p) {
714 auto &left = left_p.get();
715 auto &right = right_p.get();
716 if (left.catalog.GetName() < right.catalog.GetName()) {
717 return true;
718 }
719 if (left.catalog.GetName() == right.catalog.GetName()) {
720 return left.name < right.name;
721 }
722 return false;
723 });
724
725 return result;
726}
727
728void Catalog::Alter(ClientContext &context, AlterInfo &info) {
729 ModifyCatalog();
730 auto lookup = LookupEntry(context, type: info.GetCatalogType(), schema: info.schema, name: info.name, if_not_found: info.if_not_found);
731 if (!lookup.Found()) {
732 return;
733 }
734 return lookup.schema->Alter(context, info);
735}
736
737void Catalog::Verify() {
738}
739
740//===--------------------------------------------------------------------===//
741// Catalog Version
742//===--------------------------------------------------------------------===//
743idx_t Catalog::GetCatalogVersion() {
744 return GetDatabase().GetDatabaseManager().catalog_version;
745}
746
747idx_t Catalog::ModifyCatalog() {
748 return GetDatabase().GetDatabaseManager().ModifyCatalog();
749}
750
751bool Catalog::IsSystemCatalog() const {
752 return db.IsSystem();
753}
754
755bool Catalog::IsTemporaryCatalog() const {
756 return db.IsTemporary();
757}
758
759} // namespace duckdb
760