quwin opened a new issue, #25038:
URL: https://github.com/apache/datafusion/issues/25038

   ### Describe the bug
   
   A `CREATE FUNCTION` with an SQL body is accepted even when the `RETURN` 
expression references a
   positional placeholder beyond the declared arguments, or references any 
argument when none were
   declared. The invalid definition gets registered, and the error only appears 
when the function
   is first invoked.
   
   For example, `better_add` declares two arguments but its body references 
`$3`:
   
   ```sql
   CREATE FUNCTION better_add(DOUBLE, DOUBLE)
       RETURNS DOUBLE
       RETURN $1 + $3
   ```
   
   The `CREATE FUNCTION` succeeds there. The first call fails inside 
optimization:
   
   ```
   Optimizer rule 'simplify_expressions' failed
   caused by
   Execution error: Invalid placeholder, out of range: $3
   ```
   
   A definition error like this should be reported at `CREATE FUNCTION` time 
(plan error), not
   deferred to every invocation. This gap is self-acknowledged by two in-code 
FIXMEs introduced with
   the named-variables/defaults work in #18450:
   
   - `datafusion/sql/src/expr/value.rs` — "FIXME: In the CREATE FUNCTION 
branch, param_type = None should raise an error"
   - `datafusion/core/tests/user_defined/user_defined_scalar_functions.rs` — 
"FIXME: Definitions with invalid placeholders are allowed, fail at runtime"
   
   ### To Reproduce
   
   SQL-function DDL requires a configured `FunctionFactory` (a plain 
`SessionContext` returns
   "Function factory has not been configured"), so the repro uses the shipped
   `function_factory` example. In 
`datafusion-examples/examples/builtin_functions/function_factory.rs`,
   add a third function alongside `f1`/`f2`:
   
   ```rust
   // f3 is declared with two arguments, but its body references $3
   let sql = r#"
       CREATE FUNCTION f3(BIGINT, BIGINT)
           RETURNS BIGINT
           RETURN $1 + $3
   "#;
   ctx.sql(sql).await?.show().await?;
   ctx.sql("SELECT f3(1, 2)").await?.show().await?;
   ```
   
   then run:
   
   ```bash
   cargo run --example builtin_functions -- function_factory
   ```
   
   The same shape is covered by the existing integration test
   `create_scalar_function_from_sql_statement` in
   `datafusion/core/tests/user_defined/user_defined_scalar_functions.rs`.
   
   ### Expected behavior
   
   `CREATE FUNCTION better_add(DOUBLE, DOUBLE) ... RETURN $1 + $3` should fail 
during planning with
   an error such as:
   
   ```
   Error during planning: Invalid placeholder, out of range: $3
   ```
   
   (PostgreSQL analog: `ERROR: there is no parameter $3`.) Named placeholders 
with zero declared
   arguments (e.g. `CREATE FUNCTION f() RETURNS DOUBLE RETURN $a`) should 
likewise be rejected at
   definition time. Valid positional, named, and defaulted-argument bodies that 
only
   reference declared arguments must keep working.
   
   ### Additional context
   
   - Verified on `main` at `e1ca94f`
   - Root cause: the `RETURN` body is planned with PREPARE-style parameter types
     (`create_placeholder_expr` in `datafusion/sql/src/expr/value.rs`); an 
out-of-range `$N` gets an
     untyped `Placeholder` instead of an error because that code path is shared 
with `PREPARE`, where
     an empty/unknown parameter list must stay permissive for deferred type 
inference. The place
     where both the declared argument list and the parsed body are available is 
the
     `Statement::CreateFunction` arm of the SQL planner, so a validation pass 
there (walking the body
     for `Expr::Placeholder`) would fix every `FunctionFactory` implementation 
with zero impact on
     `PREPARE` semantics. Runtime guards in factories would remain as a 
defensive backstop.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to