1#include "duckdb/catalog/catalog_set.hpp"
2
3#include "duckdb/catalog/duck_catalog.hpp"
4#include "duckdb/common/exception.hpp"
5#include "duckdb/transaction/transaction_manager.hpp"
6#include "duckdb/transaction/duck_transaction.hpp"
7#include "duckdb/common/serializer/buffered_serializer.hpp"
8#include "duckdb/parser/parsed_data/alter_table_info.hpp"
9#include "duckdb/catalog/dependency_manager.hpp"
10#include "duckdb/common/string_util.hpp"
11#include "duckdb/parser/column_definition.hpp"
12#include "duckdb/parser/expression/constant_expression.hpp"
13#include "duckdb/catalog/mapping_value.hpp"
14#include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp"
15#include "duckdb/catalog/catalog_entry/type_catalog_entry.hpp"
16
17namespace duckdb {
18
19//! Class responsible to keep track of state when removing entries from the catalog.
20//! When deleting, many types of errors can be thrown, since we want to avoid try/catch blocks
21//! this class makes sure that whatever elements were modified are returned to a correct state
22//! when exceptions are thrown.
23//! The idea here is to use RAII (Resource acquisition is initialization) to mimic a try/catch/finally block.
24//! If any exception is raised when this object exists, then its destructor will be called
25//! and the entry will return to its previous state during deconstruction.
26class EntryDropper {
27public:
28 //! Both constructor and destructor are privates because they should only be called by DropEntryDependencies
29 explicit EntryDropper(EntryIndex &entry_index_p) : entry_index(entry_index_p) {
30 old_deleted = entry_index.GetEntry()->deleted;
31 }
32
33 ~EntryDropper() {
34 entry_index.GetEntry()->deleted = old_deleted;
35 }
36
37private:
38 //! Keeps track of the state of the entry before starting the delete
39 bool old_deleted;
40 //! Index of entry to be deleted
41 EntryIndex &entry_index;
42};
43
44CatalogSet::CatalogSet(Catalog &catalog_p, unique_ptr<DefaultGenerator> defaults)
45 : catalog(catalog_p.Cast<DuckCatalog>()), defaults(std::move(defaults)) {
46 D_ASSERT(catalog_p.IsDuckCatalog());
47}
48CatalogSet::~CatalogSet() {
49}
50
51EntryIndex CatalogSet::PutEntry(idx_t entry_index, unique_ptr<CatalogEntry> entry) {
52 if (entries.find(x: entry_index) != entries.end()) {
53 throw InternalException("Entry with entry index \"%llu\" already exists", entry_index);
54 }
55 entries.insert(x: make_pair(x&: entry_index, y: EntryValue(std::move(entry))));
56 return EntryIndex(*this, entry_index);
57}
58
59void CatalogSet::PutEntry(EntryIndex index, unique_ptr<CatalogEntry> catalog_entry) {
60 auto entry = entries.find(x: index.GetIndex());
61 if (entry == entries.end()) {
62 throw InternalException("Entry with entry index \"%llu\" does not exist", index.GetIndex());
63 }
64 catalog_entry->child = std::move(entry->second.entry);
65 catalog_entry->child->parent = catalog_entry.get();
66 entry->second.entry = std::move(catalog_entry);
67}
68
69bool CatalogSet::CreateEntry(CatalogTransaction transaction, const string &name, unique_ptr<CatalogEntry> value,
70 DependencyList &dependencies) {
71 if (value->internal && !catalog.IsSystemCatalog() && name != DEFAULT_SCHEMA) {
72 throw InternalException("Attempting to create internal entry \"%s\" in non-system catalog - internal entries "
73 "can only be created in the system catalog",
74 name);
75 }
76 if (!value->internal) {
77 if (!value->temporary && catalog.IsSystemCatalog()) {
78 throw InternalException(
79 "Attempting to create non-internal entry \"%s\" in system catalog - the system catalog "
80 "can only contain internal entries",
81 name);
82 }
83 if (value->temporary && !catalog.IsTemporaryCatalog()) {
84 throw InternalException("Attempting to create temporary entry \"%s\" in non-temporary catalog", name);
85 }
86 if (!value->temporary && catalog.IsTemporaryCatalog() && name != DEFAULT_SCHEMA) {
87 throw InvalidInputException("Cannot create non-temporary entry \"%s\" in temporary catalog", name);
88 }
89 }
90 // lock the catalog for writing
91 lock_guard<mutex> write_lock(catalog.GetWriteLock());
92 // lock this catalog set to disallow reading
93 unique_lock<mutex> read_lock(catalog_lock);
94
95 // first check if the entry exists in the unordered set
96 idx_t index;
97 auto mapping_value = GetMapping(transaction, name);
98 if (mapping_value == nullptr || mapping_value->deleted) {
99 // if it does not: entry has never been created
100
101 // check if there is a default entry
102 auto entry = CreateDefaultEntry(transaction, name, lock&: read_lock);
103 if (entry) {
104 return false;
105 }
106
107 // first create a dummy deleted entry for this entry
108 // so transactions started before the commit of this transaction don't
109 // see it yet
110 auto dummy_node = make_uniq<InCatalogEntry>(args: CatalogType::INVALID, args&: value->ParentCatalog(), args: name);
111 dummy_node->timestamp = 0;
112 dummy_node->deleted = true;
113 dummy_node->set = this;
114
115 auto entry_index = PutEntry(entry_index: current_entry++, entry: std::move(dummy_node));
116 index = entry_index.GetIndex();
117 PutMapping(transaction, name, entry_index: std::move(entry_index));
118 } else {
119 index = mapping_value->index.GetIndex();
120 auto &current = *mapping_value->index.GetEntry();
121 // if it does, we have to check version numbers
122 if (HasConflict(transaction, timestamp: current.timestamp)) {
123 // current version has been written to by a currently active
124 // transaction
125 throw TransactionException("Catalog write-write conflict on create with \"%s\"", current.name);
126 }
127 // there is a current version that has been committed
128 // if it has not been deleted there is a conflict
129 if (!current.deleted) {
130 return false;
131 }
132 }
133 // create a new entry and replace the currently stored one
134 // set the timestamp to the timestamp of the current transaction
135 // and point it at the dummy node
136 value->timestamp = transaction.transaction_id;
137 value->set = this;
138
139 // now add the dependency set of this object to the dependency manager
140 catalog.GetDependencyManager().AddObject(transaction, object&: *value, dependencies);
141
142 auto value_ptr = value.get();
143 EntryIndex entry_index(*this, index);
144 PutEntry(index: std::move(entry_index), catalog_entry: std::move(value));
145 // push the old entry in the undo buffer for this transaction
146 if (transaction.transaction) {
147 auto &dtransaction = transaction.transaction->Cast<DuckTransaction>();
148 dtransaction.PushCatalogEntry(entry&: *value_ptr->child);
149 }
150 return true;
151}
152
153bool CatalogSet::CreateEntry(ClientContext &context, const string &name, unique_ptr<CatalogEntry> value,
154 DependencyList &dependencies) {
155 return CreateEntry(transaction: catalog.GetCatalogTransaction(context), name, value: std::move(value), dependencies);
156}
157
158optional_ptr<CatalogEntry> CatalogSet::GetEntryInternal(CatalogTransaction transaction, EntryIndex &entry_index) {
159 auto &catalog_entry = *entry_index.GetEntry();
160 // if it does: we have to retrieve the entry and to check version numbers
161 if (HasConflict(transaction, timestamp: catalog_entry.timestamp)) {
162 // current version has been written to by a currently active
163 // transaction
164 throw TransactionException("Catalog write-write conflict on alter with \"%s\"", catalog_entry.name);
165 }
166 // there is a current version that has been committed by this transaction
167 if (catalog_entry.deleted) {
168 // if the entry was already deleted, it now does not exist anymore
169 // so we return that we could not find it
170 return nullptr;
171 }
172 return &catalog_entry;
173}
174
175optional_ptr<CatalogEntry> CatalogSet::GetEntryInternal(CatalogTransaction transaction, const string &name,
176 EntryIndex *entry_index) {
177 auto mapping_value = GetMapping(transaction, name);
178 if (mapping_value == nullptr || mapping_value->deleted) {
179 // the entry does not exist, check if we can create a default entry
180 return nullptr;
181 }
182 if (entry_index) {
183 *entry_index = mapping_value->index.Copy();
184 }
185 return GetEntryInternal(transaction, entry_index&: mapping_value->index);
186}
187
188bool CatalogSet::AlterOwnership(CatalogTransaction transaction, ChangeOwnershipInfo &info) {
189 auto entry = GetEntryInternal(transaction, name: info.name, entry_index: nullptr);
190 if (!entry) {
191 return false;
192 }
193
194 auto &owner_entry = catalog.GetEntry(context&: transaction.GetContext(), schema: info.owner_schema, name: info.owner_name);
195 catalog.GetDependencyManager().AddOwnership(transaction, owner&: owner_entry, entry&: *entry);
196 return true;
197}
198
199bool CatalogSet::AlterEntry(CatalogTransaction transaction, const string &name, AlterInfo &alter_info) {
200 // lock the catalog for writing
201 lock_guard<mutex> write_lock(catalog.GetWriteLock());
202
203 // first check if the entry exists in the unordered set
204 EntryIndex entry_index;
205 auto entry = GetEntryInternal(transaction, name, entry_index: &entry_index);
206 if (!entry) {
207 return false;
208 }
209 if (!alter_info.allow_internal && entry->internal) {
210 throw CatalogException("Cannot alter entry \"%s\" because it is an internal system entry", entry->name);
211 }
212
213 // lock this catalog set to disallow reading
214 lock_guard<mutex> read_lock(catalog_lock);
215
216 // create a new entry and replace the currently stored one
217 // set the timestamp to the timestamp of the current transaction
218 // and point it to the updated table node
219 string original_name = entry->name;
220 if (!transaction.context) {
221 throw InternalException("Cannot AlterEntry without client context");
222 }
223 auto &context = *transaction.context;
224 auto value = entry->AlterEntry(context, info&: alter_info);
225 if (!value) {
226 // alter failed, but did not result in an error
227 return true;
228 }
229
230 if (value->name != original_name) {
231 auto mapping_value = GetMapping(transaction, name: value->name);
232 if (mapping_value && !mapping_value->deleted) {
233 auto &original_entry = GetEntryForTransaction(transaction, current&: *mapping_value->index.GetEntry());
234 if (!original_entry.deleted) {
235 entry->UndoAlter(context, info&: alter_info);
236 string rename_err_msg =
237 "Could not rename \"%s\" to \"%s\": another entry with this name already exists!";
238 throw CatalogException(rename_err_msg, original_name, value->name);
239 }
240 }
241 }
242
243 if (value->name != original_name) {
244 // Do PutMapping and DeleteMapping after dependency check
245 PutMapping(transaction, name: value->name, entry_index: entry_index.Copy());
246 DeleteMapping(transaction, name: original_name);
247 }
248
249 value->timestamp = transaction.transaction_id;
250 value->set = this;
251 auto new_entry = value.get();
252 PutEntry(index: std::move(entry_index), catalog_entry: std::move(value));
253
254 // serialize the AlterInfo into a temporary buffer
255 BufferedSerializer serializer;
256 serializer.WriteString(val: alter_info.GetColumnName());
257 alter_info.Serialize(serializer);
258 BinaryData serialized_alter = serializer.GetData();
259
260 // push the old entry in the undo buffer for this transaction
261 if (transaction.transaction) {
262 auto &dtransaction = transaction.transaction->Cast<DuckTransaction>();
263 dtransaction.PushCatalogEntry(entry&: *new_entry->child, extra_data: serialized_alter.data.get(), extra_data_size: serialized_alter.size);
264 }
265
266 // Check the dependency manager to verify that there are no conflicting dependencies with this alter
267 // Note that we do this AFTER the new entry has been entirely set up in the catalog set
268 // that is because in case the alter fails because of a dependency conflict, we need to be able to cleanly roll back
269 // to the old entry.
270 catalog.GetDependencyManager().AlterObject(transaction, old_obj&: *entry, new_obj&: *new_entry);
271
272 return true;
273}
274
275void CatalogSet::DropEntryDependencies(CatalogTransaction transaction, EntryIndex &entry_index, CatalogEntry &entry,
276 bool cascade) {
277 // Stores the deleted value of the entry before starting the process
278 EntryDropper dropper(entry_index);
279
280 // To correctly delete the object and its dependencies, it temporarily is set to deleted.
281 entry_index.GetEntry()->deleted = true;
282
283 // check any dependencies of this object
284 D_ASSERT(entry.ParentCatalog().IsDuckCatalog());
285 auto &duck_catalog = entry.ParentCatalog().Cast<DuckCatalog>();
286 duck_catalog.GetDependencyManager().DropObject(transaction, object&: entry, cascade);
287
288 // dropper destructor is called here
289 // the destructor makes sure to return the value to the previous state
290 // dropper.~EntryDropper()
291}
292
293void CatalogSet::DropEntryInternal(CatalogTransaction transaction, EntryIndex entry_index, CatalogEntry &entry,
294 bool cascade) {
295 DropEntryDependencies(transaction, entry_index, entry, cascade);
296
297 // create a new entry and replace the currently stored one
298 // set the timestamp to the timestamp of the current transaction
299 // and point it at the dummy node
300 auto value = make_uniq<InCatalogEntry>(args: CatalogType::DELETED_ENTRY, args&: entry.ParentCatalog(), args&: entry.name);
301 value->timestamp = transaction.transaction_id;
302 value->set = this;
303 value->deleted = true;
304 auto value_ptr = value.get();
305 PutEntry(index: std::move(entry_index), catalog_entry: std::move(value));
306
307 // push the old entry in the undo buffer for this transaction
308 if (transaction.transaction) {
309 auto &dtransaction = transaction.transaction->Cast<DuckTransaction>();
310 dtransaction.PushCatalogEntry(entry&: *value_ptr->child);
311 }
312}
313
314bool CatalogSet::DropEntry(CatalogTransaction transaction, const string &name, bool cascade, bool allow_drop_internal) {
315 // lock the catalog for writing
316 lock_guard<mutex> write_lock(catalog.GetWriteLock());
317 // we can only delete an entry that exists
318 EntryIndex entry_index;
319 auto entry = GetEntryInternal(transaction, name, entry_index: &entry_index);
320 if (!entry) {
321 return false;
322 }
323 if (entry->internal && !allow_drop_internal) {
324 throw CatalogException("Cannot drop entry \"%s\" because it is an internal system entry", entry->name);
325 }
326
327 lock_guard<mutex> read_lock(catalog_lock);
328 DropEntryInternal(transaction, entry_index: std::move(entry_index), entry&: *entry, cascade);
329 return true;
330}
331
332bool CatalogSet::DropEntry(ClientContext &context, const string &name, bool cascade, bool allow_drop_internal) {
333 return DropEntry(transaction: catalog.GetCatalogTransaction(context), name, cascade, allow_drop_internal);
334}
335
336DuckCatalog &CatalogSet::GetCatalog() {
337 return catalog;
338}
339
340void CatalogSet::CleanupEntry(CatalogEntry &catalog_entry) {
341 // destroy the backed up entry: it is no longer required
342 D_ASSERT(catalog_entry.parent);
343 if (catalog_entry.parent->type != CatalogType::UPDATED_ENTRY) {
344 lock_guard<mutex> write_lock(catalog.GetWriteLock());
345 lock_guard<mutex> lock(catalog_lock);
346 if (!catalog_entry.deleted) {
347 // delete the entry from the dependency manager, if it is not deleted yet
348 D_ASSERT(catalog_entry.ParentCatalog().IsDuckCatalog());
349 catalog_entry.ParentCatalog().Cast<DuckCatalog>().GetDependencyManager().EraseObject(object&: catalog_entry);
350 }
351 auto parent = catalog_entry.parent;
352 parent->child = std::move(catalog_entry.child);
353 if (parent->deleted && !parent->child && !parent->parent) {
354 auto mapping_entry = mapping.find(x: parent->name);
355 D_ASSERT(mapping_entry != mapping.end());
356 auto &entry = mapping_entry->second->index.GetEntry();
357 D_ASSERT(entry);
358 if (entry.get() == parent.get()) {
359 mapping.erase(position: mapping_entry);
360 }
361 }
362 }
363}
364
365bool CatalogSet::HasConflict(CatalogTransaction transaction, transaction_t timestamp) {
366 return (timestamp >= TRANSACTION_ID_START && timestamp != transaction.transaction_id) ||
367 (timestamp < TRANSACTION_ID_START && timestamp > transaction.start_time);
368}
369
370optional_ptr<MappingValue> CatalogSet::GetMapping(CatalogTransaction transaction, const string &name, bool get_latest) {
371 optional_ptr<MappingValue> mapping_value;
372 auto entry = mapping.find(x: name);
373 if (entry != mapping.end()) {
374 mapping_value = entry->second.get();
375 } else {
376
377 return nullptr;
378 }
379 if (get_latest) {
380 return mapping_value;
381 }
382 while (mapping_value->child) {
383 if (UseTimestamp(transaction, timestamp: mapping_value->timestamp)) {
384 break;
385 }
386 mapping_value = mapping_value->child.get();
387 D_ASSERT(mapping_value);
388 }
389 return mapping_value;
390}
391
392void CatalogSet::PutMapping(CatalogTransaction transaction, const string &name, EntryIndex entry_index) {
393 auto entry = mapping.find(x: name);
394 auto new_value = make_uniq<MappingValue>(args: std::move(entry_index));
395 new_value->timestamp = transaction.transaction_id;
396 if (entry != mapping.end()) {
397 if (HasConflict(transaction, timestamp: entry->second->timestamp)) {
398 throw TransactionException("Catalog write-write conflict on name \"%s\"", name);
399 }
400 new_value->child = std::move(entry->second);
401 new_value->child->parent = new_value.get();
402 }
403 mapping[name] = std::move(new_value);
404}
405
406void CatalogSet::DeleteMapping(CatalogTransaction transaction, const string &name) {
407 auto entry = mapping.find(x: name);
408 D_ASSERT(entry != mapping.end());
409 auto delete_marker = make_uniq<MappingValue>(args: entry->second->index.Copy());
410 delete_marker->deleted = true;
411 delete_marker->timestamp = transaction.transaction_id;
412 delete_marker->child = std::move(entry->second);
413 delete_marker->child->parent = delete_marker.get();
414 mapping[name] = std::move(delete_marker);
415}
416
417bool CatalogSet::UseTimestamp(CatalogTransaction transaction, transaction_t timestamp) {
418 if (timestamp == transaction.transaction_id) {
419 // we created this version
420 return true;
421 }
422 if (timestamp < transaction.start_time) {
423 // this version was commited before we started the transaction
424 return true;
425 }
426 return false;
427}
428
429CatalogEntry &CatalogSet::GetEntryForTransaction(CatalogTransaction transaction, CatalogEntry &current) {
430 reference<CatalogEntry> entry(current);
431 while (entry.get().child) {
432 if (UseTimestamp(transaction, timestamp: entry.get().timestamp)) {
433 break;
434 }
435 entry = *entry.get().child;
436 }
437 return entry.get();
438}
439
440CatalogEntry &CatalogSet::GetCommittedEntry(CatalogEntry &current) {
441 reference<CatalogEntry> entry(current);
442 while (entry.get().child) {
443 if (entry.get().timestamp < TRANSACTION_ID_START) {
444 // this entry is committed: use it
445 break;
446 }
447 entry = *entry.get().child;
448 }
449 return entry.get();
450}
451
452SimilarCatalogEntry CatalogSet::SimilarEntry(CatalogTransaction transaction, const string &name) {
453 unique_lock<mutex> lock(catalog_lock);
454 CreateDefaultEntries(transaction, lock);
455
456 SimilarCatalogEntry result;
457 for (auto &kv : mapping) {
458 auto mapping_value = GetMapping(transaction, name: kv.first);
459 if (mapping_value && !mapping_value->deleted) {
460 auto ldist = StringUtil::SimilarityScore(s1: kv.first, s2: name);
461 if (ldist < result.distance) {
462 result.distance = ldist;
463 result.name = kv.first;
464 }
465 }
466 }
467 return result;
468}
469
470optional_ptr<CatalogEntry> CatalogSet::CreateEntryInternal(CatalogTransaction transaction,
471 unique_ptr<CatalogEntry> entry) {
472 if (mapping.find(x: entry->name) != mapping.end()) {
473 return nullptr;
474 }
475 auto &name = entry->name;
476 auto catalog_entry = entry.get();
477
478 entry->set = this;
479 entry->timestamp = 0;
480
481 auto entry_index = PutEntry(entry_index: current_entry++, entry: std::move(entry));
482 PutMapping(transaction, name, entry_index: std::move(entry_index));
483 mapping[name]->timestamp = 0;
484 return catalog_entry;
485}
486
487optional_ptr<CatalogEntry> CatalogSet::CreateDefaultEntry(CatalogTransaction transaction, const string &name,
488 unique_lock<mutex> &lock) {
489 // no entry found with this name, check for defaults
490 if (!defaults || defaults->created_all_entries) {
491 // no defaults either: return null
492 return nullptr;
493 }
494 // this catalog set has a default map defined
495 // check if there is a default entry that we can create with this name
496 if (!transaction.context) {
497 // no context - cannot create default entry
498 return nullptr;
499 }
500 lock.unlock();
501 auto entry = defaults->CreateDefaultEntry(context&: *transaction.context, entry_name: name);
502
503 lock.lock();
504 if (!entry) {
505 // no default entry
506 return nullptr;
507 }
508 // there is a default entry! create it
509 auto result = CreateEntryInternal(transaction, entry: std::move(entry));
510 if (result) {
511 return result;
512 }
513 // we found a default entry, but failed
514 // this means somebody else created the entry first
515 // just retry?
516 lock.unlock();
517 return GetEntry(transaction, name);
518}
519
520optional_ptr<CatalogEntry> CatalogSet::GetEntry(CatalogTransaction transaction, const string &name) {
521 unique_lock<mutex> lock(catalog_lock);
522 auto mapping_value = GetMapping(transaction, name);
523 if (mapping_value != nullptr && !mapping_value->deleted) {
524 // we found an entry for this name
525 // check the version numbers
526
527 auto &catalog_entry = *mapping_value->index.GetEntry();
528 auto &current = GetEntryForTransaction(transaction, current&: catalog_entry);
529 if (current.deleted || (current.name != name && !UseTimestamp(transaction, timestamp: mapping_value->timestamp))) {
530 return nullptr;
531 }
532 return &current;
533 }
534 return CreateDefaultEntry(transaction, name, lock);
535}
536
537optional_ptr<CatalogEntry> CatalogSet::GetEntry(ClientContext &context, const string &name) {
538 return GetEntry(transaction: catalog.GetCatalogTransaction(context), name);
539}
540
541void CatalogSet::UpdateTimestamp(CatalogEntry &entry, transaction_t timestamp) {
542 entry.timestamp = timestamp;
543 mapping[entry.name]->timestamp = timestamp;
544}
545
546void CatalogSet::AdjustUserDependency(CatalogEntry &entry, ColumnDefinition &column, bool remove) {
547 auto user_type_catalog_p = EnumType::GetCatalog(type: column.Type());
548 if (!user_type_catalog_p) {
549 return;
550 }
551 auto &user_type_catalog = user_type_catalog_p->Cast<CatalogEntry>();
552 auto &dependency_manager = catalog.GetDependencyManager();
553 if (remove) {
554 dependency_manager.dependents_map[user_type_catalog].erase(x: *entry.parent);
555 dependency_manager.dependencies_map[*entry.parent].erase(x: user_type_catalog);
556 } else {
557 dependency_manager.dependents_map[user_type_catalog].insert(x: entry);
558 dependency_manager.dependencies_map[entry].insert(x: user_type_catalog);
559 }
560}
561
562void CatalogSet::AdjustDependency(CatalogEntry &entry, TableCatalogEntry &table, ColumnDefinition &column,
563 bool remove) {
564 bool found = false;
565 if (column.Type().id() == LogicalTypeId::ENUM) {
566 for (auto &old_column : table.GetColumns().Logical()) {
567 if (old_column.Name() == column.Name() && old_column.Type().id() != LogicalTypeId::ENUM) {
568 AdjustUserDependency(entry, column, remove);
569 found = true;
570 }
571 }
572 if (!found) {
573 AdjustUserDependency(entry, column, remove);
574 }
575 } else if (!(column.Type().GetAlias().empty())) {
576 auto alias = column.Type().GetAlias();
577 for (auto &old_column : table.GetColumns().Logical()) {
578 auto old_alias = old_column.Type().GetAlias();
579 if (old_column.Name() == column.Name() && old_alias != alias) {
580 AdjustUserDependency(entry, column, remove);
581 found = true;
582 }
583 }
584 if (!found) {
585 AdjustUserDependency(entry, column, remove);
586 }
587 }
588}
589
590void CatalogSet::AdjustTableDependencies(CatalogEntry &entry) {
591 if (entry.type == CatalogType::TABLE_ENTRY && entry.parent->type == CatalogType::TABLE_ENTRY) {
592 // If it's a table entry we have to check for possibly removing or adding user type dependencies
593 auto &old_table = entry.parent->Cast<TableCatalogEntry>();
594 auto &new_table = entry.Cast<TableCatalogEntry>();
595
596 for (idx_t i = 0; i < new_table.GetColumns().LogicalColumnCount(); i++) {
597 auto &new_column = new_table.GetColumnsMutable().GetColumnMutable(index: LogicalIndex(i));
598 AdjustDependency(entry, table&: old_table, column&: new_column, remove: false);
599 }
600 for (idx_t i = 0; i < old_table.GetColumns().LogicalColumnCount(); i++) {
601 auto &old_column = old_table.GetColumnsMutable().GetColumnMutable(index: LogicalIndex(i));
602 AdjustDependency(entry, table&: new_table, column&: old_column, remove: true);
603 }
604 }
605}
606
607void CatalogSet::Undo(CatalogEntry &entry) {
608 lock_guard<mutex> write_lock(catalog.GetWriteLock());
609 lock_guard<mutex> lock(catalog_lock);
610
611 // entry has to be restored
612 // and entry->parent has to be removed ("rolled back")
613
614 // i.e. we have to place (entry) as (entry->parent) again
615 auto &to_be_removed_node = *entry.parent;
616
617 AdjustTableDependencies(entry);
618
619 if (!to_be_removed_node.deleted) {
620 // delete the entry from the dependency manager as well
621 auto &dependency_manager = catalog.GetDependencyManager();
622 dependency_manager.EraseObject(object&: to_be_removed_node);
623 }
624 if (!StringUtil::CIEquals(l1: entry.name, l2: to_be_removed_node.name)) {
625 // rename: clean up the new name when the rename is rolled back
626 auto removed_entry = mapping.find(x: to_be_removed_node.name);
627 if (removed_entry->second->child) {
628 removed_entry->second->child->parent = nullptr;
629 mapping[to_be_removed_node.name] = std::move(removed_entry->second->child);
630 } else {
631 mapping.erase(position: removed_entry);
632 }
633 }
634 if (to_be_removed_node.parent) {
635 // if the to be removed node has a parent, set the child pointer to the
636 // to be restored node
637 to_be_removed_node.parent->child = std::move(to_be_removed_node.child);
638 entry.parent = to_be_removed_node.parent;
639 } else {
640 // otherwise we need to update the base entry tables
641 auto &name = entry.name;
642 to_be_removed_node.child->SetAsRoot();
643 mapping[name]->index.GetEntry() = std::move(to_be_removed_node.child);
644 entry.parent = nullptr;
645 }
646
647 // restore the name if it was deleted
648 auto restored_entry = mapping.find(x: entry.name);
649 if (restored_entry->second->deleted || entry.type == CatalogType::INVALID) {
650 if (restored_entry->second->child) {
651 restored_entry->second->child->parent = nullptr;
652 mapping[entry.name] = std::move(restored_entry->second->child);
653 } else {
654 mapping.erase(position: restored_entry);
655 }
656 }
657 // we mark the catalog as being modified, since this action can lead to e.g. tables being dropped
658 catalog.ModifyCatalog();
659}
660
661void CatalogSet::CreateDefaultEntries(CatalogTransaction transaction, unique_lock<mutex> &lock) {
662 if (!defaults || defaults->created_all_entries || !transaction.context) {
663 return;
664 }
665 // this catalog set has a default set defined:
666 auto default_entries = defaults->GetDefaultEntries();
667 for (auto &default_entry : default_entries) {
668 auto map_entry = mapping.find(x: default_entry);
669 if (map_entry == mapping.end()) {
670 // we unlock during the CreateEntry, since it might reference other catalog sets...
671 // specifically for views this can happen since the view will be bound
672 lock.unlock();
673 auto entry = defaults->CreateDefaultEntry(context&: *transaction.context, entry_name: default_entry);
674 if (!entry) {
675 throw InternalException("Failed to create default entry for %s", default_entry);
676 }
677
678 lock.lock();
679 CreateEntryInternal(transaction, entry: std::move(entry));
680 }
681 }
682 defaults->created_all_entries = true;
683}
684
685void CatalogSet::Scan(CatalogTransaction transaction, const std::function<void(CatalogEntry &)> &callback) {
686 // lock the catalog set
687 unique_lock<mutex> lock(catalog_lock);
688 CreateDefaultEntries(transaction, lock);
689
690 for (auto &kv : entries) {
691 auto &entry = *kv.second.entry.get();
692 auto &entry_for_transaction = GetEntryForTransaction(transaction, current&: entry);
693 if (!entry_for_transaction.deleted) {
694 callback(entry_for_transaction);
695 }
696 }
697}
698
699void CatalogSet::Scan(ClientContext &context, const std::function<void(CatalogEntry &)> &callback) {
700 Scan(transaction: catalog.GetCatalogTransaction(context), callback);
701}
702
703void CatalogSet::Scan(const std::function<void(CatalogEntry &)> &callback) {
704 // lock the catalog set
705 lock_guard<mutex> lock(catalog_lock);
706 for (auto &kv : entries) {
707 auto entry = kv.second.entry.get();
708 auto &commited_entry = GetCommittedEntry(current&: *entry);
709 if (!commited_entry.deleted) {
710 callback(commited_entry);
711 }
712 }
713}
714
715void CatalogSet::Verify(Catalog &catalog_p) {
716 D_ASSERT(&catalog_p == &catalog);
717 vector<reference<CatalogEntry>> entries;
718 Scan(callback: [&](CatalogEntry &entry) { entries.push_back(x: entry); });
719 for (auto &entry : entries) {
720 entry.get().Verify(catalog_p);
721 }
722}
723
724} // namespace duckdb
725