1 | #include "duckdb/catalog/dependency_manager.hpp" |
2 | |
3 | #include "duckdb/catalog/catalog.hpp" |
4 | |
5 | using namespace duckdb; |
6 | using namespace std; |
7 | |
8 | DependencyManager::DependencyManager(Catalog &catalog) : catalog(catalog) { |
9 | } |
10 | |
11 | void DependencyManager::AddObject(Transaction &transaction, CatalogEntry *object, |
12 | unordered_set<CatalogEntry *> &dependencies) { |
13 | // check for each object in the sources if they were not deleted yet |
14 | for (auto &dependency : dependencies) { |
15 | auto entry = dependency->set->data.find(dependency->name); |
16 | assert(entry != dependency->set->data.end()); |
17 | |
18 | if (CatalogSet::HasConflict(transaction, *entry->second)) { |
19 | // transaction conflict with this entry |
20 | throw TransactionException("Catalog write-write conflict on create with \"%s\"" , object->name.c_str()); |
21 | } |
22 | } |
23 | // add the object to the dependents_map of each object that it depends on |
24 | for (auto &dependency : dependencies) { |
25 | dependents_map[dependency].insert(object); |
26 | } |
27 | // create the dependents map for this object: it starts out empty |
28 | dependents_map[object] = unordered_set<CatalogEntry *>(); |
29 | dependencies_map[object] = dependencies; |
30 | } |
31 | |
32 | void DependencyManager::DropObject(Transaction &transaction, CatalogEntry *object, bool cascade, |
33 | set_lock_map_t &lock_set) { |
34 | assert(dependents_map.find(object) != dependents_map.end()); |
35 | |
36 | // first check the objects that depend on this object |
37 | auto &dependent_objects = dependents_map[object]; |
38 | for (auto &dep : dependent_objects) { |
39 | // look up the entry in the catalog set |
40 | auto &catalog_set = *dep->set; |
41 | auto entry = catalog_set.data.find(dep->name); |
42 | assert(entry != catalog_set.data.end()); |
43 | if (CatalogSet::HasConflict(transaction, *entry->second)) { |
44 | // current version has been written to by a currently active transaction |
45 | throw TransactionException("Catalog write-write conflict on drop with \"%s\": conflict with dependency" , |
46 | object->name.c_str()); |
47 | } |
48 | // there is a current version that has been committed |
49 | if (entry->second->deleted) { |
50 | // the dependent object was already deleted, no conflict |
51 | continue; |
52 | } |
53 | // conflict: attempting to delete this object but the dependent object still exists |
54 | if (cascade) { |
55 | // cascade: drop the dependent object |
56 | catalog_set.DropEntryInternal(transaction, *entry->second, cascade, lock_set); |
57 | } else { |
58 | // no cascade and there are objects that depend on this object: throw error |
59 | throw CatalogException("Cannot drop entry \"%s\" because there are entries that " |
60 | "depend on it. Use DROP...CASCADE to drop all dependents." , |
61 | object->name.c_str()); |
62 | } |
63 | } |
64 | } |
65 | |
66 | void DependencyManager::AlterObject(Transaction &transaction, CatalogEntry *old_obj, CatalogEntry *new_obj) { |
67 | assert(dependents_map.find(old_obj) != dependents_map.end()); |
68 | assert(dependencies_map.find(old_obj) != dependencies_map.end()); |
69 | |
70 | // first check the objects that depend on this object |
71 | auto &dependent_objects = dependents_map[old_obj]; |
72 | for (auto &dep : dependent_objects) { |
73 | // look up the entry in the catalog set |
74 | auto &catalog_set = *dep->set; |
75 | auto entry = catalog_set.data.find(dep->name); |
76 | assert(entry != catalog_set.data.end()); |
77 | if (CatalogSet::HasConflict(transaction, *entry->second)) { |
78 | // current version has been written to by a currently active transaction |
79 | throw TransactionException("Catalog write-write conflict on drop with \"%s\"" , old_obj->name.c_str()); |
80 | } |
81 | // there is a current version that has been committed |
82 | if (entry->second->deleted) { |
83 | // the dependent object was already deleted, no conflict |
84 | continue; |
85 | } |
86 | // conflict: attempting to alter this object but the dependent object still exists |
87 | // no cascade and there are objects that depend on this object: throw error |
88 | throw CatalogException("Cannot alter entry \"%s\" because there are entries that " |
89 | "depend on it." , |
90 | old_obj->name.c_str()); |
91 | } |
92 | // add the new object to the dependents_map of each object that it depents on |
93 | auto &old_dependencies = dependencies_map[old_obj]; |
94 | for (auto &dependency : old_dependencies) { |
95 | dependents_map[dependency].insert(new_obj); |
96 | } |
97 | // add the new object to the dependency manager |
98 | dependents_map[new_obj] = unordered_set<CatalogEntry *>(); |
99 | dependencies_map[new_obj] = old_dependencies; |
100 | } |
101 | |
102 | void DependencyManager::EraseObject(CatalogEntry *object) { |
103 | // obtain the writing lock |
104 | lock_guard<mutex> write_lock(catalog.write_lock); |
105 | EraseObjectInternal(object); |
106 | } |
107 | |
108 | void DependencyManager::EraseObjectInternal(CatalogEntry *object) { |
109 | if (dependents_map.find(object) == dependents_map.end()) { |
110 | // dependencies already removed |
111 | return; |
112 | } |
113 | assert(dependents_map.find(object) != dependents_map.end()); |
114 | assert(dependencies_map.find(object) != dependencies_map.end()); |
115 | // now for each of the dependencies, erase the entries from the dependents_map |
116 | for (auto &dependency : dependencies_map[object]) { |
117 | auto entry = dependents_map.find(dependency); |
118 | if (entry != dependents_map.end()) { |
119 | assert(entry->second.find(object) != entry->second.end()); |
120 | entry->second.erase(object); |
121 | } |
122 | } |
123 | // erase the dependents and dependencies for this object |
124 | dependents_map.erase(object); |
125 | dependencies_map.erase(object); |
126 | } |
127 | |
128 | void DependencyManager::ClearDependencies(CatalogSet &set) { |
129 | // obtain the writing lock |
130 | lock_guard<mutex> write_lock(catalog.write_lock); |
131 | |
132 | // iterate over the objects in the CatalogSet |
133 | for (auto &entry : set.data) { |
134 | CatalogEntry *centry = entry.second.get(); |
135 | while (centry) { |
136 | EraseObjectInternal(centry); |
137 | centry = centry->child.get(); |
138 | } |
139 | } |
140 | } |
141 | |