1#include "duckdb/catalog/dependency_manager.hpp"
2#include "duckdb/catalog/catalog_entry/type_catalog_entry.hpp"
3#include "duckdb/catalog/duck_catalog.hpp"
4#include "duckdb/catalog/catalog_entry.hpp"
5#include "duckdb/catalog/catalog_entry/table_catalog_entry.hpp"
6#include "duckdb/main/client_context.hpp"
7#include "duckdb/main/database.hpp"
8#include "duckdb/parser/expression/constant_expression.hpp"
9#include "duckdb/catalog/mapping_value.hpp"
10#include "duckdb/catalog/dependency_list.hpp"
11
12namespace duckdb {
13
14DependencyManager::DependencyManager(DuckCatalog &catalog) : catalog(catalog) {
15}
16
17void DependencyManager::AddObject(CatalogTransaction transaction, CatalogEntry &object, DependencyList &dependencies) {
18 // check for each object in the sources if they were not deleted yet
19 for (auto &dep : dependencies.set) {
20 auto &dependency = dep.get();
21 if (&dependency.ParentCatalog() != &object.ParentCatalog()) {
22 throw DependencyException(
23 "Error adding dependency for object \"%s\" - dependency \"%s\" is in catalog "
24 "\"%s\", which does not match the catalog \"%s\".\nCross catalog dependencies are not supported.",
25 object.name, dependency.name, dependency.ParentCatalog().GetName(), object.ParentCatalog().GetName());
26 }
27 if (!dependency.set) {
28 throw InternalException("Dependency has no set");
29 }
30 auto catalog_entry = dependency.set->GetEntryInternal(transaction, name: dependency.name, entry_index: nullptr);
31 if (!catalog_entry) {
32 throw InternalException("Dependency has already been deleted?");
33 }
34 }
35 // indexes do not require CASCADE to be dropped, they are simply always dropped along with the table
36 auto dependency_type = object.type == CatalogType::INDEX_ENTRY ? DependencyType::DEPENDENCY_AUTOMATIC
37 : DependencyType::DEPENDENCY_REGULAR;
38 // add the object to the dependents_map of each object that it depends on
39 for (auto &dependency : dependencies.set) {
40 auto &set = dependents_map[dependency];
41 set.insert(x: Dependency(object, dependency_type));
42 }
43 // create the dependents map for this object: it starts out empty
44 dependents_map[object] = dependency_set_t();
45 dependencies_map[object] = dependencies.set;
46}
47
48void DependencyManager::DropObject(CatalogTransaction transaction, CatalogEntry &object, bool cascade) {
49 D_ASSERT(dependents_map.find(object) != dependents_map.end());
50
51 // first check the objects that depend on this object
52 auto &dependent_objects = dependents_map[object];
53 for (auto &dep : dependent_objects) {
54 // look up the entry in the catalog set
55 auto &entry = dep.entry.get();
56 auto &catalog_set = *entry.set;
57 auto mapping_value = catalog_set.GetMapping(transaction, name: entry.name, get_latest: true /* get_latest */);
58 if (mapping_value == nullptr) {
59 continue;
60 }
61 auto dependency_entry = catalog_set.GetEntryInternal(transaction, entry_index&: mapping_value->index);
62 if (!dependency_entry) {
63 // the dependent object was already deleted, no conflict
64 continue;
65 }
66 // conflict: attempting to delete this object but the dependent object still exists
67 if (cascade || dep.dependency_type == DependencyType::DEPENDENCY_AUTOMATIC ||
68 dep.dependency_type == DependencyType::DEPENDENCY_OWNS) {
69 // cascade: drop the dependent object
70 catalog_set.DropEntryInternal(transaction, entry_index: mapping_value->index.Copy(), entry&: *dependency_entry, cascade);
71 } else {
72 // no cascade and there are objects that depend on this object: throw error
73 throw DependencyException("Cannot drop entry \"%s\" because there are entries that "
74 "depend on it. Use DROP...CASCADE to drop all dependents.",
75 object.name);
76 }
77 }
78}
79
80void DependencyManager::AlterObject(CatalogTransaction transaction, CatalogEntry &old_obj, CatalogEntry &new_obj) {
81 D_ASSERT(dependents_map.find(old_obj) != dependents_map.end());
82 D_ASSERT(dependencies_map.find(old_obj) != dependencies_map.end());
83
84 // first check the objects that depend on this object
85 catalog_entry_vector_t owned_objects_to_add;
86 auto &dependent_objects = dependents_map[old_obj];
87 for (auto &dep : dependent_objects) {
88 // look up the entry in the catalog set
89 auto &entry = dep.entry.get();
90 auto &catalog_set = *entry.set;
91 auto dependency_entry = catalog_set.GetEntryInternal(transaction, name: entry.name, entry_index: nullptr);
92 if (!dependency_entry) {
93 // the dependent object was already deleted, no conflict
94 continue;
95 }
96 if (dep.dependency_type == DependencyType::DEPENDENCY_OWNS) {
97 // the dependent object is owned by the current object
98 owned_objects_to_add.push_back(x: dep.entry);
99 continue;
100 }
101 // conflict: attempting to alter this object but the dependent object still exists
102 // no cascade and there are objects that depend on this object: throw error
103 throw DependencyException("Cannot alter entry \"%s\" because there are entries that "
104 "depend on it.",
105 old_obj.name);
106 }
107 // add the new object to the dependents_map of each object that it depends on
108 auto &old_dependencies = dependencies_map[old_obj];
109 catalog_entry_vector_t to_delete;
110 for (auto &dep : old_dependencies) {
111 auto &dependency = dep.get();
112 if (dependency.type == CatalogType::TYPE_ENTRY) {
113 auto &user_type = dependency.Cast<TypeCatalogEntry>();
114 auto &table = new_obj.Cast<TableCatalogEntry>();
115 bool deleted_dependency = true;
116 for (auto &column : table.GetColumns().Logical()) {
117 if (column.Type() == user_type.user_type) {
118 deleted_dependency = false;
119 break;
120 }
121 }
122 if (deleted_dependency) {
123 to_delete.push_back(x: dependency);
124 continue;
125 }
126 }
127 dependents_map[dependency].insert(x: new_obj);
128 }
129 for (auto &dep : to_delete) {
130 auto &dependency = dep.get();
131 old_dependencies.erase(x: dependency);
132 dependents_map[dependency].erase(x: old_obj);
133 }
134
135 // We might have to add a type dependency
136 catalog_entry_vector_t to_add;
137 if (new_obj.type == CatalogType::TABLE_ENTRY) {
138 auto &table = new_obj.Cast<TableCatalogEntry>();
139 for (auto &column : table.GetColumns().Logical()) {
140 auto user_type_catalog = EnumType::GetCatalog(type: column.Type());
141 if (user_type_catalog) {
142 to_add.push_back(x: *user_type_catalog);
143 }
144 }
145 }
146 // add the new object to the dependency manager
147 dependents_map[new_obj] = dependency_set_t();
148 dependencies_map[new_obj] = old_dependencies;
149
150 for (auto &dependency : to_add) {
151 dependencies_map[new_obj].insert(x: dependency);
152 dependents_map[dependency].insert(x: new_obj);
153 }
154
155 for (auto &dependency : owned_objects_to_add) {
156 dependents_map[new_obj].insert(x: Dependency(dependency, DependencyType::DEPENDENCY_OWNS));
157 dependents_map[dependency].insert(x: Dependency(new_obj, DependencyType::DEPENDENCY_OWNED_BY));
158 dependencies_map[new_obj].insert(x: dependency);
159 }
160}
161
162void DependencyManager::EraseObject(CatalogEntry &object) {
163 // obtain the writing lock
164 EraseObjectInternal(object);
165}
166
167void DependencyManager::EraseObjectInternal(CatalogEntry &object) {
168 if (dependents_map.find(x: object) == dependents_map.end()) {
169 // dependencies already removed
170 return;
171 }
172 D_ASSERT(dependents_map.find(object) != dependents_map.end());
173 D_ASSERT(dependencies_map.find(object) != dependencies_map.end());
174 // now for each of the dependencies, erase the entries from the dependents_map
175 for (auto &dependency : dependencies_map[object]) {
176 auto entry = dependents_map.find(x: dependency);
177 if (entry != dependents_map.end()) {
178 D_ASSERT(entry->second.find(object) != entry->second.end());
179 entry->second.erase(x: object);
180 }
181 }
182 // erase the dependents and dependencies for this object
183 dependents_map.erase(x: object);
184 dependencies_map.erase(x: object);
185}
186
187void DependencyManager::Scan(const std::function<void(CatalogEntry &, CatalogEntry &, DependencyType)> &callback) {
188 lock_guard<mutex> write_lock(catalog.GetWriteLock());
189 for (auto &entry : dependents_map) {
190 for (auto &dependent : entry.second) {
191 callback(entry.first, dependent.entry, dependent.dependency_type);
192 }
193 }
194}
195
196void DependencyManager::AddOwnership(CatalogTransaction transaction, CatalogEntry &owner, CatalogEntry &entry) {
197 // lock the catalog for writing
198 lock_guard<mutex> write_lock(catalog.GetWriteLock());
199
200 // If the owner is already owned by something else, throw an error
201 for (auto &dep : dependents_map[owner]) {
202 if (dep.dependency_type == DependencyType::DEPENDENCY_OWNED_BY) {
203 throw DependencyException(owner.name + " already owned by " + dep.entry.get().name);
204 }
205 }
206
207 // If the entry is already owned, throw an error
208 for (auto &dep : dependents_map[entry]) {
209 // if the entry is already owned, throw error
210 if (&dep.entry.get() != &owner) {
211 throw DependencyException(entry.name + " already depends on " + dep.entry.get().name);
212 }
213 // if the entry owns the owner, throw error
214 if (&dep.entry.get() == &owner && dep.dependency_type == DependencyType::DEPENDENCY_OWNS) {
215 throw DependencyException(entry.name + " already owns " + owner.name +
216 ". Cannot have circular dependencies");
217 }
218 }
219
220 // Emplace guarantees that the same object cannot be inserted twice in the unordered_set
221 // In the case AddOwnership is called twice, because of emplace, the object will not be repeated in the set.
222 // We use an automatic dependency because if the Owner gets deleted, then the owned objects are also deleted
223 dependents_map[owner].emplace(args&: entry, args: DependencyType::DEPENDENCY_OWNS);
224 dependents_map[entry].emplace(args&: owner, args: DependencyType::DEPENDENCY_OWNED_BY);
225 dependencies_map[owner].emplace(args&: entry);
226}
227
228} // namespace duckdb
229