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
43namespace duckdb {
44
45void 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
67void Binder::BindSchemaOrCatalog(string &catalog, string &schema) {
68 BindSchemaOrCatalog(context, catalog, schema);
69}
70
71SchemaCatalogEntry &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
109SchemaCatalogEntry &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
117void 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
137static 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
164SchemaCatalogEntry &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
212void 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
283static 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
356static 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
373static 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
386void 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
403static 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
462unique_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
492BoundStatement 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