20
20
#include " duckdb/planner/operator/logical_projection.hpp"
21
21
#include " duckdb/planner/operator/logical_update.hpp"
22
22
#include " duckdb/planner/parsed_data/bound_create_table_info.hpp"
23
+ #include " duckdb/parser/expression/function_expression.hpp"
23
24
#include " duckdb/storage/storage_manager.hpp"
24
25
#include " duckdb/storage/table_storage_info.hpp"
25
26
@@ -180,6 +181,10 @@ unique_ptr<CatalogEntry> DuckTableEntry::AlterEntry(ClientContext &context, Alte
180
181
auto &rename_info = table_info.Cast <RenameColumnInfo>();
181
182
return RenameColumn (context, rename_info);
182
183
}
184
+ case AlterTableType::RENAME_FIELD: {
185
+ auto &rename_info = table_info.Cast <RenameFieldInfo>();
186
+ return RenameField (context, rename_info);
187
+ }
183
188
case AlterTableType::RENAME_TABLE: {
184
189
auto &rename_info = table_info.Cast <RenameTableInfo>();
185
190
auto copied_table = Copy (context);
@@ -191,10 +196,18 @@ unique_ptr<CatalogEntry> DuckTableEntry::AlterEntry(ClientContext &context, Alte
191
196
auto &add_info = table_info.Cast <AddColumnInfo>();
192
197
return AddColumn (context, add_info);
193
198
}
199
+ case AlterTableType::ADD_FIELD: {
200
+ auto &add_info = table_info.Cast <AddFieldInfo>();
201
+ return AddField (context, add_info);
202
+ }
194
203
case AlterTableType::REMOVE_COLUMN: {
195
204
auto &remove_info = table_info.Cast <RemoveColumnInfo>();
196
205
return RemoveColumn (context, remove_info);
197
206
}
207
+ case AlterTableType::REMOVE_FIELD: {
208
+ auto &remove_info = table_info.Cast <RemoveFieldInfo>();
209
+ return RemoveField (context, remove_info);
210
+ }
198
211
case AlterTableType::SET_DEFAULT: {
199
212
auto &set_default_info = table_info.Cast <SetDefaultInfo>();
200
213
return SetDefault (context, set_default_info);
@@ -361,6 +374,119 @@ unique_ptr<CatalogEntry> DuckTableEntry::AddColumn(ClientContext &context, AddCo
361
374
return make_uniq<DuckTableEntry>(catalog, schema, *bound_create_info, new_storage);
362
375
}
363
376
377
+ struct StructMappingInfo {
378
+ LogicalType new_type;
379
+ unique_ptr<ParsedExpression> default_value;
380
+ ErrorData error;
381
+ };
382
+
383
+ unique_ptr<ParsedExpression> PackExpression (unique_ptr<ParsedExpression> expr, string name) {
384
+ expr->SetAlias (std::move (name));
385
+ vector<unique_ptr<ParsedExpression>> children;
386
+ children.push_back (std::move (expr));
387
+ auto res = make_uniq<FunctionExpression>(" struct_pack" , std::move (children));
388
+ return std::move (res);
389
+ }
390
+
391
+ Value ConstructMapping (const string &name, const LogicalType &type) {
392
+ if (type.id () != LogicalTypeId::STRUCT) {
393
+ return Value (name);
394
+ }
395
+ child_list_t <Value> child_mapping;
396
+ auto &child_types = StructType::GetChildTypes (type);
397
+ for (auto &entry : child_types) {
398
+ auto mapping_value = ConstructMapping (entry.first , entry.second );
399
+ if (entry.second .id () == LogicalTypeId::STRUCT) {
400
+ child_list_t <Value> child_values;
401
+ child_values.emplace_back (string (), Value (entry.first ));
402
+ child_values.emplace_back (string (), std::move (mapping_value));
403
+ mapping_value = Value::STRUCT (std::move (child_values));
404
+ }
405
+ child_mapping.emplace_back (entry.first , std::move (mapping_value));
406
+ }
407
+ return Value::STRUCT (std::move (child_mapping));
408
+ }
409
+
410
+ StructMappingInfo AddFieldToStruct (const LogicalType &type, const vector<string> &column_path,
411
+ const ColumnDefinition &new_field, idx_t depth = 0 ) {
412
+ if (type.id () != LogicalTypeId::STRUCT) {
413
+ throw BinderException (" Column %s is not a struct - ALTER TABLE can only add fields to structs" ,
414
+ column_path[depth]);
415
+ }
416
+ StructMappingInfo result;
417
+ auto child_list = StructType::GetChildTypes (type);
418
+ if (column_path.size () == depth + 1 ) {
419
+ // root path - we are adding at this level
420
+ // check if a field with this name already exists
421
+ for (auto &entry : child_list) {
422
+ if (StringUtil::CIEquals (entry.first , new_field.Name ())) {
423
+ // already exists!
424
+ result.error = ErrorData (CatalogException (" Duplicate field \" %s\" - field already exists in struct %s" ,
425
+ new_field.Name (), column_path.back ()));
426
+ return result;
427
+ }
428
+ }
429
+ // add the new type
430
+ child_list.emplace_back (new_field.Name (), new_field.Type ());
431
+ result.new_type = LogicalType::STRUCT (std::move (child_list));
432
+ // set the default value
433
+ unique_ptr<ParsedExpression> default_value;
434
+ if (new_field.HasDefaultValue ()) {
435
+ default_value = new_field.DefaultValue ().Copy ();
436
+ } else {
437
+ default_value = make_uniq<ConstantExpression>(Value (new_field.Type ()));
438
+ }
439
+ result.default_value = PackExpression (std::move (default_value), new_field.Name ());
440
+ return result;
441
+ }
442
+ // not the root path - we need to recurse
443
+ auto &next_component = column_path[depth + 1 ];
444
+ bool found = false ;
445
+ for (auto &entry : child_list) {
446
+ if (StringUtil::CIEquals (entry.first , next_component)) {
447
+ // found the entry - recurse
448
+ auto child_res = AddFieldToStruct (entry.second , column_path, new_field, depth + 1 );
449
+ if (child_res.error .HasError ()) {
450
+ return child_res;
451
+ }
452
+ entry.second = std::move (child_res.new_type );
453
+ result.default_value = PackExpression (std::move (child_res.default_value ), entry.first );
454
+ found = true ;
455
+ break ;
456
+ }
457
+ }
458
+ result.new_type = LogicalType::STRUCT (std::move (child_list));
459
+ if (!found) {
460
+ throw BinderException (" Sub-field %s does not exist in column %s" , next_component, column_path[depth]);
461
+ }
462
+ return result;
463
+ }
464
+
465
+ unique_ptr<CatalogEntry> DuckTableEntry::AddField (ClientContext &context, AddFieldInfo &info) {
466
+ // follow the path
467
+ auto &col = GetColumn (info.column_path [0 ]);
468
+ auto res = AddFieldToStruct (col.Type (), info.column_path , info.new_field );
469
+ if (res.error .HasError ()) {
470
+ if (!info.if_field_not_exists ) {
471
+ res.error .Throw ();
472
+ }
473
+ return nullptr ;
474
+ }
475
+
476
+ // construct the struct remapping expression
477
+ vector<unique_ptr<ParsedExpression>> children;
478
+ children.push_back (make_uniq<ColumnRefExpression>(info.column_path [0 ]));
479
+ children.push_back (make_uniq<ConstantExpression>(Value (res.new_type )));
480
+ children.push_back (make_uniq<ConstantExpression>(ConstructMapping (col.Name (), col.Type ())));
481
+ children.push_back (std::move (res.default_value ));
482
+
483
+ auto function = make_uniq<FunctionExpression>(" remap_struct" , std::move (children));
484
+
485
+ ChangeColumnTypeInfo change_column_type (info.GetAlterEntryData (), info.column_path [0 ], std::move (res.new_type ),
486
+ std::move (function));
487
+ return ChangeColumnType (context, change_column_type);
488
+ }
489
+
364
490
void DuckTableEntry::UpdateConstraintsOnColumnDrop (const LogicalIndex &removed_index,
365
491
const vector<LogicalIndex> &adjusted_indices,
366
492
const RemoveColumnInfo &info, CreateTableInfo &create_info,
@@ -501,6 +627,190 @@ unique_ptr<CatalogEntry> DuckTableEntry::RemoveColumn(ClientContext &context, Re
501
627
return make_uniq<DuckTableEntry>(catalog, schema, *bound_create_info, new_storage);
502
628
}
503
629
630
+ struct DroppedFieldMapping {
631
+ Value mapping;
632
+ LogicalType new_type;
633
+ ErrorData error;
634
+ };
635
+
636
+ DroppedFieldMapping DropFieldFromStruct (const LogicalType &type, const vector<string> &column_path, idx_t depth) {
637
+ if (type.id () != LogicalTypeId::STRUCT) {
638
+ throw CatalogException (" Cannot drop field from column \" %s\" - not a struct" , column_path[0 ]);
639
+ }
640
+ auto &dropped_entry = column_path[depth];
641
+ bool last_entry = depth + 1 == column_path.size ();
642
+ bool found = false ;
643
+ DroppedFieldMapping result;
644
+ child_list_t <Value> child_mapping;
645
+ child_list_t <LogicalType> new_type_children;
646
+ auto &child_types = StructType::GetChildTypes (type);
647
+ for (auto &entry : child_types) {
648
+ Value mapping_value;
649
+ LogicalType type_value;
650
+ if (StringUtil::CIEquals (entry.first , dropped_entry)) {
651
+ // this is the entry we are dropping
652
+ found = true ;
653
+ if (last_entry) {
654
+ // we are dropping this entry in its entirety - just skip
655
+ if (child_types.size () == 1 ) {
656
+ throw CatalogException (" Cannot drop field %s from column %s - it is the last field of the struct" ,
657
+ column_path.back (), column_path.front ());
658
+ }
659
+ continue ;
660
+ } else {
661
+ // we are dropping a field in this entry - recurse
662
+ auto child_result = DropFieldFromStruct (entry.second , column_path, depth + 1 );
663
+ if (child_result.error .HasError ()) {
664
+ // bubble up error
665
+ return child_result;
666
+ }
667
+ mapping_value = std::move (child_result.mapping );
668
+ type_value = std::move (child_result.new_type );
669
+ }
670
+ } else {
671
+ // we are not adjusting this entry - copy the type and create a straightforward mapping
672
+ mapping_value = ConstructMapping (entry.first , entry.second );
673
+ type_value = entry.second ;
674
+ }
675
+ if (entry.second .id () == LogicalTypeId::STRUCT) {
676
+ child_list_t <Value> child_values;
677
+ child_values.emplace_back (string (), Value (entry.first ));
678
+ child_values.emplace_back (string (), std::move (mapping_value));
679
+ mapping_value = Value::STRUCT (std::move (child_values));
680
+ }
681
+ child_mapping.emplace_back (entry.first , std::move (mapping_value));
682
+ new_type_children.emplace_back (entry.first , type_value);
683
+ }
684
+ if (!found) {
685
+ result.error = ErrorData (CatalogException (" Cannot drop field \" %s\" - it does not exist" , dropped_entry));
686
+ } else {
687
+ result.mapping = Value::STRUCT (std::move (child_mapping));
688
+ result.new_type = LogicalType::STRUCT (std::move (new_type_children));
689
+ }
690
+ return result;
691
+ }
692
+
693
+ unique_ptr<CatalogEntry> DuckTableEntry::RemoveField (ClientContext &context, RemoveFieldInfo &info) {
694
+ if (!ColumnExists (info.column_path [0 ])) {
695
+ if (!info.if_column_exists ) {
696
+ throw CatalogException (" Cannot drop field from column \" %s\" - it does not exist" , info.column_path [0 ]);
697
+ }
698
+ return nullptr ;
699
+ }
700
+ // follow the path
701
+ auto &col = GetColumn (info.column_path [0 ]);
702
+ auto res = DropFieldFromStruct (col.Type (), info.column_path , 1 );
703
+ if (res.error .HasError ()) {
704
+ if (!info.if_column_exists ) {
705
+ res.error .Throw ();
706
+ }
707
+ return nullptr ;
708
+ }
709
+
710
+ // construct the struct remapping expression
711
+ vector<unique_ptr<ParsedExpression>> children;
712
+ children.push_back (make_uniq<ColumnRefExpression>(info.column_path [0 ]));
713
+ children.push_back (make_uniq<ConstantExpression>(Value (res.new_type )));
714
+ children.push_back (make_uniq<ConstantExpression>(std::move (res.mapping )));
715
+ children.push_back (make_uniq<ConstantExpression>(Value ()));
716
+
717
+ auto function = make_uniq<FunctionExpression>(" remap_struct" , std::move (children));
718
+
719
+ ChangeColumnTypeInfo change_column_type (info.GetAlterEntryData (), info.column_path [0 ], std::move (res.new_type ),
720
+ std::move (function));
721
+ return ChangeColumnType (context, change_column_type);
722
+ }
723
+
724
+ DroppedFieldMapping RenameFieldFromStruct (const LogicalType &type, const vector<string> &column_path,
725
+ const string &new_name, idx_t depth) {
726
+ if (type.id () != LogicalTypeId::STRUCT) {
727
+ throw CatalogException (" Cannot rename field from column \" %s\" - not a struct" , column_path[0 ]);
728
+ }
729
+ auto &rename_entry = column_path[depth];
730
+ bool last_entry = depth + 1 == column_path.size ();
731
+ bool found = false ;
732
+ DroppedFieldMapping result;
733
+ child_list_t <Value> child_mapping;
734
+ child_list_t <LogicalType> new_type_children;
735
+ auto &child_types = StructType::GetChildTypes (type);
736
+ for (auto &entry : child_types) {
737
+ auto field_name = entry.first ;
738
+ Value mapping_value;
739
+ LogicalType type_value;
740
+ if (StringUtil::CIEquals (field_name, rename_entry)) {
741
+ // this is the entry we are dropping
742
+ found = true ;
743
+ if (last_entry) {
744
+ // we are renaming this entry
745
+ for (auto &sub_entry : child_types) {
746
+ if (StringUtil::CIEquals (new_name, sub_entry.first )) {
747
+ throw CatalogException (
748
+ " Cannot rename field %s from column %s to %s - a field with this name already exists" ,
749
+ column_path.back (), column_path.front (), new_name);
750
+ }
751
+ }
752
+ field_name = new_name;
753
+ mapping_value = ConstructMapping (entry.first , entry.second );
754
+ type_value = entry.second ;
755
+ } else {
756
+ // we are dropping a field in this entry - recurse
757
+ auto child_result = RenameFieldFromStruct (entry.second , column_path, new_name, depth + 1 );
758
+ if (child_result.error .HasError ()) {
759
+ // bubble up error
760
+ return child_result;
761
+ }
762
+ mapping_value = std::move (child_result.mapping );
763
+ type_value = std::move (child_result.new_type );
764
+ }
765
+ } else {
766
+ // we are not adjusting this entry - copy the type and create a straightforward mapping
767
+ mapping_value = ConstructMapping (entry.first , entry.second );
768
+ type_value = entry.second ;
769
+ }
770
+ if (entry.second .id () == LogicalTypeId::STRUCT) {
771
+ child_list_t <Value> child_values;
772
+ child_values.emplace_back (string (), Value (entry.first ));
773
+ child_values.emplace_back (string (), std::move (mapping_value));
774
+ mapping_value = Value::STRUCT (std::move (child_values));
775
+ }
776
+ child_mapping.emplace_back (field_name, std::move (mapping_value));
777
+ new_type_children.emplace_back (field_name, type_value);
778
+ }
779
+ if (!found) {
780
+ result.error = ErrorData (CatalogException (" Cannot rename field \" %s\" - it does not exist" , rename_entry));
781
+ } else {
782
+ result.mapping = Value::STRUCT (std::move (child_mapping));
783
+ result.new_type = LogicalType::STRUCT (std::move (new_type_children));
784
+ }
785
+ return result;
786
+ }
787
+
788
+ unique_ptr<CatalogEntry> DuckTableEntry::RenameField (ClientContext &context, RenameFieldInfo &info) {
789
+ if (!ColumnExists (info.column_path [0 ])) {
790
+ throw CatalogException (" Cannot rename field from column \" %s\" - it does not exist" , info.column_path [0 ]);
791
+ }
792
+ // follow the path
793
+ auto &col = GetColumn (info.column_path [0 ]);
794
+ auto res = RenameFieldFromStruct (col.Type (), info.column_path , info.new_name , 1 );
795
+ if (res.error .HasError ()) {
796
+ res.error .Throw ();
797
+ return nullptr ;
798
+ }
799
+
800
+ // construct the struct remapping expression
801
+ vector<unique_ptr<ParsedExpression>> children;
802
+ children.push_back (make_uniq<ColumnRefExpression>(info.column_path [0 ]));
803
+ children.push_back (make_uniq<ConstantExpression>(Value (res.new_type )));
804
+ children.push_back (make_uniq<ConstantExpression>(std::move (res.mapping )));
805
+ children.push_back (make_uniq<ConstantExpression>(Value ()));
806
+
807
+ auto function = make_uniq<FunctionExpression>(" remap_struct" , std::move (children));
808
+
809
+ ChangeColumnTypeInfo change_column_type (info.GetAlterEntryData (), info.column_path [0 ], std::move (res.new_type ),
810
+ std::move (function));
811
+ return ChangeColumnType (context, change_column_type);
812
+ }
813
+
504
814
unique_ptr<CatalogEntry> DuckTableEntry::SetDefault (ClientContext &context, SetDefaultInfo &info) {
505
815
auto create_info = make_uniq<CreateTableInfo>(schema, name);
506
816
create_info->comment = comment;
0 commit comments