diff --git a/src/servers/src/mysql/handler.rs b/src/servers/src/mysql/handler.rs index f8dec09114..99cde68257 100644 --- a/src/servers/src/mysql/handler.rs +++ b/src/servers/src/mysql/handler.rs @@ -195,6 +195,7 @@ impl MysqlInstanceShim { let params = if let Some(plan) = &mut plan { fix_placeholder_types(plan)?; + debug!("Plan after fix placeholder types: {:#?}", plan); prepared_params( &plan .get_parameter_types() diff --git a/src/servers/src/mysql/helper.rs b/src/servers/src/mysql/helper.rs index bf2d5688d0..467430b31d 100644 --- a/src/servers/src/mysql/helper.rs +++ b/src/servers/src/mysql/helper.rs @@ -79,15 +79,24 @@ pub fn transform_placeholders(stmt: Statement) -> Statement { } } -/// Give placeholder in skip and limit `int64` data type if it is not specified +/// Give placeholder that cast to certain type `data_type` the same data type as is cast to /// -/// because it seems datafusion will not give data type to placeholder if it's in limit/skip position, still unknown if this is a feature or a bug. And if a placeholder expr have no data type, datafusion will fail to extract it using `LogicalPlan::get_parameter_types` +/// because it seems datafusion will not give data type to placeholder if it need to be cast to certain type, still unknown if this is a feature or a bug. And if a placeholder expr have no data type, datafusion will fail to extract it using `LogicalPlan::get_parameter_types` pub fn fix_placeholder_types(plan: &mut LogicalPlan) -> Result<()> { - let give_placeholder_types = |mut e| { - if let datafusion_expr::Expr::Placeholder(ph) = &mut e { - if ph.data_type.is_none() { - ph.data_type = Some(arrow_schema::DataType::Int64); - Ok(Transformed::yes(e)) + let give_placeholder_types = |mut e: datafusion_expr::Expr| { + if let datafusion_expr::Expr::Cast(cast) = &mut e { + if let datafusion_expr::Expr::Placeholder(ph) = &mut *cast.expr { + if ph.data_type.is_none() { + ph.data_type = Some(cast.data_type.clone()); + common_telemetry::debug!( + "give placeholder type {:?} to {:?}", + cast.data_type, + ph + ); + Ok(Transformed::yes(e)) + } else { + Ok(Transformed::no(e)) + } } else { Ok(Transformed::no(e)) } @@ -95,26 +104,10 @@ pub fn fix_placeholder_types(plan: &mut LogicalPlan) -> Result<()> { Ok(Transformed::no(e)) } }; + let give_placeholder_types_recursively = + |e: datafusion_expr::Expr| e.transform(give_placeholder_types); *plan = std::mem::take(plan) - .transform(|p| { - let LogicalPlan::Limit(mut limit) = p else { - return Ok(Transformed::no(p)); - }; - - if let Some(fetch) = &mut limit.fetch { - *fetch = Box::new( - std::mem::take(fetch) - .transform(give_placeholder_types)? - .data, - ); - } - - if let Some(skip) = &mut limit.skip { - *skip = Box::new(std::mem::take(skip).transform(give_placeholder_types)?.data); - } - - Ok(Transformed::yes(LogicalPlan::Limit(limit))) - }) + .transform(|p| p.map_expressions(give_placeholder_types_recursively)) .context(DataFusionSnafu)? .data; Ok(()) diff --git a/tests/cases/standalone/common/prepare/mysql_prepare.result b/tests/cases/standalone/common/prepare/mysql_prepare.result index eb227ff0d6..f6c041f074 100644 --- a/tests/cases/standalone/common/prepare/mysql_prepare.result +++ b/tests/cases/standalone/common/prepare/mysql_prepare.result @@ -13,16 +13,16 @@ affected_rows: 0 -- SQLNESS PROTOCOL MYSQL EXECUTE stmt USING 1; -+----------+ -| Int64(1) | -+----------+ -| 1 | -+----------+ ++----+ +| $1 | ++----+ +| 1 | ++----+ -- SQLNESS PROTOCOL MYSQL EXECUTE stmt USING 'a'; -Failed to parse query result, err: MySqlError { ERROR 1815 (HY000): (EngineExecuteQuery): Cast error: Cannot cast string 'a' to value of Int32 type } +Failed to execute query, err: MySqlError { ERROR 1210 (HY000): (InvalidArguments): Invalid request parameter: Column expect type: Int32(Int32Type), actual: String(StringType) } -- SQLNESS PROTOCOL MYSQL DEALLOCATE stmt; @@ -59,11 +59,11 @@ affected_rows: 0 -- SQLNESS PROTOCOL MYSQL EXECUTE stmt USING 1, 'hello'; -+----------+---------------+ -| Int64(1) | Utf8("hello") | -+----------+---------------+ -| 1 | hello | -+----------+---------------+ ++----+-------+ +| $1 | $2 | ++----+-------+ +| 1 | hello | ++----+-------+ -- SQLNESS PROTOCOL MYSQL DEALLOCATE stmt;