1#include "duckdb/catalog/dependency_manager.hpp"
2
3#include "duckdb/catalog/catalog.hpp"
4
5using namespace duckdb;
6using namespace std;
7
8DependencyManager::DependencyManager(Catalog &catalog) : catalog(catalog) {
9}
10
11void 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
32void 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
66void 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
102void DependencyManager::EraseObject(CatalogEntry *object) {
103 // obtain the writing lock
104 lock_guard<mutex> write_lock(catalog.write_lock);
105 EraseObjectInternal(object);
106}
107
108void 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
128void 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