jayzhan211 commented on code in PR #25364:
URL: https://github.com/apache/datafusion/pull/25364#discussion_r4173315819


##########
datafusion/optimizer/src/analyzer/type_coercion.rs:
##########
@@ -355,6 +355,14 @@ impl<'a> TypeCoercionRewriter<'a> {
             schema: &DFSchema,
             expr_name: &str,
         ) -> Result<Expr> {
+            // An untyped placeholder has no type to coerce yet. Leave it bare 
so
+            // `LogicalPlan::get_parameter_fields` can still default it to 
Int64
+            // post-analysis; wrapping it in a `CAST` here would hide it from 
that
+            // inference because the cast's inner expression is no longer a
+            // top-level `Limit` operand.
+            if matches!(&expr, Expr::Placeholder(p) if p.field.is_none()) {

Review Comment:
   Leaving the placeholder uncast changes what the analyzer emits. Analyze → 
bind `Int32(1)` → optimize now leaves `fetch=Int32(1)`, which `get_fetch_type` 
rejects (`Unsupported LIMIT expression: Int32(1)`). On main the cast folds to 
`Int64(1)`. A client that binds the reported type hits this with `SELECT $1 + 
CAST(1 AS INT) AS v LIMIT $1`, where `$1` is reported as `Int32`. Keeping the 
cast and looking through it in `get_parameter_fields` avoids that; every test 
in this PR still passes.
   
   ```rs
   let analyzed = Analyzer::new().execute_and_check(plan, &config, |_, _| {})?;
   let bound = analyzed.with_param_values(vec![ScalarValue::Int32(Some(1))])?;
   let optimized = Optimizer::new().optimize(bound, &OptimizerContext::new(), 
|_, _| {})?;
   // main: fetch=Int64(1); this PR: fetch=Int32(1) -> 
FetchType::UnsupportedExpr
   ```
   
   Drop the early return (and its comment):
   ```diff
   -            if matches!(&expr, Expr::Placeholder(p) if p.field.is_none()) {
   -                return Ok(expr);
   -            }
   ```
   
   `datafusion/expr/src/logical_plan/plan.rs` (also add `Cast` to the 
`crate::expr` import):
   ```diff
   -                if matches!(plan, LogicalPlan::Limit(_))
   -                    && let Expr::Placeholder(Placeholder { id, field: None 
}) = expr
   -                {
   -                    row_count_parameters.insert(id.clone());
   +                if let LogicalPlan::Limit(_) = plan {
   +                    // `TypeCoercion` wraps LIMIT/OFFSET operands in 
`CAST(.. AS Int64)`
   +                    let operand = match expr {
   +                        Expr::Cast(Cast { expr, field })
   +                            if field.data_type() == &DataType::Int64 =>
   +                        {
   +                            expr.as_ref()
   +                        }
   +                        expr => expr,
   +                    };
   +                    if let Expr::Placeholder(Placeholder { id, field: None 
}) = operand {
   +                        row_count_parameters.insert(id.clone());
   +                    }
                    }
   ```



-- 
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