This is an automated email from the ASF dual-hosted git repository.
alamb pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-datafusion.git
The following commit(s) were added to refs/heads/main by this push:
new bb2b39cf78 refactor: Merge Expr::Like and Expr::ILike (#7007)
bb2b39cf78 is described below
commit bb2b39cf78e62d1d6cd03e5785cc9ccee798b221
Author: Ruihang Xia <[email protected]>
AuthorDate: Wed Jul 19 18:40:07 2023 +0800
refactor: Merge Expr::Like and Expr::ILike (#7007)
* merge Like and ILike expr
Signed-off-by: Ruihang Xia <[email protected]>
* change from 'case_sensitive' to 'case_insensitive'
Signed-off-by: Ruihang Xia <[email protected]>
* fix docs
Signed-off-by: Ruihang Xia <[email protected]>
---------
Signed-off-by: Ruihang Xia <[email protected]>
---
datafusion/core/src/datasource/listing/helpers.rs | 1 -
datafusion/core/src/physical_planner.rs | 26 ++-----
datafusion/expr/src/expr.rs | 82 ++++++++++------------
datafusion/expr/src/expr_schema.rs | 5 +-
datafusion/expr/src/operator.rs | 18 +++--
datafusion/expr/src/tree_node/expr.rs | 16 ++---
datafusion/expr/src/utils.rs | 1 -
datafusion/optimizer/src/analyzer/type_coercion.rs | 46 ++++++------
datafusion/optimizer/src/push_down_filter.rs | 1 -
.../src/simplify_expressions/expr_simplifier.rs | 25 ++-----
.../optimizer/src/simplify_expressions/regex.rs | 7 +-
.../optimizer/src/simplify_expressions/utils.rs | 8 +--
datafusion/physical-expr/src/planner.rs | 34 +--------
datafusion/proto/src/logical_plan/from_proto.rs | 5 +-
datafusion/proto/src/logical_plan/mod.rs | 5 +-
datafusion/proto/src/logical_plan/to_proto.rs | 52 +++++++-------
datafusion/sql/src/expr/mod.rs | 32 ++-------
datafusion/sql/src/utils.rs | 15 ++--
datafusion/substrait/src/logical_plan/consumer.rs | 24 +++----
datafusion/substrait/src/logical_plan/producer.rs | 18 +----
20 files changed, 147 insertions(+), 274 deletions(-)
diff --git a/datafusion/core/src/datasource/listing/helpers.rs
b/datafusion/core/src/datasource/listing/helpers.rs
index 370bc43319..efafd7d2c3 100644
--- a/datafusion/core/src/datasource/listing/helpers.rs
+++ b/datafusion/core/src/datasource/listing/helpers.rs
@@ -81,7 +81,6 @@ pub fn expr_applicable_for_cols(col_names: &[String], expr:
&Expr) -> bool {
| Expr::BinaryExpr { .. }
| Expr::Between { .. }
| Expr::Like { .. }
- | Expr::ILike { .. }
| Expr::SimilarTo { .. }
| Expr::InList { .. }
| Expr::Exists { .. }
diff --git a/datafusion/core/src/physical_planner.rs
b/datafusion/core/src/physical_planner.rs
index de34353d59..c0e60008bc 100644
--- a/datafusion/core/src/physical_planner.rs
+++ b/datafusion/core/src/physical_planner.rs
@@ -296,37 +296,20 @@ fn create_physical_name(e: &Expr, is_first_expr: bool) ->
Result<String> {
expr,
pattern,
escape_char,
+ case_insensitive,
}) => {
let expr = create_physical_name(expr, false)?;
let pattern = create_physical_name(pattern, false)?;
+ let op_name = if *case_insensitive { "ILIKE" } else { "LIKE" };
let escape = if let Some(char) = escape_char {
format!("CHAR '{char}'")
} else {
"".to_string()
};
if *negated {
- Ok(format!("{expr} NOT LIKE {pattern}{escape}"))
+ Ok(format!("{expr} NOT {op_name} {pattern}{escape}"))
} else {
- Ok(format!("{expr} LIKE {pattern}{escape}"))
- }
- }
- Expr::ILike(Like {
- negated,
- expr,
- pattern,
- escape_char,
- }) => {
- let expr = create_physical_name(expr, false)?;
- let pattern = create_physical_name(pattern, false)?;
- let escape = if let Some(char) = escape_char {
- format!("CHAR '{char}'")
- } else {
- "".to_string()
- };
- if *negated {
- Ok(format!("{expr} NOT ILIKE {pattern}{escape}"))
- } else {
- Ok(format!("{expr} ILIKE {pattern}{escape}"))
+ Ok(format!("{expr} {op_name} {pattern}{escape}"))
}
}
Expr::SimilarTo(Like {
@@ -334,6 +317,7 @@ fn create_physical_name(e: &Expr, is_first_expr: bool) ->
Result<String> {
expr,
pattern,
escape_char,
+ case_insensitive: _,
}) => {
let expr = create_physical_name(expr, false)?;
let pattern = create_physical_name(pattern, false)?;
diff --git a/datafusion/expr/src/expr.rs b/datafusion/expr/src/expr.rs
index 9e1a0aae4c..485b91141f 100644
--- a/datafusion/expr/src/expr.rs
+++ b/datafusion/expr/src/expr.rs
@@ -93,8 +93,6 @@ pub enum Expr {
BinaryExpr(BinaryExpr),
/// LIKE expression
Like(Like),
- /// Case-insensitive LIKE expression
- ILike(Like),
/// LIKE expression that uses regular expressions
SimilarTo(Like),
/// Negation of an expression. The expression's type must be a boolean to
make sense.
@@ -280,6 +278,8 @@ pub struct Like {
pub expr: Box<Expr>,
pub pattern: Box<Expr>,
pub escape_char: Option<char>,
+ /// Whether to ignore case on comparing
+ pub case_insensitive: bool,
}
impl Like {
@@ -289,12 +289,14 @@ impl Like {
expr: Box<Expr>,
pattern: Box<Expr>,
escape_char: Option<char>,
+ case_insensitive: bool,
) -> Self {
Self {
negated,
expr,
pattern,
escape_char,
+ case_insensitive,
}
}
}
@@ -691,7 +693,6 @@ impl Expr {
Expr::IsNotNull(..) => "IsNotNull",
Expr::IsNull(..) => "IsNull",
Expr::Like { .. } => "Like",
- Expr::ILike { .. } => "ILike",
Expr::SimilarTo { .. } => "RLike",
Expr::IsTrue(..) => "IsTrue",
Expr::IsFalse(..) => "IsFalse",
@@ -757,22 +758,40 @@ impl Expr {
/// Return `self LIKE other`
pub fn like(self, other: Expr) -> Expr {
- Expr::Like(Like::new(false, Box::new(self), Box::new(other), None))
+ Expr::Like(Like::new(
+ false,
+ Box::new(self),
+ Box::new(other),
+ None,
+ false,
+ ))
}
/// Return `self NOT LIKE other`
pub fn not_like(self, other: Expr) -> Expr {
- Expr::Like(Like::new(true, Box::new(self), Box::new(other), None))
+ Expr::Like(Like::new(
+ true,
+ Box::new(self),
+ Box::new(other),
+ None,
+ false,
+ ))
}
/// Return `self ILIKE other`
pub fn ilike(self, other: Expr) -> Expr {
- Expr::ILike(Like::new(false, Box::new(self), Box::new(other), None))
+ Expr::Like(Like::new(
+ false,
+ Box::new(self),
+ Box::new(other),
+ None,
+ true,
+ ))
}
/// Return `self NOT ILIKE other`
pub fn not_ilike(self, other: Expr) -> Expr {
- Expr::ILike(Like::new(true, Box::new(self), Box::new(other), None))
+ Expr::Like(Like::new(true, Box::new(self), Box::new(other), None,
true))
}
/// Return the name to use for the specific Expr, recursing into
@@ -1077,31 +1096,17 @@ impl fmt::Display for Expr {
expr,
pattern,
escape_char,
+ case_insensitive,
}) => {
write!(f, "{expr}")?;
+ let op_name = if *case_insensitive { "ILIKE" } else { "LIKE" };
if *negated {
write!(f, " NOT")?;
}
if let Some(char) = escape_char {
- write!(f, " LIKE {pattern} ESCAPE '{char}'")
+ write!(f, " {op_name} {pattern} ESCAPE '{char}'")
} else {
- write!(f, " LIKE {pattern}")
- }
- }
- Expr::ILike(Like {
- negated,
- expr,
- pattern,
- escape_char,
- }) => {
- write!(f, "{expr}")?;
- if *negated {
- write!(f, " NOT")?;
- }
- if let Some(char) = escape_char {
- write!(f, " ILIKE {pattern} ESCAPE '{char}'")
- } else {
- write!(f, " ILIKE {pattern}")
+ write!(f, " {op_name} {pattern}")
}
}
Expr::SimilarTo(Like {
@@ -1109,6 +1114,7 @@ impl fmt::Display for Expr {
expr,
pattern,
escape_char,
+ case_insensitive: _,
}) => {
write!(f, "{expr}")?;
if *negated {
@@ -1211,30 +1217,13 @@ fn create_name(e: &Expr) -> Result<String> {
expr,
pattern,
escape_char,
+ case_insensitive,
}) => {
let s = format!(
- "{} {} {} {}",
- expr,
- if *negated { "NOT LIKE" } else { "LIKE" },
- pattern,
- if let Some(char) = escape_char {
- format!("CHAR '{char}'")
- } else {
- "".to_string()
- }
- );
- Ok(s)
- }
- Expr::ILike(Like {
- negated,
- expr,
- pattern,
- escape_char,
- }) => {
- let s = format!(
- "{} {} {} {}",
+ "{} {}{} {} {}",
expr,
- if *negated { "NOT ILIKE" } else { "ILIKE" },
+ if *negated { "NOT " } else { "" },
+ if *case_insensitive { "ILIKE" } else { "LIKE" },
pattern,
if let Some(char) = escape_char {
format!("CHAR '{char}'")
@@ -1249,6 +1238,7 @@ fn create_name(e: &Expr) -> Result<String> {
expr,
pattern,
escape_char,
+ case_insensitive: _,
}) => {
let s = format!(
"{} {} {} {}",
diff --git a/datafusion/expr/src/expr_schema.rs
b/datafusion/expr/src/expr_schema.rs
index 2ef2951d49..4a2673bcc9 100644
--- a/datafusion/expr/src/expr_schema.rs
+++ b/datafusion/expr/src/expr_schema.rs
@@ -133,9 +133,7 @@ impl ExprSchemable for Expr {
ref right,
ref op,
}) => get_result_type(&left.get_type(schema)?, op,
&right.get_type(schema)?),
- Expr::Like { .. } | Expr::ILike { .. } | Expr::SimilarTo { .. } =>
{
- Ok(DataType::Boolean)
- }
+ Expr::Like { .. } | Expr::SimilarTo { .. } =>
Ok(DataType::Boolean),
Expr::Placeholder(Placeholder { data_type, .. }) => {
data_type.clone().ok_or_else(|| {
DataFusionError::Plan(
@@ -256,7 +254,6 @@ impl ExprSchemable for Expr {
..
}) => Ok(left.nullable(input_schema)? ||
right.nullable(input_schema)?),
Expr::Like(Like { expr, pattern, .. })
- | Expr::ILike(Like { expr, pattern, .. })
| Expr::SimilarTo(Like { expr, pattern, .. }) => {
Ok(expr.nullable(input_schema)? ||
pattern.nullable(input_schema)?)
}
diff --git a/datafusion/expr/src/operator.rs b/datafusion/expr/src/operator.rs
index 2f14d51797..3e5b773dba 100644
--- a/datafusion/expr/src/operator.rs
+++ b/datafusion/expr/src/operator.rs
@@ -361,19 +361,27 @@ impl Not for Expr {
expr,
pattern,
escape_char,
- }) => Expr::Like(Like::new(!negated, expr, pattern, escape_char)),
- Expr::ILike(Like {
- negated,
+ case_insensitive,
+ }) => Expr::Like(Like::new(
+ !negated,
expr,
pattern,
escape_char,
- }) => Expr::ILike(Like::new(!negated, expr, pattern, escape_char)),
+ case_insensitive,
+ )),
Expr::SimilarTo(Like {
negated,
expr,
pattern,
escape_char,
- }) => Expr::SimilarTo(Like::new(!negated, expr, pattern,
escape_char)),
+ case_insensitive,
+ }) => Expr::SimilarTo(Like::new(
+ !negated,
+ expr,
+ pattern,
+ escape_char,
+ case_insensitive,
+ )),
_ => Expr::Not(Box::new(self)),
}
}
diff --git a/datafusion/expr/src/tree_node/expr.rs
b/datafusion/expr/src/tree_node/expr.rs
index 095a0f4e27..c56228d40b 100644
--- a/datafusion/expr/src/tree_node/expr.rs
+++ b/datafusion/expr/src/tree_node/expr.rs
@@ -72,7 +72,6 @@ impl TreeNode for Expr {
vec![left.as_ref().clone(), right.as_ref().clone()]
}
Expr::Like(Like { expr, pattern, .. })
- | Expr::ILike(Like { expr, pattern, .. })
| Expr::SimilarTo(Like { expr, pattern, .. }) => {
vec![expr.as_ref().clone(), pattern.as_ref().clone()]
}
@@ -177,33 +176,26 @@ impl TreeNode for Expr {
expr,
pattern,
escape_char,
+ case_insensitive,
}) => Expr::Like(Like::new(
negated,
transform_boxed(expr, &mut transform)?,
transform_boxed(pattern, &mut transform)?,
escape_char,
- )),
- Expr::ILike(Like {
- negated,
- expr,
- pattern,
- escape_char,
- }) => Expr::ILike(Like::new(
- negated,
- transform_boxed(expr, &mut transform)?,
- transform_boxed(pattern, &mut transform)?,
- escape_char,
+ case_insensitive,
)),
Expr::SimilarTo(Like {
negated,
expr,
pattern,
escape_char,
+ case_insensitive,
}) => Expr::SimilarTo(Like::new(
negated,
transform_boxed(expr, &mut transform)?,
transform_boxed(pattern, &mut transform)?,
escape_char,
+ case_insensitive,
)),
Expr::Not(expr) => Expr::Not(transform_boxed(expr, &mut
transform)?),
Expr::IsNotNull(expr) => {
diff --git a/datafusion/expr/src/utils.rs b/datafusion/expr/src/utils.rs
index 3111579246..3ddfea5105 100644
--- a/datafusion/expr/src/utils.rs
+++ b/datafusion/expr/src/utils.rs
@@ -279,7 +279,6 @@ pub fn expr_to_columns(expr: &Expr, accum: &mut
HashSet<Column>) -> Result<()> {
| Expr::Literal(_)
| Expr::BinaryExpr { .. }
| Expr::Like { .. }
- | Expr::ILike { .. }
| Expr::SimilarTo { .. }
| Expr::Not(_)
| Expr::IsNotNull(_)
diff --git a/datafusion/optimizer/src/analyzer/type_coercion.rs
b/datafusion/optimizer/src/analyzer/type_coercion.rs
index 7cf4a233f7..8279f68e3c 100644
--- a/datafusion/optimizer/src/analyzer/type_coercion.rs
+++ b/datafusion/optimizer/src/analyzer/type_coercion.rs
@@ -206,35 +206,29 @@ impl TreeNodeRewriter for TypeCoercionRewriter {
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(|| {
+ let op_name = if case_insensitive {
+ "ILIKE"
+ } else {
+ "LIKE"
+ };
DataFusionError::Plan(format!(
- "There isn't a common type to coerce {left_type} and
{right_type} in LIKE expression"
+ "There isn't a common type to coerce {left_type} and
{right_type} in {op_name} expression"
))
})?;
let expr = Box::new(expr.cast_to(&coerced_type,
&self.schema)?);
let pattern = Box::new(pattern.cast_to(&coerced_type,
&self.schema)?);
- let expr = Expr::Like(Like::new(negated, expr, pattern,
escape_char));
- Ok(expr)
- }
- Expr::ILike(Like {
- negated,
- expr,
- pattern,
- escape_char,
- }) => {
- 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(|| {
- DataFusionError::Plan(format!(
- "There isn't a common type to coerce {left_type} and
{right_type} in ILIKE expression"
- ))
- })?;
- let expr = Box::new(expr.cast_to(&coerced_type,
&self.schema)?);
- let pattern = Box::new(pattern.cast_to(&coerced_type,
&self.schema)?);
- let expr = Expr::ILike(Like::new(negated, expr, pattern,
escape_char));
+ let expr = Expr::Like(Like::new(
+ negated,
+ expr,
+ pattern,
+ escape_char,
+ case_insensitive,
+ ));
Ok(expr)
}
Expr::BinaryExpr(BinaryExpr { left, op, right }) => {
@@ -1126,7 +1120,7 @@ mod test {
// like : utf8 like "abc"
let expr = Box::new(col("a"));
let pattern = Box::new(lit(ScalarValue::new_utf8("abc")));
- let like_expr = Expr::Like(Like::new(false, expr, pattern, None));
+ let like_expr = Expr::Like(Like::new(false, expr, pattern, None,
false));
let empty = empty_with_type(DataType::Utf8);
let plan =
LogicalPlan::Projection(Projection::try_new(vec![like_expr], empty)?);
let expected = "Projection: a LIKE Utf8(\"abc\")\n EmptyRelation";
@@ -1134,7 +1128,7 @@ mod test {
let expr = Box::new(col("a"));
let pattern = Box::new(lit(ScalarValue::Null));
- let like_expr = Expr::Like(Like::new(false, expr, pattern, None));
+ let like_expr = Expr::Like(Like::new(false, expr, pattern, None,
false));
let empty = empty_with_type(DataType::Utf8);
let plan =
LogicalPlan::Projection(Projection::try_new(vec![like_expr], empty)?);
let expected = "Projection: a LIKE CAST(NULL AS Utf8) AS a LIKE NULL \
@@ -1143,7 +1137,7 @@ mod test {
let expr = Box::new(col("a"));
let pattern = Box::new(lit(ScalarValue::new_utf8("abc")));
- let like_expr = Expr::Like(Like::new(false, expr, pattern, None));
+ let like_expr = Expr::Like(Like::new(false, expr, pattern, None,
false));
let empty = empty_with_type(DataType::Int64);
let plan =
LogicalPlan::Projection(Projection::try_new(vec![like_expr], empty)?);
let err = assert_analyzed_plan_eq(Arc::new(TypeCoercion::new()),
&plan, expected);
@@ -1155,7 +1149,7 @@ mod test {
// ilike
let expr = Box::new(col("a"));
let pattern = Box::new(lit(ScalarValue::new_utf8("abc")));
- let ilike_expr = Expr::ILike(Like::new(false, expr, pattern, None));
+ let ilike_expr = Expr::Like(Like::new(false, expr, pattern, None,
true));
let empty = empty_with_type(DataType::Utf8);
let plan =
LogicalPlan::Projection(Projection::try_new(vec![ilike_expr], empty)?);
let expected = "Projection: a ILIKE Utf8(\"abc\")\n EmptyRelation";
@@ -1163,7 +1157,7 @@ mod test {
let expr = Box::new(col("a"));
let pattern = Box::new(lit(ScalarValue::Null));
- let ilike_expr = Expr::ILike(Like::new(false, expr, pattern, None));
+ let ilike_expr = Expr::Like(Like::new(false, expr, pattern, None,
true));
let empty = empty_with_type(DataType::Utf8);
let plan =
LogicalPlan::Projection(Projection::try_new(vec![ilike_expr], empty)?);
let expected = "Projection: a ILIKE CAST(NULL AS Utf8) AS a ILIKE NULL
\
@@ -1172,7 +1166,7 @@ mod test {
let expr = Box::new(col("a"));
let pattern = Box::new(lit(ScalarValue::new_utf8("abc")));
- let ilike_expr = Expr::ILike(Like::new(false, expr, pattern, None));
+ let ilike_expr = Expr::Like(Like::new(false, expr, pattern, None,
true));
let empty = empty_with_type(DataType::Int64);
let plan =
LogicalPlan::Projection(Projection::try_new(vec![ilike_expr], empty)?);
let err = assert_analyzed_plan_eq(Arc::new(TypeCoercion::new()),
&plan, expected);
diff --git a/datafusion/optimizer/src/push_down_filter.rs
b/datafusion/optimizer/src/push_down_filter.rs
index 92fa2d895b..e62ad7a996 100644
--- a/datafusion/optimizer/src/push_down_filter.rs
+++ b/datafusion/optimizer/src/push_down_filter.rs
@@ -168,7 +168,6 @@ fn can_evaluate_as_join_condition(predicate: &Expr) ->
Result<bool> {
Expr::Alias(_)
| Expr::BinaryExpr(_)
| Expr::Like(_)
- | Expr::ILike(_)
| Expr::SimilarTo(_)
| Expr::Not(_)
| Expr::IsNotNull(_)
diff --git a/datafusion/optimizer/src/simplify_expressions/expr_simplifier.rs
b/datafusion/optimizer/src/simplify_expressions/expr_simplifier.rs
index 447585e70a..5562e10f69 100644
--- a/datafusion/optimizer/src/simplify_expressions/expr_simplifier.rs
+++ b/datafusion/optimizer/src/simplify_expressions/expr_simplifier.rs
@@ -292,7 +292,6 @@ impl<'a> ConstEvaluator<'a> {
| Expr::Negative(_)
| Expr::Between { .. }
| Expr::Like { .. }
- | Expr::ILike { .. }
| Expr::SimilarTo { .. }
| Expr::Case(_)
| Expr::Cast { .. }
@@ -1166,21 +1165,7 @@ impl<'a, S: SimplifyInfo> TreeNodeRewriter for
Simplifier<'a, S> {
pattern,
negated,
escape_char: _,
- }) if !is_null(&expr)
- && matches!(
- pattern.as_ref(),
- Expr::Literal(ScalarValue::Utf8(Some(pattern_str))) if
pattern_str == "%"
- ) =>
- {
- lit(!negated)
- }
-
- // Rules for ILike
- Expr::ILike(Like {
- expr,
- pattern,
- negated,
- escape_char: _,
+ case_insensitive: _,
}) if !is_null(&expr)
&& matches!(
pattern.as_ref(),
@@ -2612,6 +2597,7 @@ mod tests {
expr: Box::new(expr),
pattern: Box::new(lit(pattern)),
escape_char: None,
+ case_insensitive: false,
})
}
@@ -2621,24 +2607,27 @@ mod tests {
expr: Box::new(expr),
pattern: Box::new(lit(pattern)),
escape_char: None,
+ case_insensitive: false,
})
}
fn ilike(expr: Expr, pattern: &str) -> Expr {
- Expr::ILike(Like {
+ Expr::Like(Like {
negated: false,
expr: Box::new(expr),
pattern: Box::new(lit(pattern)),
escape_char: None,
+ case_insensitive: true,
})
}
fn not_ilike(expr: Expr, pattern: &str) -> Expr {
- Expr::ILike(Like {
+ Expr::Like(Like {
negated: true,
expr: Box::new(expr),
pattern: Box::new(lit(pattern)),
escape_char: None,
+ case_insensitive: true,
})
}
diff --git a/datafusion/optimizer/src/simplify_expressions/regex.rs
b/datafusion/optimizer/src/simplify_expressions/regex.rs
index 108f1774b4..27fcfc5dbf 100644
--- a/datafusion/optimizer/src/simplify_expressions/regex.rs
+++ b/datafusion/optimizer/src/simplify_expressions/regex.rs
@@ -86,13 +86,10 @@ impl OperatorMode {
expr,
pattern: Box::new(Expr::Literal(ScalarValue::Utf8(Some(pattern)))),
escape_char: None,
+ case_insensitive: self.i,
};
- if self.i {
- Expr::ILike(like)
- } else {
- Expr::Like(like)
- }
+ Expr::Like(like)
}
fn expr_matches_literal(&self, left: Box<Expr>, right: Box<Expr>) -> Expr {
diff --git a/datafusion/optimizer/src/simplify_expressions/utils.rs
b/datafusion/optimizer/src/simplify_expressions/utils.rs
index 9d36202485..daebfc5a2b 100644
--- a/datafusion/optimizer/src/simplify_expressions/utils.rs
+++ b/datafusion/optimizer/src/simplify_expressions/utils.rs
@@ -300,13 +300,7 @@ pub fn negate_clause(expr: Expr) -> Expr {
like.expr,
like.pattern,
like.escape_char,
- )),
- // not (A ilike B) ===> A not ilike B
- Expr::ILike(like) => Expr::ILike(Like::new(
- !like.negated,
- like.expr,
- like.pattern,
- like.escape_char,
+ like.case_insensitive,
)),
// use not clause
_ => Expr::Not(Box::new(expr)),
diff --git a/datafusion/physical-expr/src/planner.rs
b/datafusion/physical-expr/src/planner.rs
index d3eb6d1db9..72a5ccef34 100644
--- a/datafusion/physical-expr/src/planner.rs
+++ b/datafusion/physical-expr/src/planner.rs
@@ -228,6 +228,7 @@ pub fn create_physical_expr(
expr,
pattern,
escape_char,
+ case_insensitive,
}) => {
if escape_char.is_some() {
return Err(DataFusionError::Execution(
@@ -248,38 +249,7 @@ pub fn create_physical_expr(
)?;
like(
*negated,
- false,
- physical_expr,
- physical_pattern,
- input_schema,
- )
- }
- Expr::ILike(Like {
- negated,
- expr,
- pattern,
- escape_char,
- }) => {
- if escape_char.is_some() {
- return Err(DataFusionError::Execution(
- "ILIKE does not support escape_char".to_string(),
- ));
- }
- let physical_expr = create_physical_expr(
- expr,
- input_dfschema,
- input_schema,
- execution_props,
- )?;
- let physical_pattern = create_physical_expr(
- pattern,
- input_dfschema,
- input_schema,
- execution_props,
- )?;
- like(
- *negated,
- true,
+ *case_insensitive,
physical_expr,
physical_pattern,
input_schema,
diff --git a/datafusion/proto/src/logical_plan/from_proto.rs
b/datafusion/proto/src/logical_plan/from_proto.rs
index 8b70480e7d..202de7df08 100644
--- a/datafusion/proto/src/logical_plan/from_proto.rs
+++ b/datafusion/proto/src/logical_plan/from_proto.rs
@@ -1113,8 +1113,9 @@ pub fn parse_expr(
"pattern",
)?),
parse_escape_char(&like.escape_char)?,
+ false,
))),
- ExprType::Ilike(like) => Ok(Expr::ILike(Like::new(
+ ExprType::Ilike(like) => Ok(Expr::Like(Like::new(
like.negated,
Box::new(parse_required_expr(like.expr.as_deref(), registry,
"expr")?),
Box::new(parse_required_expr(
@@ -1123,6 +1124,7 @@ pub fn parse_expr(
"pattern",
)?),
parse_escape_char(&like.escape_char)?,
+ true,
))),
ExprType::SimilarTo(like) => Ok(Expr::SimilarTo(Like::new(
like.negated,
@@ -1133,6 +1135,7 @@ pub fn parse_expr(
"pattern",
)?),
parse_escape_char(&like.escape_char)?,
+ false,
))),
ExprType::Case(case) => {
let when_then_expr = case
diff --git a/datafusion/proto/src/logical_plan/mod.rs
b/datafusion/proto/src/logical_plan/mod.rs
index ea293067b7..b25f470f8d 100644
--- a/datafusion/proto/src/logical_plan/mod.rs
+++ b/datafusion/proto/src/logical_plan/mod.rs
@@ -2548,6 +2548,7 @@ mod roundtrip_tests {
Box::new(col("col")),
Box::new(lit("[0-9]+")),
escape_char,
+ false,
));
let ctx = SessionContext::new();
roundtrip_expr_test(test_expr, ctx);
@@ -2561,11 +2562,12 @@ mod roundtrip_tests {
#[test]
fn roundtrip_ilike() {
fn ilike(negated: bool, escape_char: Option<char>) {
- let test_expr = Expr::ILike(Like::new(
+ let test_expr = Expr::Like(Like::new(
negated,
Box::new(col("col")),
Box::new(lit("[0-9]+")),
escape_char,
+ true,
));
let ctx = SessionContext::new();
roundtrip_expr_test(test_expr, ctx);
@@ -2584,6 +2586,7 @@ mod roundtrip_tests {
Box::new(col("col")),
Box::new(lit("[0-9]+")),
escape_char,
+ false,
));
let ctx = SessionContext::new();
roundtrip_expr_test(test_expr, ctx);
diff --git a/datafusion/proto/src/logical_plan/to_proto.rs
b/datafusion/proto/src/logical_plan/to_proto.rs
index 30bb30950c..e899eee6fe 100644
--- a/datafusion/proto/src/logical_plan/to_proto.rs
+++ b/datafusion/proto/src/logical_plan/to_proto.rs
@@ -526,31 +526,34 @@ impl TryFrom<&Expr> for protobuf::LogicalExprNode {
expr,
pattern,
escape_char,
+ case_insensitive,
}) => {
- let pb = Box::new(protobuf::LikeNode {
- negated: *negated,
- expr: Some(Box::new(expr.as_ref().try_into()?)),
- pattern: Some(Box::new(pattern.as_ref().try_into()?)),
- escape_char: escape_char.map(|ch|
ch.to_string()).unwrap_or_default(),
- });
- Self {
- expr_type: Some(ExprType::Like(pb)),
- }
- }
- Expr::ILike(Like {
- negated,
- expr,
- pattern,
- escape_char,
- }) => {
- let pb = Box::new(protobuf::ILikeNode {
- negated: *negated,
- expr: Some(Box::new(expr.as_ref().try_into()?)),
- pattern: Some(Box::new(pattern.as_ref().try_into()?)),
- escape_char: escape_char.map(|ch|
ch.to_string()).unwrap_or_default(),
- });
- Self {
- expr_type: Some(ExprType::Ilike(pb)),
+ if *case_insensitive {
+ let pb = Box::new(protobuf::ILikeNode {
+ negated: *negated,
+ expr: Some(Box::new(expr.as_ref().try_into()?)),
+ pattern: Some(Box::new(pattern.as_ref().try_into()?)),
+ escape_char: escape_char
+ .map(|ch| ch.to_string())
+ .unwrap_or_default(),
+ });
+
+ Self {
+ expr_type: Some(ExprType::Ilike(pb)),
+ }
+ } else {
+ let pb = Box::new(protobuf::LikeNode {
+ negated: *negated,
+ expr: Some(Box::new(expr.as_ref().try_into()?)),
+ pattern: Some(Box::new(pattern.as_ref().try_into()?)),
+ escape_char: escape_char
+ .map(|ch| ch.to_string())
+ .unwrap_or_default(),
+ });
+
+ Self {
+ expr_type: Some(ExprType::Like(pb)),
+ }
}
}
Expr::SimilarTo(Like {
@@ -558,6 +561,7 @@ impl TryFrom<&Expr> for protobuf::LogicalExprNode {
expr,
pattern,
escape_char,
+ case_insensitive: _,
}) => {
let pb = Box::new(protobuf::SimilarToNode {
negated: *negated,
diff --git a/datafusion/sql/src/expr/mod.rs b/datafusion/sql/src/expr/mod.rs
index 06102424f7..b21c205ffc 100644
--- a/datafusion/sql/src/expr/mod.rs
+++ b/datafusion/sql/src/expr/mod.rs
@@ -269,9 +269,9 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
negated,
} => self.sql_in_list_to_expr(*expr, list, negated, schema,
planner_context),
- SQLExpr::Like { negated, expr, pattern, escape_char } =>
self.sql_like_to_expr(negated, *expr, *pattern, escape_char, schema,
planner_context),
+ SQLExpr::Like { negated, expr, pattern, escape_char } =>
self.sql_like_to_expr(negated, *expr, *pattern, escape_char, schema,
planner_context,false),
- SQLExpr::ILike { negated, expr, pattern, escape_char } =>
self.sql_ilike_to_expr(negated, *expr, *pattern, escape_char, schema,
planner_context),
+ SQLExpr::ILike { negated, expr, pattern, escape_char } =>
self.sql_like_to_expr(negated, *expr, *pattern, escape_char, schema,
planner_context,true),
SQLExpr::SimilarTo { negated, expr, pattern, escape_char } =>
self.sql_similarto_to_expr(negated, *expr, *pattern, escape_char, schema,
planner_context),
@@ -389,6 +389,7 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
)))
}
+ #[allow(clippy::too_many_arguments)]
fn sql_like_to_expr(
&self,
negated: bool,
@@ -397,6 +398,7 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
escape_char: Option<char>,
schema: &DFSchema,
planner_context: &mut PlannerContext,
+ case_insensitive: bool,
) -> Result<Expr> {
let pattern = self.sql_expr_to_logical_expr(pattern, schema,
planner_context)?;
let pattern_type = pattern.get_type(schema)?;
@@ -410,30 +412,7 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
Box::new(self.sql_expr_to_logical_expr(expr, schema,
planner_context)?),
Box::new(pattern),
escape_char,
- )))
- }
-
- fn sql_ilike_to_expr(
- &self,
- negated: bool,
- expr: SQLExpr,
- pattern: SQLExpr,
- escape_char: Option<char>,
- schema: &DFSchema,
- planner_context: &mut PlannerContext,
- ) -> Result<Expr> {
- let pattern = self.sql_expr_to_logical_expr(pattern, schema,
planner_context)?;
- let pattern_type = pattern.get_type(schema)?;
- if pattern_type != DataType::Utf8 && pattern_type != DataType::Null {
- return Err(DataFusionError::Plan(
- "Invalid pattern in ILIKE expression".to_string(),
- ));
- }
- Ok(Expr::ILike(Like::new(
- negated,
- Box::new(self.sql_expr_to_logical_expr(expr, schema,
planner_context)?),
- Box::new(pattern),
- escape_char,
+ case_insensitive,
)))
}
@@ -458,6 +437,7 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
Box::new(self.sql_expr_to_logical_expr(expr, schema,
planner_context)?),
Box::new(pattern),
escape_char,
+ false,
)))
}
diff --git a/datafusion/sql/src/utils.rs b/datafusion/sql/src/utils.rs
index 1cf4138773..27e62526b8 100644
--- a/datafusion/sql/src/utils.rs
+++ b/datafusion/sql/src/utils.rs
@@ -248,33 +248,26 @@ where
expr,
pattern,
escape_char,
+ case_insensitive,
}) => Ok(Expr::Like(Like::new(
*negated,
Box::new(clone_with_replacement(expr, replacement_fn)?),
Box::new(clone_with_replacement(pattern, replacement_fn)?),
*escape_char,
- ))),
- Expr::ILike(Like {
- negated,
- expr,
- pattern,
- escape_char,
- }) => Ok(Expr::ILike(Like::new(
- *negated,
- Box::new(clone_with_replacement(expr, replacement_fn)?),
- Box::new(clone_with_replacement(pattern, replacement_fn)?),
- *escape_char,
+ *case_insensitive,
))),
Expr::SimilarTo(Like {
negated,
expr,
pattern,
escape_char,
+ case_insensitive,
}) => Ok(Expr::SimilarTo(Like::new(
*negated,
Box::new(clone_with_replacement(expr, replacement_fn)?),
Box::new(clone_with_replacement(pattern, replacement_fn)?),
*escape_char,
+ *case_insensitive,
))),
Expr::Case(case) => Ok(Expr::Case(Case::new(
match &case.expr {
diff --git a/datafusion/substrait/src/logical_plan/consumer.rs
b/datafusion/substrait/src/logical_plan/consumer.rs
index 7b54bea493..a2f95a0d89 100644
--- a/datafusion/substrait/src/logical_plan/consumer.rs
+++ b/datafusion/substrait/src/logical_plan/consumer.rs
@@ -71,7 +71,7 @@ enum ScalarFunctionType {
Not,
/// [Expr::Like] Used for filtering rows based on the given wildcard
pattern. Case sensitive
Like,
- /// [Expr::ILike] Case insensitive operator counterpart of `Like`
+ /// [Expr::Like] Case insensitive operator counterpart of `Like`
ILike,
}
@@ -1333,19 +1333,11 @@ async fn make_datafusion_like(
)))
};
- if case_insensitive {
- Ok(Arc::new(Expr::ILike(Like {
- negated: false,
- expr: Box::new(expr),
- pattern: Box::new(pattern),
- escape_char: escape_char.map(|c| c.chars().next().unwrap()),
- })))
- } else {
- Ok(Arc::new(Expr::Like(Like {
- negated: false,
- expr: Box::new(expr),
- pattern: Box::new(pattern),
- escape_char: escape_char.map(|c| c.chars().next().unwrap()),
- })))
- }
+ Ok(Arc::new(Expr::Like(Like {
+ negated: false,
+ expr: Box::new(expr),
+ pattern: Box::new(pattern),
+ escape_char: escape_char.map(|c| c.chars().next().unwrap()),
+ case_insensitive,
+ })))
}
diff --git a/datafusion/substrait/src/logical_plan/producer.rs
b/datafusion/substrait/src/logical_plan/producer.rs
index ece1651683..bddcc577ad 100644
--- a/datafusion/substrait/src/logical_plan/producer.rs
+++ b/datafusion/substrait/src/logical_plan/producer.rs
@@ -919,23 +919,9 @@ pub fn to_substrait_rex(
expr,
pattern,
escape_char,
+ case_insensitive,
}) => make_substrait_like_expr(
- false,
- *negated,
- expr,
- pattern,
- *escape_char,
- schema,
- col_ref_offset,
- extension_info,
- ),
- Expr::ILike(Like {
- negated,
- expr,
- pattern,
- escape_char,
- }) => make_substrait_like_expr(
- true,
+ *case_insensitive,
*negated,
expr,
pattern,