@@ -58,6 +58,7 @@ use data_engine_expressions::{
5858 StaticScalarExpression , StringScalarExpression , StringValue , TextScalarExpression ,
5959} ;
6060use datafusion:: common:: DFSchema ;
61+ use datafusion:: functions:: core:: coalesce:: CoalesceFunc ;
6162use datafusion:: functions:: core:: expr_ext:: FieldAccessor ;
6263use datafusion:: functions:: crypto:: { md5, sha256, sha512} ;
6364use datafusion:: functions:: datetime:: to_char;
@@ -66,8 +67,9 @@ use datafusion::functions::encoding::encode;
6667use datafusion:: functions:: math:: log10;
6768use datafusion:: functions:: string:: { concat, concat_ws, lower, ltrim, replace, rtrim, upper, uuid} ;
6869use datafusion:: logical_expr:: expr:: ScalarFunction ;
70+ use datafusion:: logical_expr:: simplify:: { ExprSimplifyResult , SimplifyContext } ;
6971use datafusion:: logical_expr:: {
70- BinaryExpr , ColumnarValue , Expr , Operator , ScalarUDF , cast, col, lit, when ,
72+ BinaryExpr , ColumnarValue , Expr , Operator , ScalarUDF , ScalarUDFImpl , cast, col, lit,
7173} ;
7274use datafusion:: physical_expr:: { PhysicalExprRef , create_physical_expr} ;
7375use datafusion:: prelude:: SessionContext ;
@@ -426,10 +428,21 @@ impl ExprLogicalPlanner {
426428 let ( df_args, source_scope, _requires_dict_downcast) =
427429 self . plan_function_args ( coalesce_expr. get_expressions ( ) . iter ( ) , functions) ?;
428430
429- // DataFusion's `coalesce` UDF is simplified to CASE during logical optimization and does not
430- // support direct physical evaluation. Mirror that rewrite here (same shape as
431- // `CoalesceFunc::simplify` in Apache DataFusion).
432- let case_expr = Self :: coalesce_args_to_case_expr ( df_args) ?;
431+ // DataFusion's `coalesce` UDF does not support direct physical evaluation; the optimizer
432+ // rewrites it via `CoalesceFunc::simplify`. Reuse that implementation here.
433+ let coalesce_func = CoalesceFunc :: new ( ) ;
434+ let simplify_result = coalesce_func
435+ . simplify ( df_args, & SimplifyContext :: default ( ) )
436+ . map_err ( Error :: from) ?;
437+ let case_expr = match simplify_result {
438+ ExprSimplifyResult :: Simplified ( expr) => expr,
439+ ExprSimplifyResult :: Original ( _) => {
440+ return Err ( Error :: InvalidPipelineError {
441+ cause : "expected coalesce simplify to produce a single expression" . into ( ) ,
442+ query_location : None ,
443+ } ) ;
444+ }
445+ } ;
433446
434447 Ok ( ScopedLogicalExpr {
435448 logical_expr : case_expr,
@@ -441,37 +454,6 @@ impl ExprLogicalPlanner {
441454 } )
442455 }
443456
444- /// Rewrites `coalesce(e1, …, eN)` to chained `WHEN eK IS NOT NULL THEN eK … ELSE eN`,
445- /// matching DataFusion's `coalesce` simplification.
446- fn coalesce_args_to_case_expr ( mut args : Vec < Expr > ) -> Result < Expr > {
447- match args. len ( ) {
448- 0 => Err ( Error :: InvalidPipelineError {
449- cause : "coalesce requires at least one argument" . into ( ) ,
450- query_location : None ,
451- } ) ,
452- 1 => Ok ( args. pop ( ) . ok_or_else ( || Error :: InvalidPipelineError {
453- cause : "coalesce internal error: empty args after len check" . into ( ) ,
454- query_location : None ,
455- } ) ?) ,
456- _ => {
457- let last = args. pop ( ) . ok_or_else ( || Error :: InvalidPipelineError {
458- cause : "coalesce internal error: missing last argument" . into ( ) ,
459- query_location : None ,
460- } ) ?;
461- let first = args. first ( ) . ok_or_else ( || Error :: InvalidPipelineError {
462- cause : "coalesce internal error: missing first argument" . into ( ) ,
463- query_location : None ,
464- } ) ?;
465- let first = first. clone ( ) ;
466- let mut builder = when ( first. clone ( ) . is_not_null ( ) , first) ;
467- for a in args. into_iter ( ) . skip ( 1 ) {
468- builder = builder. when ( a. clone ( ) . is_not_null ( ) , a) ;
469- }
470- builder. otherwise ( last) . map_err ( Error :: from)
471- }
472- }
473- }
474-
475457 fn plan_binary_math_expr (
476458 & self ,
477459 binary_math_expr : & BinaryMathematicalScalarExpression ,
0 commit comments