From 2b7f22da51318011fc06f3fb6f0c075887cf73f8 Mon Sep 17 00:00:00 2001 From: Devam Patel Date: Sun, 21 Sep 2025 03:24:42 +0530 Subject: [PATCH 1/4] display function's alias name in output column --- .../examples/date_time_functions.rs | 16 ++--- datafusion/sql/src/expr/function.rs | 58 ++++++++++++++++--- 2 files changed, 57 insertions(+), 17 deletions(-) diff --git a/datafusion-examples/examples/date_time_functions.rs b/datafusion-examples/examples/date_time_functions.rs index dbe9970439df..2628319ae31f 100644 --- a/datafusion-examples/examples/date_time_functions.rs +++ b/datafusion-examples/examples/date_time_functions.rs @@ -492,14 +492,14 @@ async fn query_to_char() -> Result<()> { assert_batches_eq!( &[ - "+------------------------------+", - "| to_char(t.values,t.patterns) |", - "+------------------------------+", - "| 2020-09-01 |", - "| 2020:09:02 |", - "| 20200903 |", - "| 04-09-2020 |", - "+------------------------------+", + "+----------------------------------+", + "| date_format(t.values,t.patterns) |", + "+----------------------------------+", + "| 2020-09-01 |", + "| 2020:09:02 |", + "| 20200903 |", + "| 04-09-2020 |", + "+----------------------------------+", ], &result ); diff --git a/datafusion/sql/src/expr/function.rs b/datafusion/sql/src/expr/function.rs index 5a47c56f3488..8b289e816926 100644 --- a/datafusion/sql/src/expr/function.rs +++ b/datafusion/sql/src/expr/function.rs @@ -268,7 +268,26 @@ impl SqlToRel<'_, S> { // User-defined function (UDF) should have precedence if let Some(fm) = self.context_provider.get_function_meta(&name) { let args = self.function_args_to_expr(args, schema, planner_context)?; - return Ok(Expr::ScalarFunction(ScalarFunction::new_udf(fm, args))); + let scalar_func_expr = + Expr::ScalarFunction(ScalarFunction::new_udf(fm.clone(), args.clone())); + + if name.eq_ignore_ascii_case(fm.name()) { + return Ok(scalar_func_expr); + } else { + + let arg_names = args + .iter() + .map(|arg| arg.to_string()) + .collect::>() + .join(","); + let verbose_alias = format!("{}({})", name, arg_names); + + return Ok(Expr::Alias(expr::Alias::new( + scalar_func_expr, + None::, + verbose_alias, + ))); + } } // Build Unnest expression @@ -470,14 +489,35 @@ impl SqlToRel<'_, S> { null_treatment, } = aggregate_expr; - return Ok(Expr::AggregateFunction(expr::AggregateFunction::new_udf( - func, - args, - distinct, - filter, - order_by, - null_treatment, - ))); + + let agg_func_expr = + Expr::AggregateFunction(expr::AggregateFunction::new_udf( + func.clone(), + args.clone(), + distinct, + filter, + order_by, + null_treatment, + )); + + if name.eq_ignore_ascii_case(func.name()) { + return Ok(agg_func_expr); + }else { + + let arg_names = args + .iter() + .map(|arg| arg.to_string()) + .collect::>() + .join(","); + let verbose_alias = format!("{}({})", name, arg_names); + + return Ok(Expr::Alias(expr::Alias::new( + agg_func_expr, + None::, + verbose_alias, + ))); + } + } } From 404411ff7f4bed4710b4a1792546e0e3b2d93e64 Mon Sep 17 00:00:00 2001 From: Devam Patel Date: Sun, 21 Sep 2025 04:31:56 +0530 Subject: [PATCH 2/4] Update function.rs --- datafusion/sql/src/expr/function.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/datafusion/sql/src/expr/function.rs b/datafusion/sql/src/expr/function.rs index 8b289e816926..eec73c422acc 100644 --- a/datafusion/sql/src/expr/function.rs +++ b/datafusion/sql/src/expr/function.rs @@ -270,11 +270,10 @@ impl SqlToRel<'_, S> { let args = self.function_args_to_expr(args, schema, planner_context)?; let scalar_func_expr = Expr::ScalarFunction(ScalarFunction::new_udf(fm.clone(), args.clone())); - + if name.eq_ignore_ascii_case(fm.name()) { return Ok(scalar_func_expr); } else { - let arg_names = args .iter() .map(|arg| arg.to_string()) @@ -489,7 +488,6 @@ impl SqlToRel<'_, S> { null_treatment, } = aggregate_expr; - let agg_func_expr = Expr::AggregateFunction(expr::AggregateFunction::new_udf( func.clone(), @@ -499,11 +497,10 @@ impl SqlToRel<'_, S> { order_by, null_treatment, )); - + if name.eq_ignore_ascii_case(func.name()) { return Ok(agg_func_expr); - }else { - + } else { let arg_names = args .iter() .map(|arg| arg.to_string()) @@ -517,7 +514,6 @@ impl SqlToRel<'_, S> { verbose_alias, ))); } - } } From 13f4112124d49688904e7d30cabf5ae90a6d2b12 Mon Sep 17 00:00:00 2001 From: Devam Patel <115361140+devampatel03@users.noreply.github.com> Date: Tue, 23 Sep 2025 09:47:24 +0000 Subject: [PATCH 3/4] updated verbose name format --- .../tests/user_defined/user_defined_aggregates.rs | 14 +++++++------- .../user_defined/user_defined_scalar_functions.rs | 14 +++++++------- datafusion/sql/src/expr/function.rs | 13 ++++++++----- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/datafusion/core/tests/user_defined/user_defined_aggregates.rs b/datafusion/core/tests/user_defined/user_defined_aggregates.rs index a5b073b147ea..db70caf52534 100644 --- a/datafusion/core/tests/user_defined/user_defined_aggregates.rs +++ b/datafusion/core/tests/user_defined/user_defined_aggregates.rs @@ -379,13 +379,13 @@ async fn test_user_defined_functions_with_alias() -> Result<()> { let alias_result = plan_and_collect(&ctx, "SELECT dummy_alias(i) FROM t").await?; - insta::assert_snapshot!(batches_to_string(&alias_result), @r###" - +------------+ - | dummy(t.i) | - +------------+ - | 1.0 | - +------------+ - "###); + insta::assert_snapshot!(batches_to_string(&alias_result), @r" + +------------------+ + | dummy_alias(t.i) | + +------------------+ + | 1.0 | + +------------------+ + "); Ok(()) } diff --git a/datafusion/core/tests/user_defined/user_defined_scalar_functions.rs b/datafusion/core/tests/user_defined/user_defined_scalar_functions.rs index 1e0515356b9a..1c155853b21c 100644 --- a/datafusion/core/tests/user_defined/user_defined_scalar_functions.rs +++ b/datafusion/core/tests/user_defined/user_defined_scalar_functions.rs @@ -478,13 +478,13 @@ async fn test_user_defined_functions_with_alias() -> Result<()> { "###); let alias_result = plan_and_collect(&ctx, "SELECT dummy_alias(i) FROM t").await?; - insta::assert_snapshot!(batches_to_string(&alias_result), @r###" - +------------+ - | dummy(t.i) | - +------------+ - | 1 | - +------------+ - "###); + insta::assert_snapshot!(batches_to_string(&alias_result), @r" + +------------------+ + | dummy_alias(t.i) | + +------------------+ + | 1 | + +------------------+ + "); Ok(()) } diff --git a/datafusion/sql/src/expr/function.rs b/datafusion/sql/src/expr/function.rs index 0071a660b0db..b5d20c9e8556 100644 --- a/datafusion/sql/src/expr/function.rs +++ b/datafusion/sql/src/expr/function.rs @@ -32,6 +32,7 @@ use sqlparser::ast::{ FunctionArgExpr, FunctionArgumentClause, FunctionArgumentList, FunctionArguments, ObjectName, OrderByExpr, Spanned, WindowType, }; +use std::sync::Arc; /// Suggest a valid function based on an invalid input function name /// @@ -270,8 +271,10 @@ impl SqlToRel<'_, S> { // User-defined function (UDF) should have precedence if let Some(fm) = self.context_provider.get_function_meta(&name) { let args = self.function_args_to_expr(args, schema, planner_context)?; - let scalar_func_expr = - Expr::ScalarFunction(ScalarFunction::new_udf(fm.clone(), args.clone())); + let scalar_func_expr = Expr::ScalarFunction(ScalarFunction::new_udf( + Arc::clone(&fm), + args.clone(), + )); if name.eq_ignore_ascii_case(fm.name()) { return Ok(scalar_func_expr); @@ -281,7 +284,7 @@ impl SqlToRel<'_, S> { .map(|arg| arg.to_string()) .collect::>() .join(","); - let verbose_alias = format!("{}({})", name, arg_names); + let verbose_alias = format!("{name}({arg_names})"); return Ok(Expr::Alias(expr::Alias::new( scalar_func_expr, @@ -492,7 +495,7 @@ impl SqlToRel<'_, S> { let agg_func_expr = Expr::AggregateFunction(expr::AggregateFunction::new_udf( - func.clone(), + Arc::clone(&func), args.clone(), distinct, filter, @@ -508,7 +511,7 @@ impl SqlToRel<'_, S> { .map(|arg| arg.to_string()) .collect::>() .join(","); - let verbose_alias = format!("{}({})", name, arg_names); + let verbose_alias = format!("{name}({arg_names})"); return Ok(Expr::Alias(expr::Alias::new( agg_func_expr, From 1c51eabb95711335be6391ffcd948a4a48f0bb72 Mon Sep 17 00:00:00 2001 From: Devam Patel <115361140+devampatel03@users.noreply.github.com> Date: Wed, 24 Sep 2025 13:25:46 +0000 Subject: [PATCH 4/4] simplify alias logic and removing args clone --- datafusion/sql/src/expr/function.rs | 56 ++++++++++++++--------------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/datafusion/sql/src/expr/function.rs b/datafusion/sql/src/expr/function.rs index b5d20c9e8556..143324e37492 100644 --- a/datafusion/sql/src/expr/function.rs +++ b/datafusion/sql/src/expr/function.rs @@ -32,7 +32,6 @@ use sqlparser::ast::{ FunctionArgExpr, FunctionArgumentClause, FunctionArgumentList, FunctionArguments, ObjectName, OrderByExpr, Spanned, WindowType, }; -use std::sync::Arc; /// Suggest a valid function based on an invalid input function name /// @@ -271,26 +270,23 @@ impl SqlToRel<'_, S> { // User-defined function (UDF) should have precedence if let Some(fm) = self.context_provider.get_function_meta(&name) { let args = self.function_args_to_expr(args, schema, planner_context)?; - let scalar_func_expr = Expr::ScalarFunction(ScalarFunction::new_udf( - Arc::clone(&fm), - args.clone(), - )); + let inner = ScalarFunction::new_udf(fm, args); - if name.eq_ignore_ascii_case(fm.name()) { - return Ok(scalar_func_expr); + if name.eq_ignore_ascii_case(inner.name()) { + return Ok(Expr::ScalarFunction(inner)); } else { - let arg_names = args + // If the function is called by an alias, a verbose string representation is created + // (e.g., "my_alias(arg1, arg2)") and the expression is wrapped in an `Alias` + // to ensure the output column name matches the user's query. + let arg_names = inner + .args .iter() .map(|arg| arg.to_string()) .collect::>() .join(","); let verbose_alias = format!("{name}({arg_names})"); - return Ok(Expr::Alias(expr::Alias::new( - scalar_func_expr, - None::, - verbose_alias, - ))); + return Ok(Expr::ScalarFunction(inner).alias(verbose_alias)); } } @@ -493,31 +489,31 @@ impl SqlToRel<'_, S> { null_treatment, } = aggregate_expr; - let agg_func_expr = - Expr::AggregateFunction(expr::AggregateFunction::new_udf( - Arc::clone(&func), - args.clone(), - distinct, - filter, - order_by, - null_treatment, - )); + let inner = expr::AggregateFunction::new_udf( + func, + args, + distinct, + filter, + order_by, + null_treatment, + ); - if name.eq_ignore_ascii_case(func.name()) { - return Ok(agg_func_expr); + if name.eq_ignore_ascii_case(inner.func.name()) { + return Ok(Expr::AggregateFunction(inner)); } else { - let arg_names = args + // If the function is called by an alias, a verbose string representation is created + // (e.g., "my_alias(arg1, arg2)") and the expression is wrapped in an `Alias` + // to ensure the output column name matches the user's query. + let arg_names = inner + .params + .args .iter() .map(|arg| arg.to_string()) .collect::>() .join(","); let verbose_alias = format!("{name}({arg_names})"); - return Ok(Expr::Alias(expr::Alias::new( - agg_func_expr, - None::, - verbose_alias, - ))); + return Ok(Expr::AggregateFunction(inner).alias(verbose_alias)); } } }