diff --git a/datafusion/expr/src/expr_rewriter/mod.rs b/datafusion/expr/src/expr_rewriter/mod.rs index f5f49c643c285..a0faca76e91e4 100644 --- a/datafusion/expr/src/expr_rewriter/mod.rs +++ b/datafusion/expr/src/expr_rewriter/mod.rs @@ -260,7 +260,11 @@ fn coerce_exprs_for_schema( } #[expect(deprecated)] Expr::Wildcard { .. } => Ok(expr), - _ => expr.cast_to(new_type, src_schema), + _ => { + // maintain the original name when casting + let name = dst_schema.field(idx).name(); + Ok(expr.cast_to(new_type, src_schema)?.alias(name)) + } } } else { Ok(expr) diff --git a/datafusion/optimizer/src/analyzer/type_coercion.rs b/datafusion/optimizer/src/analyzer/type_coercion.rs index a557d3356dba0..85751fd70c05e 100644 --- a/datafusion/optimizer/src/analyzer/type_coercion.rs +++ b/datafusion/optimizer/src/analyzer/type_coercion.rs @@ -1305,7 +1305,7 @@ mod test { true, plan.clone(), @r" - Projection: CAST(a AS LargeUtf8) + Projection: CAST(a AS LargeUtf8) AS a EmptyRelation: rows=0 " )?; @@ -1341,7 +1341,7 @@ mod test { true, plan.clone(), @r" - Projection: CAST(a AS LargeUtf8) + Projection: CAST(a AS LargeUtf8) AS a EmptyRelation: rows=0 " )?; @@ -1371,7 +1371,7 @@ mod test { true, sort_plan.clone(), @r" - Projection: CAST(a AS LargeUtf8) + Projection: CAST(a AS LargeUtf8) AS a Sort: a ASC NULLS FIRST Projection: a EmptyRelation: rows=0 @@ -1400,7 +1400,7 @@ mod test { true, plan.clone(), @r" - Projection: CAST(a AS LargeUtf8) + Projection: CAST(a AS LargeUtf8) AS a Sort: a ASC NULLS FIRST Projection: a EmptyRelation: rows=0 @@ -1436,7 +1436,7 @@ mod test { true, plan.clone(), @r" - Projection: CAST(a AS LargeBinary) + Projection: CAST(a AS LargeBinary) AS a EmptyRelation: rows=0 " )?; @@ -1493,7 +1493,7 @@ mod test { true, sort_plan.clone(), @r" - Projection: CAST(a AS LargeBinary) + Projection: CAST(a AS LargeBinary) AS a Sort: a ASC NULLS FIRST Projection: a EmptyRelation: rows=0 @@ -1524,7 +1524,7 @@ mod test { true, plan.clone(), @r" - Projection: CAST(a AS LargeBinary) + Projection: CAST(a AS LargeBinary) AS a Sort: a ASC NULLS FIRST Projection: a EmptyRelation: rows=0 diff --git a/datafusion/optimizer/tests/optimizer_integration.rs b/datafusion/optimizer/tests/optimizer_integration.rs index 31ec026c1c8d6..180d85be20fb7 100644 --- a/datafusion/optimizer/tests/optimizer_integration.rs +++ b/datafusion/optimizer/tests/optimizer_integration.rs @@ -538,14 +538,15 @@ fn recursive_cte_projection_pushdown() -> Result<()> { // columns from the base table and recursive table, eliminating unused columns assert_snapshot!( format!("{plan}"), - @r#"SubqueryAlias: nodes - RecursiveQuery: is_distinct=false - Projection: test.col_int32 AS id - TableScan: test projection=[col_int32] - Projection: CAST(CAST(nodes.id AS Int64) + Int64(1) AS Int32) - Filter: nodes.id < Int32(3) - TableScan: nodes projection=[id] -"# + @r" + SubqueryAlias: nodes + RecursiveQuery: is_distinct=false + Projection: test.col_int32 AS id + TableScan: test projection=[col_int32] + Projection: CAST(CAST(nodes.id AS Int64) + Int64(1) AS Int32) AS id + Filter: nodes.id < Int32(3) + TableScan: nodes projection=[id] + " ); Ok(()) } @@ -561,14 +562,16 @@ fn recursive_cte_with_aliased_self_reference() -> Result<()> { assert_snapshot!( format!("{plan}"), - @r#"SubqueryAlias: nodes - RecursiveQuery: is_distinct=false - Projection: test.col_int32 AS id - TableScan: test projection=[col_int32] - Projection: CAST(CAST(child.id AS Int64) + Int64(1) AS Int32) - SubqueryAlias: child - Filter: nodes.id < Int32(3) - TableScan: nodes projection=[id]"#, + @r" + SubqueryAlias: nodes + RecursiveQuery: is_distinct=false + Projection: test.col_int32 AS id + TableScan: test projection=[col_int32] + Projection: CAST(CAST(child.id AS Int64) + Int64(1) AS Int32) AS id + SubqueryAlias: child + Filter: nodes.id < Int32(3) + TableScan: nodes projection=[id] + ", ); Ok(()) } @@ -620,15 +623,16 @@ fn recursive_cte_projection_pushdown_baseline() -> Result<()> { // and only the needed column is selected from the recursive table assert_snapshot!( format!("{plan}"), - @r#"SubqueryAlias: countdown - RecursiveQuery: is_distinct=false - Projection: test.col_int32 AS n - Filter: test.col_int32 = Int32(5) - TableScan: test projection=[col_int32] - Projection: CAST(CAST(countdown.n AS Int64) - Int64(1) AS Int32) - Filter: countdown.n > Int32(1) - TableScan: countdown projection=[n] -"# + @r" + SubqueryAlias: countdown + RecursiveQuery: is_distinct=false + Projection: test.col_int32 AS n + Filter: test.col_int32 = Int32(5) + TableScan: test projection=[col_int32] + Projection: CAST(CAST(countdown.n AS Int64) - Int64(1) AS Int32) AS n + Filter: countdown.n > Int32(1) + TableScan: countdown projection=[n] + " ); Ok(()) } diff --git a/datafusion/sqllogictest/test_files/cast.slt b/datafusion/sqllogictest/test_files/cast.slt index 3466354e54d71..916895b8be1eb 100644 --- a/datafusion/sqllogictest/test_files/cast.slt +++ b/datafusion/sqllogictest/test_files/cast.slt @@ -89,3 +89,39 @@ select * from t0 where v0<1e100; statement ok drop table t0; + + +# ensure that automatically casting with "datafusion.optimizer.expand_views_at_output" does not +# change the column name + +statement ok +create table t(a int, b varchar); + +statement ok +set datafusion.optimizer.expand_views_at_output = true; + +query TT +explain select * from t; +---- +logical_plan +01)Projection: t.a, CAST(t.b AS LargeUtf8) AS b +02)--TableScan: t projection=[a, b] +physical_plan +01)ProjectionExec: expr=[a@0 as a, CAST(b@1 AS LargeUtf8) as b] +02)--DataSourceExec: partitions=1, partition_sizes=[0] + +query TT +explain select b from t; +---- +logical_plan +01)Projection: CAST(t.b AS LargeUtf8) AS b +02)--TableScan: t projection=[b] +physical_plan +01)ProjectionExec: expr=[CAST(b@0 AS LargeUtf8) as b] +02)--DataSourceExec: partitions=1, partition_sizes=[0] + +statement ok +set datafusion.optimizer.expand_views_at_output = false; + +statement ok +drop table t;