diff --git a/src/expr/core/src/aggregate/mod.rs b/src/expr/core/src/aggregate/mod.rs index 695aad482343d..e77c549a99db0 100644 --- a/src/expr/core/src/aggregate/mod.rs +++ b/src/expr/core/src/aggregate/mod.rs @@ -160,16 +160,7 @@ pub fn build(agg: &AggCall, prefer_append_only: bool) -> Result {}", - kind.as_str_name().to_ascii_lowercase(), - agg.args.arg_types().iter().format(", "), - agg.return_type, - )) - })?; + let sig = crate::sig::FUNCTION_REGISTRY.get(*kind, agg.args.arg_types(), &agg.return_type)?; if let FuncBuilder::Aggregate { append_only: Some(f), diff --git a/src/expr/core/src/expr/build.rs b/src/expr/core/src/expr/build.rs index 91ebe67f479a9..988adbb5d8342 100644 --- a/src/expr/core/src/expr/build.rs +++ b/src/expr/core/src/expr/build.rs @@ -30,7 +30,7 @@ use crate::expr::{ BoxedExpression, Expression, ExpressionBoxExt, InputRefExpression, LiteralExpression, }; use crate::sig::FUNCTION_REGISTRY; -use crate::{bail, ExprError, Result}; +use crate::{bail, Result}; /// Build an expression from protobuf. pub fn build_from_prost(prost: &ExprNode) -> Result { @@ -188,16 +188,7 @@ pub fn build_func( children: Vec, ) -> Result { let args = children.iter().map(|c| c.return_type()).collect_vec(); - let desc = FUNCTION_REGISTRY - .get(func, &args, &ret_type) - .ok_or_else(|| { - ExprError::UnsupportedFunction(format!( - "{}({}) -> {}", - func.as_str_name().to_ascii_lowercase(), - args.iter().format(", "), - ret_type, - )) - })?; + let desc = FUNCTION_REGISTRY.get(func, &args, &ret_type)?; desc.build_scalar(ret_type, children) } diff --git a/src/expr/core/src/sig/mod.rs b/src/expr/core/src/sig/mod.rs index ae5af5b57c649..aae3802489d09 100644 --- a/src/expr/core/src/sig/mod.rs +++ b/src/expr/core/src/sig/mod.rs @@ -113,9 +113,38 @@ impl FunctionRegistry { name: impl Into, args: &[DataType], ret: &DataType, - ) -> Option<&FuncSign> { - let v = self.0.get(&name.into())?; - v.iter().find(|d| d.match_args_ret(args, ret)) + ) -> Result<&FuncSign, ExprError> { + let name = name.into(); + let err = |candidates: &Vec| { + // Note: if we return error here, it probably means there is a bug in frontend type inference, + // because such error should be caught in the frontend. + ExprError::UnsupportedFunction(format!( + "{}({}) -> {}{}", + name, + args.iter().format(", "), + ret, + if candidates.is_empty() { + "".to_string() + } else { + format!( + "\nHINT: Supported functions:\n{}", + candidates + .iter() + .map(|d| format!( + " {}({}) -> {}", + d.name, + d.inputs_type.iter().format(", "), + d.ret_type + )) + .format("\n") + ) + } + )) + }; + let v = self.0.get(&name).ok_or_else(|| err(&vec![]))?; + v.iter() + .find(|d| d.match_args_ret(args, ret)) + .ok_or_else(|| err(v)) } /// Returns all function signatures with the same type and number of arguments. diff --git a/src/expr/core/src/table_function/mod.rs b/src/expr/core/src/table_function/mod.rs index d2d8e291ee076..ba976bda59404 100644 --- a/src/expr/core/src/table_function/mod.rs +++ b/src/expr/core/src/table_function/mod.rs @@ -127,16 +127,7 @@ pub fn build( ) -> Result { use itertools::Itertools; let args = children.iter().map(|t| t.return_type()).collect_vec(); - let desc = crate::sig::FUNCTION_REGISTRY - .get(func, &args, &return_type) - .ok_or_else(|| { - ExprError::UnsupportedFunction(format!( - "{}({}) -> setof {}", - func.as_str_name().to_ascii_lowercase(), - args.iter().format(", "), - return_type, - )) - })?; + let desc = crate::sig::FUNCTION_REGISTRY.get(func, &args, &return_type)?; desc.build_table(return_type, chunk_size, children) }