diff --git a/src/Interpreters/inplaceBlockConversions.cpp b/src/Interpreters/inplaceBlockConversions.cpp index f90eb52cd632..cba6d145bb5b 100644 --- a/src/Interpreters/inplaceBlockConversions.cpp +++ b/src/Interpreters/inplaceBlockConversions.cpp @@ -167,10 +167,18 @@ ASTPtr convertRequiredExpressions(Block & block, const NamesAndTypesList & requi "Please specify `DEFAULT` expression in ALTER MODIFY COLUMN statement", required_column.name, column_in_block.type->getName(), required_column.type->getName()); - auto convert_func = makeASTFunction("_CAST", - makeASTFunction("ifNull", std::make_shared(required_column.name), default_value), + /// Cast the nullable column to Nullable(TargetType) first, then strip NULLs with ifNull. + /// This handles cross-type conversion (e.g. Nullable(UInt8) -> String) correctly, + /// because _CAST(Nullable(UInt8), 'Nullable(String)') -> Nullable(String), + /// and ifNull(Nullable(String), String) trivially resolves to String. + auto nullable_target_type_name = "Nullable(" + required_column.type->getName() + ")"; + auto cast_col = makeASTFunction("_CAST", + std::make_shared(required_column.name), + std::make_shared(nullable_target_type_name)); + auto cast_default = makeASTFunction("_CAST", + default_value, std::make_shared(required_column.type->getName())); - + auto convert_func = makeASTFunction("ifNull", std::move(cast_col), std::move(cast_default)); conversion_expr_list->children.emplace_back(setAlias(convert_func, required_column.name)); continue; } diff --git a/tests/queries/0_stateless/03575_modify_column_null_to_default_cross_type.reference b/tests/queries/0_stateless/03575_modify_column_null_to_default_cross_type.reference new file mode 100644 index 000000000000..3d59c6f1a4c9 --- /dev/null +++ b/tests/queries/0_stateless/03575_modify_column_null_to_default_cross_type.reference @@ -0,0 +1,6 @@ +\N a +42 b + a +42 b + a +42 b diff --git a/tests/queries/0_stateless/03575_modify_column_null_to_default_cross_type.sql b/tests/queries/0_stateless/03575_modify_column_null_to_default_cross_type.sql new file mode 100644 index 000000000000..647fd572b904 --- /dev/null +++ b/tests/queries/0_stateless/03575_modify_column_null_to_default_cross_type.sql @@ -0,0 +1,14 @@ +-- Tags: no-random-settings, no-random-merge-tree-settings + +-- Test cross-type Nullable(X) -> Y conversion (e.g. Nullable(UInt8) -> String) +-- Follow-up to PR #84770 + +DROP TABLE IF EXISTS nullable_cross_type_test; +CREATE TABLE nullable_cross_type_test (x Nullable(UInt8), y String) ORDER BY tuple(); +INSERT INTO nullable_cross_type_test VALUES (NULL, 'a'), (42, 'b'); +SELECT * FROM nullable_cross_type_test ORDER BY y; +ALTER TABLE nullable_cross_type_test MODIFY COLUMN x String DEFAULT ''; +SELECT * FROM nullable_cross_type_test ORDER BY y; +OPTIMIZE TABLE nullable_cross_type_test FINAL; +SELECT * FROM nullable_cross_type_test ORDER BY y; +DROP TABLE nullable_cross_type_test;