1#include "duckdb/catalog/catalog.hpp"
2
3#include "duckdb/catalog/catalog_entry/list.hpp"
4#include "duckdb/common/exception.hpp"
5#include "duckdb/main/client_context.hpp"
6#include "duckdb/parser/expression/function_expression.hpp"
7#include "duckdb/parser/parsed_data/alter_table_info.hpp"
8#include "duckdb/parser/parsed_data/create_index_info.hpp"
9#include "duckdb/parser/parsed_data/create_aggregate_function_info.hpp"
10#include "duckdb/parser/parsed_data/create_collation_info.hpp"
11#include "duckdb/parser/parsed_data/create_scalar_function_info.hpp"
12#include "duckdb/parser/parsed_data/create_schema_info.hpp"
13#include "duckdb/parser/parsed_data/create_sequence_info.hpp"
14#include "duckdb/parser/parsed_data/create_table_function_info.hpp"
15#include "duckdb/parser/parsed_data/create_view_info.hpp"
16#include "duckdb/parser/parsed_data/drop_info.hpp"
17#include "duckdb/planner/parsed_data/bound_create_table_info.hpp"
18#include "duckdb/storage/storage_manager.hpp"
19#include "duckdb/main/database.hpp"
20
21using namespace duckdb;
22using namespace std;
23
24Catalog::Catalog(StorageManager &storage) : storage(storage), schemas(*this), dependency_manager(*this) {
25}
26
27Catalog &Catalog::GetCatalog(ClientContext &context) {
28 return context.catalog;
29}
30
31CatalogEntry *Catalog::CreateTable(ClientContext &context, BoundCreateTableInfo *info) {
32 auto schema = GetSchema(context, info->base->schema);
33 return schema->CreateTable(context, info);
34}
35
36CatalogEntry *Catalog::CreateView(ClientContext &context, CreateViewInfo *info) {
37 auto schema = GetSchema(context, info->schema);
38 return schema->CreateView(context, info);
39}
40
41CatalogEntry *Catalog::CreateSequence(ClientContext &context, CreateSequenceInfo *info) {
42 auto schema = GetSchema(context, info->schema);
43 return schema->CreateSequence(context, info);
44}
45
46CatalogEntry *Catalog::CreateTableFunction(ClientContext &context, CreateTableFunctionInfo *info) {
47 auto schema = GetSchema(context, info->schema);
48 return schema->CreateTableFunction(context, info);
49}
50
51CatalogEntry *Catalog::CreateFunction(ClientContext &context, CreateFunctionInfo *info) {
52 auto schema = GetSchema(context, info->schema);
53 return schema->CreateFunction(context, info);
54}
55
56CatalogEntry *Catalog::CreateCollation(ClientContext &context, CreateCollationInfo *info) {
57 auto schema = GetSchema(context, info->schema);
58 return schema->CreateCollation(context, info);
59}
60
61CatalogEntry *Catalog::CreateSchema(ClientContext &context, CreateSchemaInfo *info) {
62 if (info->schema == INVALID_SCHEMA) {
63 throw CatalogException("Schema not specified");
64 }
65 if (info->schema == TEMP_SCHEMA) {
66 throw CatalogException("Cannot create built-in schema \"%s\"", info->schema.c_str());
67 }
68
69 unordered_set<CatalogEntry *> dependencies;
70 auto entry = make_unique<SchemaCatalogEntry>(this, info->schema);
71 auto result = entry.get();
72 if (!schemas.CreateEntry(context.ActiveTransaction(), info->schema, move(entry), dependencies)) {
73 if (info->on_conflict == OnCreateConflict::ERROR) {
74 throw CatalogException("Schema with name %s already exists!", info->schema.c_str());
75 } else {
76 assert(info->on_conflict == OnCreateConflict::IGNORE);
77 }
78 return nullptr;
79 }
80 return result;
81}
82
83void Catalog::DropSchema(ClientContext &context, DropInfo *info) {
84 if (info->name == INVALID_SCHEMA) {
85 throw CatalogException("Schema not specified");
86 }
87 if (info->name == DEFAULT_SCHEMA || info->name == TEMP_SCHEMA) {
88 throw CatalogException("Cannot drop schema \"%s\" because it is required by the database system",
89 info->name.c_str());
90 }
91
92 if (!schemas.DropEntry(context.ActiveTransaction(), info->name, info->cascade)) {
93 if (!info->if_exists) {
94 throw CatalogException("Schema with name \"%s\" does not exist!", info->name.c_str());
95 }
96 }
97}
98
99void Catalog::DropEntry(ClientContext &context, DropInfo *info) {
100 if (info->type == CatalogType::SCHEMA) {
101 // DROP SCHEMA
102 DropSchema(context, info);
103 } else {
104 if (info->schema == INVALID_SCHEMA) {
105 // invalid schema: check if the entry is in the temp schema
106 auto entry = GetEntry(context, info->type, TEMP_SCHEMA, info->name, true);
107 info->schema = entry ? TEMP_SCHEMA : DEFAULT_SCHEMA;
108 }
109 auto schema = GetSchema(context, info->schema);
110 schema->DropEntry(context, info);
111 }
112}
113
114SchemaCatalogEntry *Catalog::GetSchema(ClientContext &context, const string &schema_name) {
115 if (schema_name == INVALID_SCHEMA) {
116 throw CatalogException("Schema not specified");
117 }
118 if (schema_name == TEMP_SCHEMA) {
119 return context.temporary_objects.get();
120 }
121 auto entry = schemas.GetEntry(context.ActiveTransaction(), schema_name);
122 if (!entry) {
123 throw CatalogException("Schema with name %s does not exist!", schema_name.c_str());
124 }
125 return (SchemaCatalogEntry *)entry;
126}
127
128CatalogEntry *Catalog::GetEntry(ClientContext &context, CatalogType type, string schema_name, const string &name,
129 bool if_exists) {
130 if (schema_name == INVALID_SCHEMA) {
131 // invalid schema: first search the temporary schema
132 auto entry = GetEntry(context, type, TEMP_SCHEMA, name, true);
133 if (entry) {
134 return entry;
135 }
136 // if the entry does not exist in the temp schema, search in the default schema
137 schema_name = DEFAULT_SCHEMA;
138 }
139 auto schema = GetSchema(context, schema_name);
140 return schema->GetEntry(context, type, name, if_exists);
141}
142
143template <>
144TableCatalogEntry *Catalog::GetEntry(ClientContext &context, string schema_name, const string &name, bool if_exists) {
145 auto entry = GetEntry(context, CatalogType::TABLE, move(schema_name), name, if_exists);
146 if (!entry) {
147 return nullptr;
148 }
149 if (entry->type != CatalogType::TABLE) {
150 throw CatalogException("%s is not a table", name.c_str());
151 }
152 return (TableCatalogEntry *)entry;
153}
154
155template <>
156SequenceCatalogEntry *Catalog::GetEntry(ClientContext &context, string schema_name, const string &name,
157 bool if_exists) {
158 return (SequenceCatalogEntry *)GetEntry(context, CatalogType::SEQUENCE, move(schema_name), name, if_exists);
159}
160
161template <>
162TableFunctionCatalogEntry *Catalog::GetEntry(ClientContext &context, string schema_name, const string &name,
163 bool if_exists) {
164 return (TableFunctionCatalogEntry *)GetEntry(context, CatalogType::TABLE_FUNCTION, move(schema_name), name,
165 if_exists);
166}
167
168template <>
169AggregateFunctionCatalogEntry *Catalog::GetEntry(ClientContext &context, string schema_name, const string &name,
170 bool if_exists) {
171 auto entry = GetEntry(context, CatalogType::AGGREGATE_FUNCTION, move(schema_name), name, if_exists);
172 if (entry->type != CatalogType::AGGREGATE_FUNCTION) {
173 throw CatalogException("%s is not an aggregate function", name.c_str());
174 }
175 return (AggregateFunctionCatalogEntry *)entry;
176}
177
178template <>
179CollateCatalogEntry *Catalog::GetEntry(ClientContext &context, string schema_name, const string &name, bool if_exists) {
180 return (CollateCatalogEntry *)GetEntry(context, CatalogType::COLLATION, move(schema_name), name, if_exists);
181}
182
183void Catalog::AlterTable(ClientContext &context, AlterTableInfo *info) {
184 if (info->schema == INVALID_SCHEMA) {
185 // invalid schema, look for table in temp schema
186 auto entry = GetEntry(context, CatalogType::TABLE, TEMP_SCHEMA, info->table, true);
187 info->schema = entry ? TEMP_SCHEMA : DEFAULT_SCHEMA;
188 }
189 auto schema = GetSchema(context, info->schema);
190 schema->AlterTable(context, info);
191}
192
193void Catalog::ParseRangeVar(string input, string &schema, string &name) {
194 idx_t idx = 0;
195 vector<string> entries;
196 string entry;
197normal:
198 // quote
199 for (; idx < input.size(); idx++) {
200 if (input[idx] == '"') {
201 idx++;
202 goto quoted;
203 } else if (input[idx] == '.') {
204 goto separator;
205 }
206 entry += input[idx];
207 }
208 goto end;
209separator:
210 entries.push_back(entry);
211 entry = "";
212 idx++;
213 goto normal;
214quoted:
215 // look for another quote
216 for (; idx < input.size(); idx++) {
217 if (input[idx] == '"') {
218 // unquote
219 idx++;
220 goto normal;
221 }
222 entry += input[idx];
223 }
224 throw ParserException("Unterminated quote in range var!");
225end:
226 if (entries.size() == 0) {
227 schema = INVALID_SCHEMA;
228 name = entry;
229 } else if (entries.size() == 1) {
230 schema = entries[0];
231 name = entry;
232 } else {
233 throw ParserException("Expected schema.entry or entry: too many entries found");
234 }
235}
236