Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion src/query/sql/src/planner/semantic/type_check/scalar_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -643,12 +643,27 @@ where A: TypeCheckAdapter
// will be folded from `timestamp > to_timestamp('2001-01-01')` to `timestamp > 978307200000000`
// Note: check function may reorder the args

let checked_func_name = BUILTIN_FUNCTIONS
.aliases
.get(func_name)
.map_or(func_name, String::as_str);

// `check_function` may eliminate an identity conversion and return its argument.
// In that case, the checked root arguments no longer correspond to the original call.
let checked_root_matches = match &expr {
expr::Expr::FunctionCall(expr::FunctionCall {
function,
args: checked_args,
..
}) => function.signature.name == checked_func_name && checked_args.len() == args.len(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Compare against the rewritten argument list

For a nonconstant positional call such as as_decimal(variant_column, 10, 2), the earlier split_off(1) moves precision and scale into params, so the checked function has one argument while args still has three. This comparison therefore marks the valid checked root as mismatched, and the fallback constructs a scalar call containing both the new parameters and all three original arguments; the as_decimal factory accepts exactly one argument, so later type checking/evaluation rejects the query. Compare with the post-rewrite arguments and add a nonconstant regression alongside the positional syntax in tests/sqllogictests/suites/query/functions/02_0056_function_semi_structureds_as.test.

AGENTS.md reference: src/query/AGENTS.md:L8-L8

Useful? React with 👍 / 👎.

_ => false,
};
let mut folded_args = match &expr {
expr::Expr::FunctionCall(expr::FunctionCall {
function,
args: checked_args,
..
}) => checked_args
}) if checked_root_matches => checked_args
.iter()
.zip(
function
Expand Down
6 changes: 6 additions & 0 deletions src/query/sql/tests/it/semantic/type_check/scalar_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ async fn test_type_check_scalar_function_rules() -> Result<()> {
setup_sqls: &[],
sql: "to_decimal(number, number)",
},
SqlTestCase {
name: "identity_cast_preserves_complete_argument",
description: "An eliminated identity cast must not remap an inner function argument onto the original call.",
setup_sqls: &[],
sql: "to_int64(100000000 + ((854435761::UInt64 * number + 123456789) % 900000000))",
},
];

run_type_check_cases("scalar_function.txt", &cases).await
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,10 @@ sql: to_decimal(number, number)
status: error
code: 1065
message: Invalid arguments for `to_decimal`, precision is only allowed to be a constant

=== identity_cast_preserves_complete_argument ===
description: An eliminated identity cast must not remap an inner function argument onto the original call.
sql: to_int64(100000000 + ((854435761::UInt64 * number + 123456789) % 900000000))
status: ok
scalar: to_int64(plus(100000000, modulo(plus(multiply(854435761, number (#0)), 123456789), 900000000)))
type: Int64 NULL
Loading