1 | #include "duckdb/catalog/catalog.hpp" |
2 | #include "duckdb/catalog/catalog_search_path.hpp" |
3 | #include "duckdb/catalog/catalog_entry/duck_table_entry.hpp" |
4 | #include "duckdb/catalog/catalog_entry/schema_catalog_entry.hpp" |
5 | #include "duckdb/catalog/catalog_entry/type_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/parser/expression/function_expression.hpp" |
10 | #include "duckdb/parser/expression/subquery_expression.hpp" |
11 | #include "duckdb/planner/expression/bound_cast_expression.hpp" |
12 | #include "duckdb/planner/expression/bound_columnref_expression.hpp" |
13 | #include "duckdb/parser/parsed_data/create_index_info.hpp" |
14 | #include "duckdb/parser/parsed_data/create_macro_info.hpp" |
15 | #include "duckdb/parser/parsed_data/create_view_info.hpp" |
16 | #include "duckdb/parser/tableref/table_function_ref.hpp" |
17 | #include "duckdb/parser/parsed_expression_iterator.hpp" |
18 | #include "duckdb/parser/statement/create_statement.hpp" |
19 | #include "duckdb/planner/binder.hpp" |
20 | #include "duckdb/planner/bound_query_node.hpp" |
21 | #include "duckdb/planner/expression_binder/index_binder.hpp" |
22 | #include "duckdb/planner/expression_binder/select_binder.hpp" |
23 | #include "duckdb/planner/operator/logical_create.hpp" |
24 | #include "duckdb/planner/operator/logical_create_index.hpp" |
25 | #include "duckdb/planner/operator/logical_create_table.hpp" |
26 | #include "duckdb/planner/operator/logical_get.hpp" |
27 | #include "duckdb/planner/operator/logical_distinct.hpp" |
28 | #include "duckdb/planner/operator/logical_projection.hpp" |
29 | #include "duckdb/planner/parsed_data/bound_create_table_info.hpp" |
30 | #include "duckdb/planner/query_node/bound_select_node.hpp" |
31 | #include "duckdb/planner/tableref/bound_basetableref.hpp" |
32 | #include "duckdb/parser/constraints/foreign_key_constraint.hpp" |
33 | #include "duckdb/function/scalar_macro_function.hpp" |
34 | #include "duckdb/storage/data_table.hpp" |
35 | #include "duckdb/storage/storage_extension.hpp" |
36 | #include "duckdb/main/client_data.hpp" |
37 | #include "duckdb/parser/constraints/unique_constraint.hpp" |
38 | #include "duckdb/parser/constraints/list.hpp" |
39 | #include "duckdb/main/database_manager.hpp" |
40 | #include "duckdb/main/attached_database.hpp" |
41 | #include "duckdb/catalog/duck_catalog.hpp" |
42 | |
43 | namespace duckdb { |
44 | |
45 | void Binder::BindSchemaOrCatalog(ClientContext &context, string &catalog, string &schema) { |
46 | if (catalog.empty() && !schema.empty()) { |
47 | // schema is specified - but catalog is not |
48 | // try searching for the catalog instead |
49 | auto &db_manager = DatabaseManager::Get(db&: context); |
50 | auto database = db_manager.GetDatabase(context, name: schema); |
51 | if (database) { |
52 | // we have a database with this name |
53 | // check if there is a schema |
54 | auto schema_obj = Catalog::GetSchema(context, INVALID_CATALOG, schema_name: schema, if_not_found: OnEntryNotFound::RETURN_NULL); |
55 | if (schema_obj) { |
56 | auto &attached = schema_obj->catalog.GetAttached(); |
57 | throw BinderException( |
58 | "Ambiguous reference to catalog or schema \"%s\" - use a fully qualified path like \"%s.%s\"" , |
59 | schema, attached.GetName(), schema); |
60 | } |
61 | catalog = schema; |
62 | schema = string(); |
63 | } |
64 | } |
65 | } |
66 | |
67 | void Binder::BindSchemaOrCatalog(string &catalog, string &schema) { |
68 | BindSchemaOrCatalog(context, catalog, schema); |
69 | } |
70 | |
71 | SchemaCatalogEntry &Binder::BindSchema(CreateInfo &info) { |
72 | BindSchemaOrCatalog(catalog&: info.catalog, schema&: info.schema); |
73 | if (IsInvalidCatalog(str: info.catalog) && info.temporary) { |
74 | info.catalog = TEMP_CATALOG; |
75 | } |
76 | auto &search_path = ClientData::Get(context).catalog_search_path; |
77 | if (IsInvalidCatalog(str: info.catalog) && IsInvalidSchema(str: info.schema)) { |
78 | auto &default_entry = search_path->GetDefault(); |
79 | info.catalog = default_entry.catalog; |
80 | info.schema = default_entry.schema; |
81 | } else if (IsInvalidSchema(str: info.schema)) { |
82 | info.schema = search_path->GetDefaultSchema(catalog: info.catalog); |
83 | } else if (IsInvalidCatalog(str: info.catalog)) { |
84 | info.catalog = search_path->GetDefaultCatalog(schema: info.schema); |
85 | } |
86 | if (IsInvalidCatalog(str: info.catalog)) { |
87 | info.catalog = DatabaseManager::GetDefaultDatabase(context); |
88 | } |
89 | if (!info.temporary) { |
90 | // non-temporary create: not read only |
91 | if (info.catalog == TEMP_CATALOG) { |
92 | throw ParserException("Only TEMPORARY table names can use the \"%s\" catalog" , TEMP_CATALOG); |
93 | } |
94 | } else { |
95 | if (info.catalog != TEMP_CATALOG) { |
96 | throw ParserException("TEMPORARY table names can *only* use the \"%s\" catalog" , TEMP_CATALOG); |
97 | } |
98 | } |
99 | // fetch the schema in which we want to create the object |
100 | auto &schema_obj = Catalog::GetSchema(context, catalog_name: info.catalog, schema_name: info.schema); |
101 | D_ASSERT(schema_obj.type == CatalogType::SCHEMA_ENTRY); |
102 | info.schema = schema_obj.name; |
103 | if (!info.temporary) { |
104 | properties.modified_databases.insert(x: schema_obj.catalog.GetName()); |
105 | } |
106 | return schema_obj; |
107 | } |
108 | |
109 | SchemaCatalogEntry &Binder::BindCreateSchema(CreateInfo &info) { |
110 | auto &schema = BindSchema(info); |
111 | if (schema.catalog.IsSystemCatalog()) { |
112 | throw BinderException("Cannot create entry in system catalog" ); |
113 | } |
114 | return schema; |
115 | } |
116 | |
117 | void Binder::BindCreateViewInfo(CreateViewInfo &base) { |
118 | // bind the view as if it were a query so we can catch errors |
119 | // note that we bind the original, and replace the original with a copy |
120 | auto view_binder = Binder::CreateBinder(context); |
121 | view_binder->can_contain_nulls = true; |
122 | |
123 | auto copy = base.query->Copy(); |
124 | auto query_node = view_binder->Bind(stmt&: *base.query); |
125 | base.query = unique_ptr_cast<SQLStatement, SelectStatement>(src: std::move(copy)); |
126 | if (base.aliases.size() > query_node.names.size()) { |
127 | throw BinderException("More VIEW aliases than columns in query result" ); |
128 | } |
129 | // fill up the aliases with the remaining names of the bound query |
130 | base.aliases.reserve(n: query_node.names.size()); |
131 | for (idx_t i = base.aliases.size(); i < query_node.names.size(); i++) { |
132 | base.aliases.push_back(x: query_node.names[i]); |
133 | } |
134 | base.types = query_node.types; |
135 | } |
136 | |
137 | static void QualifyFunctionNames(ClientContext &context, unique_ptr<ParsedExpression> &expr) { |
138 | switch (expr->GetExpressionClass()) { |
139 | case ExpressionClass::FUNCTION: { |
140 | auto &func = expr->Cast<FunctionExpression>(); |
141 | auto function = Catalog::GetEntry(context, type: CatalogType::SCALAR_FUNCTION_ENTRY, catalog: func.catalog, schema: func.schema, |
142 | name: func.function_name, if_not_found: OnEntryNotFound::RETURN_NULL); |
143 | if (function) { |
144 | func.catalog = function->ParentCatalog().GetName(); |
145 | func.schema = function->ParentSchema().name; |
146 | } |
147 | break; |
148 | } |
149 | case ExpressionClass::SUBQUERY: { |
150 | // replacing parameters within a subquery is slightly different |
151 | auto &sq = (expr->Cast<SubqueryExpression>()).subquery; |
152 | ParsedExpressionIterator::EnumerateQueryNodeChildren( |
153 | node&: *sq->node, callback: [&](unique_ptr<ParsedExpression> &child) { QualifyFunctionNames(context, expr&: child); }); |
154 | break; |
155 | } |
156 | default: // fall through |
157 | break; |
158 | } |
159 | // unfold child expressions |
160 | ParsedExpressionIterator::EnumerateChildren( |
161 | expr&: *expr, callback: [&](unique_ptr<ParsedExpression> &child) { QualifyFunctionNames(context, expr&: child); }); |
162 | } |
163 | |
164 | SchemaCatalogEntry &Binder::BindCreateFunctionInfo(CreateInfo &info) { |
165 | auto &base = info.Cast<CreateMacroInfo>(); |
166 | auto &scalar_function = base.function->Cast<ScalarMacroFunction>(); |
167 | |
168 | if (scalar_function.expression->HasParameter()) { |
169 | throw BinderException("Parameter expressions within macro's are not supported!" ); |
170 | } |
171 | |
172 | // create macro binding in order to bind the function |
173 | vector<LogicalType> dummy_types; |
174 | vector<string> dummy_names; |
175 | // positional parameters |
176 | for (idx_t i = 0; i < base.function->parameters.size(); i++) { |
177 | auto param = base.function->parameters[i]->Cast<ColumnRefExpression>(); |
178 | if (param.IsQualified()) { |
179 | throw BinderException("Invalid parameter name '%s': must be unqualified" , param.ToString()); |
180 | } |
181 | dummy_types.emplace_back(args: LogicalType::SQLNULL); |
182 | dummy_names.push_back(x: param.GetColumnName()); |
183 | } |
184 | // default parameters |
185 | for (auto it = base.function->default_parameters.begin(); it != base.function->default_parameters.end(); it++) { |
186 | auto &val = it->second->Cast<ConstantExpression>(); |
187 | dummy_types.push_back(x: val.value.type()); |
188 | dummy_names.push_back(x: it->first); |
189 | } |
190 | auto this_macro_binding = make_uniq<DummyBinding>(args&: dummy_types, args&: dummy_names, args&: base.name); |
191 | macro_binding = this_macro_binding.get(); |
192 | ExpressionBinder::QualifyColumnNames(binder&: *this, expr&: scalar_function.expression); |
193 | QualifyFunctionNames(context, expr&: scalar_function.expression); |
194 | |
195 | // create a copy of the expression because we do not want to alter the original |
196 | auto expression = scalar_function.expression->Copy(); |
197 | |
198 | // bind it to verify the function was defined correctly |
199 | string error; |
200 | auto sel_node = make_uniq<BoundSelectNode>(); |
201 | auto group_info = make_uniq<BoundGroupInformation>(); |
202 | SelectBinder binder(*this, context, *sel_node, *group_info); |
203 | error = binder.Bind(expr&: expression, depth: 0, root_expression: false); |
204 | |
205 | if (!error.empty()) { |
206 | throw BinderException(error); |
207 | } |
208 | |
209 | return BindCreateSchema(info); |
210 | } |
211 | |
212 | void Binder::BindLogicalType(ClientContext &context, LogicalType &type, optional_ptr<Catalog> catalog, |
213 | const string &schema) { |
214 | if (type.id() == LogicalTypeId::LIST || type.id() == LogicalTypeId::MAP) { |
215 | auto child_type = ListType::GetChildType(type); |
216 | BindLogicalType(context, type&: child_type, catalog, schema); |
217 | auto alias = type.GetAlias(); |
218 | if (type.id() == LogicalTypeId::LIST) { |
219 | type = LogicalType::LIST(child: child_type); |
220 | } else { |
221 | D_ASSERT(child_type.id() == LogicalTypeId::STRUCT); // map must be list of structs |
222 | type = LogicalType::MAP(child: child_type); |
223 | } |
224 | |
225 | type.SetAlias(alias); |
226 | } else if (type.id() == LogicalTypeId::STRUCT) { |
227 | auto child_types = StructType::GetChildTypes(type); |
228 | for (auto &child_type : child_types) { |
229 | BindLogicalType(context, type&: child_type.second, catalog, schema); |
230 | } |
231 | // Generate new Struct Type |
232 | auto alias = type.GetAlias(); |
233 | type = LogicalType::STRUCT(children: child_types); |
234 | type.SetAlias(alias); |
235 | } else if (type.id() == LogicalTypeId::UNION) { |
236 | auto member_types = UnionType::CopyMemberTypes(type); |
237 | for (auto &member_type : member_types) { |
238 | BindLogicalType(context, type&: member_type.second, catalog, schema); |
239 | } |
240 | // Generate new Union Type |
241 | auto alias = type.GetAlias(); |
242 | type = LogicalType::UNION(members: member_types); |
243 | type.SetAlias(alias); |
244 | } else if (type.id() == LogicalTypeId::USER) { |
245 | auto user_type_name = UserType::GetTypeName(type); |
246 | if (catalog) { |
247 | // The search order is: |
248 | // 1) In the same schema as the table |
249 | // 2) In the same catalog |
250 | // 3) System catalog |
251 | type = catalog->GetType(context, schema, names: user_type_name, if_not_found: OnEntryNotFound::RETURN_NULL); |
252 | |
253 | if (type.id() == LogicalTypeId::INVALID) { |
254 | type = catalog->GetType(context, INVALID_SCHEMA, names: user_type_name, if_not_found: OnEntryNotFound::RETURN_NULL); |
255 | } |
256 | |
257 | if (type.id() == LogicalTypeId::INVALID) { |
258 | type = Catalog::GetType(context, INVALID_CATALOG, schema, name: user_type_name); |
259 | } |
260 | } else { |
261 | type = Catalog::GetType(context, INVALID_CATALOG, schema, name: user_type_name); |
262 | } |
263 | } else if (type.id() == LogicalTypeId::ENUM) { |
264 | auto enum_type_name = EnumType::GetTypeName(type); |
265 | optional_ptr<TypeCatalogEntry> enum_type_catalog; |
266 | if (catalog) { |
267 | enum_type_catalog = |
268 | catalog->GetEntry<TypeCatalogEntry>(context, schema_name: schema, name: enum_type_name, if_not_found: OnEntryNotFound::RETURN_NULL); |
269 | if (!enum_type_catalog) { |
270 | // look in the system catalog if the type was not found |
271 | enum_type_catalog = Catalog::GetEntry<TypeCatalogEntry>(context, SYSTEM_CATALOG, schema_name: schema, name: enum_type_name, |
272 | if_not_found: OnEntryNotFound::RETURN_NULL); |
273 | } |
274 | } else { |
275 | enum_type_catalog = Catalog::GetEntry<TypeCatalogEntry>(context, INVALID_CATALOG, schema_name: schema, name: enum_type_name, |
276 | if_not_found: OnEntryNotFound::RETURN_NULL); |
277 | } |
278 | |
279 | EnumType::SetCatalog(type, catalog_entry: enum_type_catalog.get()); |
280 | } |
281 | } |
282 | |
283 | static void FindMatchingPrimaryKeyColumns(const ColumnList &columns, const vector<unique_ptr<Constraint>> &constraints, |
284 | ForeignKeyConstraint &fk) { |
285 | // find the matching primary key constraint |
286 | bool found_constraint = false; |
287 | // if no columns are defined, we will automatically try to bind to the primary key |
288 | bool find_primary_key = fk.pk_columns.empty(); |
289 | for (auto &constr : constraints) { |
290 | if (constr->type != ConstraintType::UNIQUE) { |
291 | continue; |
292 | } |
293 | auto &unique = constr->Cast<UniqueConstraint>(); |
294 | if (find_primary_key && !unique.is_primary_key) { |
295 | continue; |
296 | } |
297 | found_constraint = true; |
298 | |
299 | vector<string> pk_names; |
300 | if (unique.index.index != DConstants::INVALID_INDEX) { |
301 | pk_names.push_back(x: columns.GetColumn(index: LogicalIndex(unique.index)).Name()); |
302 | } else { |
303 | pk_names = unique.columns; |
304 | } |
305 | if (find_primary_key) { |
306 | // found matching primary key |
307 | if (pk_names.size() != fk.fk_columns.size()) { |
308 | auto pk_name_str = StringUtil::Join(input: pk_names, separator: "," ); |
309 | auto fk_name_str = StringUtil::Join(input: fk.fk_columns, separator: "," ); |
310 | throw BinderException( |
311 | "Failed to create foreign key: number of referencing (%s) and referenced columns (%s) differ" , |
312 | fk_name_str, pk_name_str); |
313 | } |
314 | fk.pk_columns = pk_names; |
315 | return; |
316 | } |
317 | if (pk_names.size() != fk.fk_columns.size()) { |
318 | // the number of referencing and referenced columns for foreign keys must be the same |
319 | continue; |
320 | } |
321 | bool equals = true; |
322 | for (idx_t i = 0; i < fk.pk_columns.size(); i++) { |
323 | if (!StringUtil::CIEquals(l1: fk.pk_columns[i], l2: pk_names[i])) { |
324 | equals = false; |
325 | break; |
326 | } |
327 | } |
328 | if (!equals) { |
329 | continue; |
330 | } |
331 | // found match |
332 | return; |
333 | } |
334 | // no match found! examine why |
335 | if (!found_constraint) { |
336 | // no unique constraint or primary key |
337 | string search_term = find_primary_key ? "primary key" : "primary key or unique constraint" ; |
338 | throw BinderException("Failed to create foreign key: there is no %s for referenced table \"%s\"" , search_term, |
339 | fk.info.table); |
340 | } |
341 | // check if all the columns exist |
342 | for (auto &name : fk.pk_columns) { |
343 | bool found = columns.ColumnExists(name); |
344 | if (!found) { |
345 | throw BinderException( |
346 | "Failed to create foreign key: referenced table \"%s\" does not have a column named \"%s\"" , |
347 | fk.info.table, name); |
348 | } |
349 | } |
350 | auto fk_names = StringUtil::Join(input: fk.pk_columns, separator: "," ); |
351 | throw BinderException("Failed to create foreign key: referenced table \"%s\" does not have a primary key or unique " |
352 | "constraint on the columns %s" , |
353 | fk.info.table, fk_names); |
354 | } |
355 | |
356 | static void FindForeignKeyIndexes(const ColumnList &columns, const vector<string> &names, |
357 | vector<PhysicalIndex> &indexes) { |
358 | D_ASSERT(indexes.empty()); |
359 | D_ASSERT(!names.empty()); |
360 | for (auto &name : names) { |
361 | if (!columns.ColumnExists(name)) { |
362 | throw BinderException("column \"%s\" named in key does not exist" , name); |
363 | } |
364 | auto &column = columns.GetColumn(name); |
365 | if (column.Generated()) { |
366 | throw BinderException("Failed to create foreign key: referenced column \"%s\" is a generated column" , |
367 | column.Name()); |
368 | } |
369 | indexes.push_back(x: column.Physical()); |
370 | } |
371 | } |
372 | |
373 | static void CheckForeignKeyTypes(const ColumnList &pk_columns, const ColumnList &fk_columns, ForeignKeyConstraint &fk) { |
374 | D_ASSERT(fk.info.pk_keys.size() == fk.info.fk_keys.size()); |
375 | for (idx_t c_idx = 0; c_idx < fk.info.pk_keys.size(); c_idx++) { |
376 | auto &pk_col = pk_columns.GetColumn(index: fk.info.pk_keys[c_idx]); |
377 | auto &fk_col = fk_columns.GetColumn(index: fk.info.fk_keys[c_idx]); |
378 | if (pk_col.Type() != fk_col.Type()) { |
379 | throw BinderException("Failed to create foreign key: incompatible types between column \"%s\" (\"%s\") and " |
380 | "column \"%s\" (\"%s\")" , |
381 | pk_col.Name(), pk_col.Type().ToString(), fk_col.Name(), fk_col.Type().ToString()); |
382 | } |
383 | } |
384 | } |
385 | |
386 | void ExpressionContainsGeneratedColumn(const ParsedExpression &expr, const unordered_set<string> &gcols, |
387 | bool &contains_gcol) { |
388 | if (contains_gcol) { |
389 | return; |
390 | } |
391 | if (expr.type == ExpressionType::COLUMN_REF) { |
392 | auto &column_ref = expr.Cast<ColumnRefExpression>(); |
393 | auto &name = column_ref.GetColumnName(); |
394 | if (gcols.count(x: name)) { |
395 | contains_gcol = true; |
396 | return; |
397 | } |
398 | } |
399 | ParsedExpressionIterator::EnumerateChildren( |
400 | expression: expr, callback: [&](const ParsedExpression &child) { ExpressionContainsGeneratedColumn(expr: child, gcols, contains_gcol); }); |
401 | } |
402 | |
403 | static bool AnyConstraintReferencesGeneratedColumn(CreateTableInfo &table_info) { |
404 | unordered_set<string> generated_columns; |
405 | for (auto &col : table_info.columns.Logical()) { |
406 | if (!col.Generated()) { |
407 | continue; |
408 | } |
409 | generated_columns.insert(x: col.Name()); |
410 | } |
411 | if (generated_columns.empty()) { |
412 | return false; |
413 | } |
414 | |
415 | for (auto &constr : table_info.constraints) { |
416 | switch (constr->type) { |
417 | case ConstraintType::CHECK: { |
418 | auto &constraint = constr->Cast<CheckConstraint>(); |
419 | auto &expr = constraint.expression; |
420 | bool contains_generated_column = false; |
421 | ExpressionContainsGeneratedColumn(expr: *expr, gcols: generated_columns, contains_gcol&: contains_generated_column); |
422 | if (contains_generated_column) { |
423 | return true; |
424 | } |
425 | break; |
426 | } |
427 | case ConstraintType::NOT_NULL: { |
428 | auto &constraint = constr->Cast<NotNullConstraint>(); |
429 | if (table_info.columns.GetColumn(index: constraint.index).Generated()) { |
430 | return true; |
431 | } |
432 | break; |
433 | } |
434 | case ConstraintType::UNIQUE: { |
435 | auto &constraint = constr->Cast<UniqueConstraint>(); |
436 | auto index = constraint.index; |
437 | if (index.index == DConstants::INVALID_INDEX) { |
438 | for (auto &col : constraint.columns) { |
439 | if (generated_columns.count(x: col)) { |
440 | return true; |
441 | } |
442 | } |
443 | } else { |
444 | if (table_info.columns.GetColumn(index).Generated()) { |
445 | return true; |
446 | } |
447 | } |
448 | break; |
449 | } |
450 | case ConstraintType::FOREIGN_KEY: { |
451 | // If it contained a generated column, an exception would have been thrown inside AddDataTableIndex earlier |
452 | break; |
453 | } |
454 | default: { |
455 | throw NotImplementedException("ConstraintType not implemented" ); |
456 | } |
457 | } |
458 | } |
459 | return false; |
460 | } |
461 | |
462 | unique_ptr<LogicalOperator> DuckCatalog::BindCreateIndex(Binder &binder, CreateStatement &stmt, |
463 | TableCatalogEntry &table, unique_ptr<LogicalOperator> plan) { |
464 | D_ASSERT(plan->type == LogicalOperatorType::LOGICAL_GET); |
465 | auto &base = stmt.info->Cast<CreateIndexInfo>(); |
466 | |
467 | auto &get = plan->Cast<LogicalGet>(); |
468 | // bind the index expressions |
469 | IndexBinder index_binder(binder, binder.context); |
470 | vector<unique_ptr<Expression>> expressions; |
471 | expressions.reserve(n: base.expressions.size()); |
472 | for (auto &expr : base.expressions) { |
473 | expressions.push_back(x: index_binder.Bind(expr)); |
474 | } |
475 | |
476 | auto create_index_info = unique_ptr_cast<CreateInfo, CreateIndexInfo>(src: std::move(stmt.info)); |
477 | for (auto &column_id : get.column_ids) { |
478 | if (column_id == COLUMN_IDENTIFIER_ROW_ID) { |
479 | throw BinderException("Cannot create an index on the rowid!" ); |
480 | } |
481 | create_index_info->scan_types.push_back(x: get.returned_types[column_id]); |
482 | } |
483 | create_index_info->scan_types.emplace_back(args: LogicalType::ROW_TYPE); |
484 | create_index_info->names = get.names; |
485 | create_index_info->column_ids = get.column_ids; |
486 | |
487 | // the logical CREATE INDEX also needs all fields to scan the referenced table |
488 | return make_uniq<LogicalCreateIndex>(args: std::move(get.bind_data), args: std::move(create_index_info), args: std::move(expressions), |
489 | args&: table, args: std::move(get.function)); |
490 | } |
491 | |
492 | BoundStatement Binder::Bind(CreateStatement &stmt) { |
493 | BoundStatement result; |
494 | result.names = {"Count" }; |
495 | result.types = {LogicalType::BIGINT}; |
496 | |
497 | auto catalog_type = stmt.info->type; |
498 | switch (catalog_type) { |
499 | case CatalogType::SCHEMA_ENTRY: |
500 | result.plan = make_uniq<LogicalCreate>(args: LogicalOperatorType::LOGICAL_CREATE_SCHEMA, args: std::move(stmt.info)); |
501 | break; |
502 | case CatalogType::VIEW_ENTRY: { |
503 | auto &base = stmt.info->Cast<CreateViewInfo>(); |
504 | // bind the schema |
505 | auto &schema = BindCreateSchema(info&: *stmt.info); |
506 | BindCreateViewInfo(base); |
507 | result.plan = make_uniq<LogicalCreate>(args: LogicalOperatorType::LOGICAL_CREATE_VIEW, args: std::move(stmt.info), args: &schema); |
508 | break; |
509 | } |
510 | case CatalogType::SEQUENCE_ENTRY: { |
511 | auto &schema = BindCreateSchema(info&: *stmt.info); |
512 | result.plan = |
513 | make_uniq<LogicalCreate>(args: LogicalOperatorType::LOGICAL_CREATE_SEQUENCE, args: std::move(stmt.info), args: &schema); |
514 | break; |
515 | } |
516 | case CatalogType::TABLE_MACRO_ENTRY: { |
517 | auto &schema = BindCreateSchema(info&: *stmt.info); |
518 | result.plan = |
519 | make_uniq<LogicalCreate>(args: LogicalOperatorType::LOGICAL_CREATE_MACRO, args: std::move(stmt.info), args: &schema); |
520 | break; |
521 | } |
522 | case CatalogType::MACRO_ENTRY: { |
523 | auto &schema = BindCreateFunctionInfo(info&: *stmt.info); |
524 | result.plan = |
525 | make_uniq<LogicalCreate>(args: LogicalOperatorType::LOGICAL_CREATE_MACRO, args: std::move(stmt.info), args: &schema); |
526 | break; |
527 | } |
528 | case CatalogType::INDEX_ENTRY: { |
529 | auto &base = stmt.info->Cast<CreateIndexInfo>(); |
530 | |
531 | // visit the table reference |
532 | auto bound_table = Bind(ref&: *base.table); |
533 | if (bound_table->type != TableReferenceType::BASE_TABLE) { |
534 | throw BinderException("Can only create an index over a base table!" ); |
535 | } |
536 | auto &table_binding = bound_table->Cast<BoundBaseTableRef>(); |
537 | auto &table = table_binding.table; |
538 | if (table.temporary) { |
539 | stmt.info->temporary = true; |
540 | } |
541 | // create a plan over the bound table |
542 | auto plan = CreatePlan(ref&: *bound_table); |
543 | if (plan->type != LogicalOperatorType::LOGICAL_GET) { |
544 | throw BinderException("Cannot create index on a view!" ); |
545 | } |
546 | |
547 | result.plan = table.catalog.BindCreateIndex(binder&: *this, stmt, table, plan: std::move(plan)); |
548 | break; |
549 | } |
550 | case CatalogType::TABLE_ENTRY: { |
551 | auto &create_info = stmt.info->Cast<CreateTableInfo>(); |
552 | // If there is a foreign key constraint, resolve primary key column's index from primary key column's name |
553 | reference_set_t<SchemaCatalogEntry> fk_schemas; |
554 | for (idx_t i = 0; i < create_info.constraints.size(); i++) { |
555 | auto &cond = create_info.constraints[i]; |
556 | if (cond->type != ConstraintType::FOREIGN_KEY) { |
557 | continue; |
558 | } |
559 | auto &fk = cond->Cast<ForeignKeyConstraint>(); |
560 | if (fk.info.type != ForeignKeyType::FK_TYPE_FOREIGN_KEY_TABLE) { |
561 | continue; |
562 | } |
563 | D_ASSERT(fk.info.pk_keys.empty()); |
564 | D_ASSERT(fk.info.fk_keys.empty()); |
565 | FindForeignKeyIndexes(columns: create_info.columns, names: fk.fk_columns, indexes&: fk.info.fk_keys); |
566 | if (StringUtil::CIEquals(l1: create_info.table, l2: fk.info.table)) { |
567 | // self-referential foreign key constraint |
568 | fk.info.type = ForeignKeyType::FK_TYPE_SELF_REFERENCE_TABLE; |
569 | FindMatchingPrimaryKeyColumns(columns: create_info.columns, constraints: create_info.constraints, fk); |
570 | FindForeignKeyIndexes(columns: create_info.columns, names: fk.pk_columns, indexes&: fk.info.pk_keys); |
571 | CheckForeignKeyTypes(pk_columns: create_info.columns, fk_columns: create_info.columns, fk); |
572 | } else { |
573 | // have to resolve referenced table |
574 | auto &pk_table_entry_ptr = |
575 | Catalog::GetEntry<TableCatalogEntry>(context, INVALID_CATALOG, schema_name: fk.info.schema, name: fk.info.table); |
576 | fk_schemas.insert(x: pk_table_entry_ptr.schema); |
577 | FindMatchingPrimaryKeyColumns(columns: pk_table_entry_ptr.GetColumns(), constraints: pk_table_entry_ptr.GetConstraints(), fk); |
578 | FindForeignKeyIndexes(columns: pk_table_entry_ptr.GetColumns(), names: fk.pk_columns, indexes&: fk.info.pk_keys); |
579 | CheckForeignKeyTypes(pk_columns: pk_table_entry_ptr.GetColumns(), fk_columns: create_info.columns, fk); |
580 | auto &storage = pk_table_entry_ptr.GetStorage(); |
581 | auto index = storage.info->indexes.FindForeignKeyIndex(fk_keys: fk.info.pk_keys, |
582 | fk_type: ForeignKeyType::FK_TYPE_PRIMARY_KEY_TABLE); |
583 | if (!index) { |
584 | auto fk_column_names = StringUtil::Join(input: fk.pk_columns, separator: "," ); |
585 | throw BinderException("Failed to create foreign key on %s(%s): no UNIQUE or PRIMARY KEY constraint " |
586 | "present on these columns" , |
587 | pk_table_entry_ptr.name, fk_column_names); |
588 | } |
589 | } |
590 | D_ASSERT(fk.info.pk_keys.size() == fk.info.fk_keys.size()); |
591 | D_ASSERT(fk.info.pk_keys.size() == fk.pk_columns.size()); |
592 | D_ASSERT(fk.info.fk_keys.size() == fk.fk_columns.size()); |
593 | } |
594 | if (AnyConstraintReferencesGeneratedColumn(table_info&: create_info)) { |
595 | throw BinderException("Constraints on generated columns are not supported yet" ); |
596 | } |
597 | auto bound_info = BindCreateTableInfo(info: std::move(stmt.info)); |
598 | auto root = std::move(bound_info->query); |
599 | for (auto &fk_schema : fk_schemas) { |
600 | if (&fk_schema.get() != &bound_info->schema) { |
601 | throw BinderException("Creating foreign keys across different schemas or catalogs is not supported" ); |
602 | } |
603 | } |
604 | |
605 | // create the logical operator |
606 | auto &schema = bound_info->schema; |
607 | auto create_table = make_uniq<LogicalCreateTable>(args&: schema, args: std::move(bound_info)); |
608 | if (root) { |
609 | // CREATE TABLE AS |
610 | properties.return_type = StatementReturnType::CHANGED_ROWS; |
611 | create_table->children.push_back(x: std::move(root)); |
612 | } |
613 | result.plan = std::move(create_table); |
614 | break; |
615 | } |
616 | case CatalogType::TYPE_ENTRY: { |
617 | auto &schema = BindCreateSchema(info&: *stmt.info); |
618 | auto &create_type_info = stmt.info->Cast<CreateTypeInfo>(); |
619 | result.plan = make_uniq<LogicalCreate>(args: LogicalOperatorType::LOGICAL_CREATE_TYPE, args: std::move(stmt.info), args: &schema); |
620 | if (create_type_info.query) { |
621 | // CREATE TYPE mood AS ENUM (SELECT 'happy') |
622 | auto query_obj = Bind(statement&: *create_type_info.query); |
623 | auto query = std::move(query_obj.plan); |
624 | create_type_info.query.reset(); |
625 | |
626 | auto &sql_types = query_obj.types; |
627 | if (sql_types.size() != 1) { |
628 | // add cast expression? |
629 | throw BinderException("The query must return a single column" ); |
630 | } |
631 | if (sql_types[0].id() != LogicalType::VARCHAR) { |
632 | // push a projection casting to varchar |
633 | vector<unique_ptr<Expression>> select_list; |
634 | auto ref = make_uniq<BoundColumnRefExpression>(args&: sql_types[0], args&: query->GetColumnBindings()[0]); |
635 | auto cast_expr = BoundCastExpression::AddCastToType(context, expr: std::move(ref), target_type: LogicalType::VARCHAR); |
636 | select_list.push_back(x: std::move(cast_expr)); |
637 | auto proj = make_uniq<LogicalProjection>(args: GenerateTableIndex(), args: std::move(select_list)); |
638 | proj->AddChild(child: std::move(query)); |
639 | query = std::move(proj); |
640 | } |
641 | |
642 | result.plan->AddChild(child: std::move(query)); |
643 | } else if (create_type_info.type.id() == LogicalTypeId::USER) { |
644 | // two cases: |
645 | // 1: create a type with a non-existant type as source, catalog.GetType(...) will throw exception. |
646 | // 2: create a type alias with a custom type. |
647 | // eg. CREATE TYPE a AS INT; CREATE TYPE b AS a; |
648 | // We set b to be an alias for the underlying type of a |
649 | auto inner_type = Catalog::GetType(context, catalog_name: schema.catalog.GetName(), schema: schema.name, |
650 | name: UserType::GetTypeName(type: create_type_info.type)); |
651 | // clear to nullptr, we don't need this |
652 | EnumType::SetCatalog(type&: inner_type, catalog_entry: nullptr); |
653 | inner_type.SetAlias(create_type_info.name); |
654 | create_type_info.type = inner_type; |
655 | } |
656 | break; |
657 | } |
658 | default: |
659 | throw Exception("Unrecognized type!" ); |
660 | } |
661 | properties.return_type = StatementReturnType::NOTHING; |
662 | properties.allow_stream_result = false; |
663 | return result; |
664 | } |
665 | |
666 | } // namespace duckdb |
667 | |