Skip to content

Commit ed0cac2

Browse files
authored
Merge branch 'main' into spark_path_option
2 parents 2f76bcd + c0e947f commit ed0cac2

File tree

23 files changed

+10873
-9930
lines changed

23 files changed

+10873
-9930
lines changed

.github/workflows/Java.yml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -276,10 +276,7 @@ jobs:
276276
jdbc-compliance:
277277
name: JDBC Compliance
278278
runs-on: ubuntu-20.04
279-
# temporary disabled until jdbccts is fixed
280-
# https://github.com/cwida/jdbccts/pull/1
281-
if: ${{ false }}
282-
#if: ${{ inputs.skip_tests != 'true' }}
279+
if: ${{ inputs.skip_tests != 'true' }}
283280
needs: java-linux-amd64
284281
container: quay.io/pypa/manylinux_2_28_x86_64
285282
env:

src/duckdb/src/catalog/catalog_entry/duck_table_entry.cpp

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "duckdb/planner/operator/logical_projection.hpp"
2121
#include "duckdb/planner/operator/logical_update.hpp"
2222
#include "duckdb/planner/parsed_data/bound_create_table_info.hpp"
23+
#include "duckdb/parser/expression/function_expression.hpp"
2324
#include "duckdb/storage/storage_manager.hpp"
2425
#include "duckdb/storage/table_storage_info.hpp"
2526

@@ -180,6 +181,10 @@ unique_ptr<CatalogEntry> DuckTableEntry::AlterEntry(ClientContext &context, Alte
180181
auto &rename_info = table_info.Cast<RenameColumnInfo>();
181182
return RenameColumn(context, rename_info);
182183
}
184+
case AlterTableType::RENAME_FIELD: {
185+
auto &rename_info = table_info.Cast<RenameFieldInfo>();
186+
return RenameField(context, rename_info);
187+
}
183188
case AlterTableType::RENAME_TABLE: {
184189
auto &rename_info = table_info.Cast<RenameTableInfo>();
185190
auto copied_table = Copy(context);
@@ -191,10 +196,18 @@ unique_ptr<CatalogEntry> DuckTableEntry::AlterEntry(ClientContext &context, Alte
191196
auto &add_info = table_info.Cast<AddColumnInfo>();
192197
return AddColumn(context, add_info);
193198
}
199+
case AlterTableType::ADD_FIELD: {
200+
auto &add_info = table_info.Cast<AddFieldInfo>();
201+
return AddField(context, add_info);
202+
}
194203
case AlterTableType::REMOVE_COLUMN: {
195204
auto &remove_info = table_info.Cast<RemoveColumnInfo>();
196205
return RemoveColumn(context, remove_info);
197206
}
207+
case AlterTableType::REMOVE_FIELD: {
208+
auto &remove_info = table_info.Cast<RemoveFieldInfo>();
209+
return RemoveField(context, remove_info);
210+
}
198211
case AlterTableType::SET_DEFAULT: {
199212
auto &set_default_info = table_info.Cast<SetDefaultInfo>();
200213
return SetDefault(context, set_default_info);
@@ -361,6 +374,119 @@ unique_ptr<CatalogEntry> DuckTableEntry::AddColumn(ClientContext &context, AddCo
361374
return make_uniq<DuckTableEntry>(catalog, schema, *bound_create_info, new_storage);
362375
}
363376

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+
364490
void DuckTableEntry::UpdateConstraintsOnColumnDrop(const LogicalIndex &removed_index,
365491
const vector<LogicalIndex> &adjusted_indices,
366492
const RemoveColumnInfo &info, CreateTableInfo &create_info,
@@ -501,6 +627,190 @@ unique_ptr<CatalogEntry> DuckTableEntry::RemoveColumn(ClientContext &context, Re
501627
return make_uniq<DuckTableEntry>(catalog, schema, *bound_create_info, new_storage);
502628
}
503629

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+
504814
unique_ptr<CatalogEntry> DuckTableEntry::SetDefault(ClientContext &context, SetDefaultInfo &info) {
505815
auto create_info = make_uniq<CreateTableInfo>(schema, name);
506816
create_info->comment = comment;

src/duckdb/src/common/enum_util.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -391,19 +391,22 @@ const StringUtil::EnumStringLiteral *GetAlterTableTypeValues() {
391391
{ static_cast<uint32_t>(AlterTableType::SET_COLUMN_COMMENT), "SET_COLUMN_COMMENT" },
392392
{ static_cast<uint32_t>(AlterTableType::ADD_CONSTRAINT), "ADD_CONSTRAINT" },
393393
{ static_cast<uint32_t>(AlterTableType::SET_PARTITIONED_BY), "SET_PARTITIONED_BY" },
394-
{ static_cast<uint32_t>(AlterTableType::SET_SORTED_BY), "SET_SORTED_BY" }
394+
{ static_cast<uint32_t>(AlterTableType::SET_SORTED_BY), "SET_SORTED_BY" },
395+
{ static_cast<uint32_t>(AlterTableType::ADD_FIELD), "ADD_FIELD" },
396+
{ static_cast<uint32_t>(AlterTableType::REMOVE_FIELD), "REMOVE_FIELD" },
397+
{ static_cast<uint32_t>(AlterTableType::RENAME_FIELD), "RENAME_FIELD" }
395398
};
396399
return values;
397400
}
398401

399402
template<>
400403
const char* EnumUtil::ToChars<AlterTableType>(AlterTableType value) {
401-
return StringUtil::EnumToString(GetAlterTableTypeValues(), 14, "AlterTableType", static_cast<uint32_t>(value));
404+
return StringUtil::EnumToString(GetAlterTableTypeValues(), 17, "AlterTableType", static_cast<uint32_t>(value));
402405
}
403406

404407
template<>
405408
AlterTableType EnumUtil::FromString<AlterTableType>(const char *value) {
406-
return static_cast<AlterTableType>(StringUtil::StringToEnum(GetAlterTableTypeValues(), 14, "AlterTableType", value));
409+
return static_cast<AlterTableType>(StringUtil::StringToEnum(GetAlterTableTypeValues(), 17, "AlterTableType", value));
407410
}
408411

409412
const StringUtil::EnumStringLiteral *GetAlterTypeValues() {

0 commit comments

Comments
 (0)