Skip to content
Closed
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
80 changes: 79 additions & 1 deletion datafusion/optimizer/src/analyzer/type_coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,33 @@ impl TreeNodeRewriter for TypeCoercionRewriter<'_> {
case_insensitive,
))))
}
Expr::SimilarTo(Like {
negated,
expr,
pattern,
escape_char,
case_insensitive,
}) => {
let left_type = expr.get_type(self.schema)?;
let right_type = pattern.get_type(self.schema)?;
let coerced_type = like_coercion(&left_type, &right_type).ok_or_else(|| {
plan_datafusion_err!(
"There isn't a common type to coerce {left_type} and {right_type} in SIMILAR TO expression"
)
})?;
let expr = match left_type {
DataType::Dictionary(_, inner) if *inner == DataType::Utf8 => expr,
_ => Box::new(expr.cast_to(&coerced_type, self.schema)?),
};
let pattern = Box::new(pattern.cast_to(&coerced_type, self.schema)?);
Ok(Transformed::yes(Expr::SimilarTo(Like::new(
negated,
expr,
pattern,
escape_char,
case_insensitive,
))))
}
Expr::BinaryExpr(BinaryExpr { left, op, right }) => {
let (left, right) =
self.coerce_binary_op(*left, self.schema, op, *right, self.schema)?;
Expand Down Expand Up @@ -812,7 +839,6 @@ impl TreeNodeRewriter for TypeCoercionRewriter<'_> {
| Expr::Column(_)
| Expr::ScalarVariable(_, _)
| Expr::Literal(_, _)
| Expr::SimilarTo(_)
| Expr::IsNotNull(_)
| Expr::IsNull(_)
| Expr::Cast(_)
Expand Down Expand Up @@ -2239,6 +2265,58 @@ mod test {
Ok(())
}

#[test]
fn similar_to_for_type_coercion() -> Result<()> {
// similar to : utf8 SIMILAR TO "abc"
let expr = Box::new(col("a"));
let pattern = Box::new(lit(ScalarValue::new_utf8("abc")));
let similar_to_expr =
Expr::SimilarTo(Like::new(false, expr, pattern, None, false));
let empty = empty_with_type(Utf8);
let plan =
LogicalPlan::Projection(Projection::try_new(vec![similar_to_expr], empty)?);

assert_analyzed_plan_eq!(
plan,
@r#"
Projection: a SIMILAR TO Utf8("abc")
EmptyRelation: rows=0
"#
)?;

// NULL pattern is coerced into the common type rather than panicking
// during constant folding (see issue #22886).
let expr = Box::new(col("a"));
let pattern = Box::new(lit(ScalarValue::Null));
let similar_to_expr =
Expr::SimilarTo(Like::new(false, expr, pattern, None, false));
let empty = empty_with_type(Utf8);
let plan =
LogicalPlan::Projection(Projection::try_new(vec![similar_to_expr], empty)?);

assert_analyzed_plan_eq!(
plan,
@r"
Projection: a SIMILAR TO CAST(NULL AS Utf8)
EmptyRelation: rows=0
"
)?;

let expr = Box::new(col("a"));
let pattern = Box::new(lit(ScalarValue::new_utf8("abc")));
let similar_to_expr =
Expr::SimilarTo(Like::new(false, expr, pattern, None, false));
let empty = empty_with_type(DataType::Int64);
let plan =
LogicalPlan::Projection(Projection::try_new(vec![similar_to_expr], empty)?);
assert_type_coercion_error(
plan,
"There isn't a common type to coerce Int64 and Utf8 in SIMILAR TO expression",
)?;

Ok(())
}

#[test]
fn unknown_for_type_coercion() -> Result<()> {
// unknown
Expand Down
32 changes: 31 additions & 1 deletion datafusion/sqllogictest/test_files/type_coercion.slt
Original file line number Diff line number Diff line change
Expand Up @@ -301,4 +301,34 @@ query error does not support zero arguments
SELECT * FROM (SELECT 1) WHERE CAST(STARTS_WITH() AS STRING) = 'x';

query error does not support zero arguments
SELECT * FROM (SELECT 1) WHERE TRY_CAST(STARTS_WITH() AS INT) = 1;
SELECT * FROM (SELECT 1) WHERE TRY_CAST(STARTS_WITH() AS INT) = 1;

################################################################
## SIMILAR TO operand type coercion
## https://github.com/apache/datafusion/issues/22886
################################################################

statement ok
CREATE TABLE similar_to_t AS
SELECT arrow_cast(s, 'Utf8View') AS v, p FROM (VALUES
('abc', 'abc'),
('xyz', 'abc')
) AS t(s, p);

# Utf8View column matched against a non-literal Utf8 pattern column used to panic
# with "failed to downcast array"; both operands now coerce to a common type.
query B
SELECT v SIMILAR TO p FROM similar_to_t ORDER BY v;
----
true
false

# A NULL pattern used to panic during constant folding; it now evaluates to NULL.
query B
SELECT v SIMILAR TO NULL FROM similar_to_t ORDER BY v;
----
NULL
NULL

statement ok
DROP TABLE similar_to_t;
Loading