fix: properly give placeholder types (#5760)

* fix: properly give placeholder types

* chore: update sqlness
This commit is contained in:
discord9
2025-03-24 16:41:32 +08:00
committed by GitHub
parent 9f9307de73
commit c4ac242c69
3 changed files with 31 additions and 37 deletions

View File

@@ -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()

View File

@@ -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(())

View File

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