@@ -633,8 +633,43 @@ impl LogicalPlan {
633633 LogicalPlan :: Dml ( _) => Ok ( self ) ,
634634 LogicalPlan :: Copy ( _) => Ok ( self ) ,
635635 LogicalPlan :: Values ( Values { schema, values } ) => {
636- // todo it isn't clear why the schema is not recomputed here
637- Ok ( LogicalPlan :: Values ( Values { schema, values } ) )
636+ // Using `values` alone cannot compute correct schema for the plan. For example:
637+ // Projection: col_1, col_2
638+ // Values: (Float32(1), Float32(10)), (Float32(100), Float32(10))
639+ //
640+ // Thus, we need to recompute a new schema from `values` and retain some
641+ // information from the original schema.
642+ let new_plan = LogicalPlanBuilder :: values ( values. clone ( ) ) ?. build ( ) ?;
643+
644+ let qualified_fields = schema
645+ . iter ( )
646+ . zip ( new_plan. schema ( ) . fields ( ) )
647+ . map ( |( ( table_ref, old_field) , new_field) | {
648+ // `old_field`'s data type is unknown but `new_field`'s is known
649+ if old_field. data_type ( ) . is_null ( )
650+ && !new_field. data_type ( ) . is_null ( )
651+ {
652+ let field = old_field
653+ . as_ref ( )
654+ . clone ( )
655+ . with_data_type ( new_field. data_type ( ) . clone ( ) ) ;
656+ ( table_ref. cloned ( ) , Arc :: new ( field) )
657+ } else {
658+ ( table_ref. cloned ( ) , Arc :: clone ( old_field) )
659+ }
660+ } )
661+ . collect :: < Vec < _ > > ( ) ;
662+
663+ let schema = DFSchema :: new_with_metadata (
664+ qualified_fields,
665+ schema. metadata ( ) . clone ( ) ,
666+ ) ?
667+ . with_functional_dependencies ( schema. functional_dependencies ( ) . clone ( ) ) ?;
668+
669+ Ok ( LogicalPlan :: Values ( Values {
670+ schema : Arc :: new ( schema) ,
671+ values,
672+ } ) )
638673 }
639674 LogicalPlan :: Filter ( Filter { predicate, input } ) => {
640675 Filter :: try_new ( predicate, input) . map ( LogicalPlan :: Filter )
@@ -1471,7 +1506,10 @@ impl LogicalPlan {
14711506 // Preserve name to avoid breaking column references to this expression
14721507 Ok ( transformed_expr. update_data ( |expr| original_name. restore ( expr) ) )
14731508 }
1474- } )
1509+ } ) ?
1510+ // always recompute the schema to ensure the changed in the schema's field should be
1511+ // poplulated to the plan's parent
1512+ . map_data ( |plan| plan. recompute_schema ( ) )
14751513 } )
14761514 . map ( |res| res. data )
14771515 }
@@ -4247,6 +4285,7 @@ mod tests {
42474285 use super :: * ;
42484286 use crate :: builder:: LogicalTableSource ;
42494287 use crate :: logical_plan:: table_scan;
4288+ use crate :: select_expr:: SelectExpr ;
42504289 use crate :: test:: function_stub:: { count, count_udaf} ;
42514290 use crate :: {
42524291 binary_expr, col, exists, in_subquery, lit, placeholder, scalar_subquery,
@@ -4825,6 +4864,85 @@ mod tests {
48254864 . expect_err ( "prepared field metadata mismatch unexpectedly succeeded" ) ;
48264865 }
48274866
4867+ #[ test]
4868+ fn test_replace_placeholder_values_relation_valid_schema ( ) {
4869+ // SELECT a, b, c, d FROM (VALUES (1), ($1), ($2), ($3 + $4)) AS t(a, b, c, d);
4870+ let plan = LogicalPlanBuilder :: values ( vec ! [ vec![
4871+ lit( 1 ) ,
4872+ placeholder( "$1" ) ,
4873+ placeholder( "$2" ) ,
4874+ binary_expr( placeholder( "$3" ) , Operator :: Plus , placeholder( "$4" ) ) ,
4875+ ] ] )
4876+ . unwrap ( )
4877+ . project ( vec ! [
4878+ col( "column1" ) . alias( "a" ) ,
4879+ col( "column2" ) . alias( "b" ) ,
4880+ col( "column3" ) . alias( "c" ) ,
4881+ col( "column4" ) . alias( "d" ) ,
4882+ ] )
4883+ . unwrap ( )
4884+ . alias ( "t" )
4885+ . unwrap ( )
4886+ . project ( vec ! [ col( "a" ) , col( "b" ) , col( "c" ) , col( "d" ) ] )
4887+ . unwrap ( )
4888+ . build ( )
4889+ . unwrap ( ) ;
4890+
4891+ // original
4892+ assert_snapshot ! ( plan. display_indent_schema( ) , @r"
4893+ Projection: t.a, t.b, t.c, t.d [a:Int32;N, b:Null;N, c:Null;N, d:Int64;N]
4894+ SubqueryAlias: t [a:Int32;N, b:Null;N, c:Null;N, d:Int64;N]
4895+ Projection: column1 AS a, column2 AS b, column3 AS c, column4 AS d [a:Int32;N, b:Null;N, c:Null;N, d:Int64;N]
4896+ Values: (Int32(1), $1, $2, $3 + $4) [column1:Int32;N, column2:Null;N, column3:Null;N, column4:Int64;N]
4897+ " ) ;
4898+
4899+ let plan = plan
4900+ . with_param_values ( vec ! [
4901+ ScalarValue :: from( 1i32 ) ,
4902+ ScalarValue :: from( "s" ) ,
4903+ ScalarValue :: from( 3 ) ,
4904+ ScalarValue :: from( 4 ) ,
4905+ ] )
4906+ . unwrap ( ) ;
4907+
4908+ // replaced
4909+ assert_snapshot ! ( plan. display_indent_schema( ) , @r#"
4910+ Projection: t.a, t.b, t.c, t.d [a:Int32;N, b:Int32;N, c:Utf8;N, d:Int64;N]
4911+ SubqueryAlias: t [a:Int32;N, b:Int32;N, c:Utf8;N, d:Int64;N]
4912+ Projection: column1 AS a, column2 AS b, column3 AS c, column4 AS d [a:Int32;N, b:Int32;N, c:Utf8;N, d:Int64;N]
4913+ Values: (Int32(1), Int32(1) AS $1, Utf8("s") AS $2, Int32(3) + Int32(4) AS $3 + $4) [column1:Int32;N, column2:Int32;N, column3:Utf8;N, column4:Int64;N]
4914+ "# ) ;
4915+ }
4916+
4917+ #[ test]
4918+ fn test_replace_placeholder_empty_relation_valid_schema ( ) {
4919+ // SELECT $1, $2;
4920+ let plan = LogicalPlanBuilder :: empty ( false )
4921+ . project ( vec ! [
4922+ SelectExpr :: from( placeholder( "$1" ) ) ,
4923+ SelectExpr :: from( placeholder( "$2" ) ) ,
4924+ ] )
4925+ . unwrap ( )
4926+ . build ( )
4927+ . unwrap ( ) ;
4928+
4929+ // original
4930+ assert_snapshot ! ( plan. display_indent_schema( ) , @r"
4931+ Projection: $1, $2 [$1:Null;N, $2:Null;N]
4932+ EmptyRelation: rows=0 []
4933+ " ) ;
4934+
4935+ let plan = plan
4936+ . with_param_values ( vec ! [ ScalarValue :: from( 1i32 ) , ScalarValue :: from( "s" ) ] )
4937+ . unwrap ( ) ;
4938+
4939+ // replaced
4940+ assert_snapshot ! ( plan. display_indent_schema( ) , @r#"
4941+ Projection: Int32(1) AS $1, Utf8("s") AS $2 [$1:Int32, $2:Utf8]
4942+ EmptyRelation: rows=0 []
4943+ "# ) ;
4944+ }
4945+
48284946 #[ test]
48294947 fn test_nullable_schema_after_grouping_set ( ) {
48304948 let schema = Schema :: new ( vec ! [
0 commit comments