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,


Reply via email to