From 2b0db64d445123c3fdff762341c545dc55f18e2c Mon Sep 17 00:00:00 2001 From: albertlockett Date: Fri, 1 May 2026 12:44:27 -0400 Subject: [PATCH 1/2] arrow-string: added like::eq_ascii_ignore_case kernel --- arrow-string/src/like.rs | 53 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/arrow-string/src/like.rs b/arrow-string/src/like.rs index 55fe292ecb76..3eb6cb5070cb 100644 --- a/arrow-string/src/like.rs +++ b/arrow-string/src/like.rs @@ -34,6 +34,7 @@ pub(crate) enum Op { Like(bool), ILike(bool), Contains, + EqIgnoreAsciiCase, StartsWith, EndsWith, } @@ -46,6 +47,7 @@ impl std::fmt::Display for Op { Op::ILike(false) => write!(f, "ILIKE"), Op::ILike(true) => write!(f, "NILIKE"), Op::Contains => write!(f, "CONTAINS"), + Op::EqIgnoreAsciiCase => write!(f, "EQ_IGNORE_ASCII_CASE"), Op::StartsWith => write!(f, "STARTS_WITH"), Op::EndsWith => write!(f, "ENDS_WITH"), } @@ -124,7 +126,7 @@ pub fn nilike(left: &dyn Datum, right: &dyn Datum) -> Result Result Result Result Result { + like_op(Op::EqIgnoreAsciiCase, left, right) +} + fn like_op(op: Op, lhs: &dyn Datum, rhs: &dyn Datum) -> Result { use arrow_schema::DataType::*; let (l, l_s) = lhs.get(); @@ -328,6 +354,7 @@ fn op_scalar<'a, T: StringArrayType<'a>>( Op::Like(neg) => Predicate::like(r)?.evaluate_array(l, neg), Op::ILike(neg) => Predicate::ilike(r, l.is_ascii())?.evaluate_array(l, neg), Op::Contains => Predicate::contains(r).evaluate_array(l, false), + Op::EqIgnoreAsciiCase => Predicate::IEqAscii(r).evaluate_array(l, false), Op::StartsWith => Predicate::StartsWith(r).evaluate_array(l, false), Op::EndsWith => Predicate::EndsWith(r).evaluate_array(l, false), }; @@ -362,6 +389,10 @@ fn op_binary<'a>( Op::Like(neg) => binary_predicate(l, r, neg, Predicate::like), Op::ILike(neg) => binary_predicate(l, r, neg, |s| Predicate::ilike(s, false)), Op::Contains => Ok(l.zip(r).map(|(l, r)| Some(str_contains(l?, r?))).collect()), + Op::EqIgnoreAsciiCase => Ok(l + .zip(r) + .map(|(l, r)| Some(Predicate::IEqAscii(l?).evaluate(r?))) + .collect()), Op::StartsWith => Ok(l .zip(r) .map(|(l, r)| Some(Predicate::StartsWith(r?).evaluate(l?))) @@ -1394,6 +1425,22 @@ mod tests { vec![true, false, true, true, true] ); + test_utf8!( + test_utf8_array_eq_ignore_ascii_case, + vec!["arrow", "arrow", "arrow", "parquet", "parquet"], + vec!["arrow", "ARROW", "aRrOw", "arrow", "ARROW"], + eq_ignore_ascii_case, + vec![true, true, true, false, false] + ); + + test_utf8_scalar!( + test_utf8_array_eq_ignore_ascii_case_scalar, + vec!["arrow", "aRrOW", "ARROW", "parquet", "PARQUET"], + "arrow", + eq_ignore_ascii_case, + vec![true, true, true, false, false] + ); + #[test] fn test_dict_like_kernels() { let data = vec![ From 1f3b03cbb839c3d5172f92b686d56fc741cb3121 Mon Sep 17 00:00:00 2001 From: albertlockett Date: Tue, 5 May 2026 19:33:42 -0400 Subject: [PATCH 2/2] PR feedback --- arrow-string/src/like.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/arrow-string/src/like.rs b/arrow-string/src/like.rs index 3eb6cb5070cb..2ad7e1cfd961 100644 --- a/arrow-string/src/like.rs +++ b/arrow-string/src/like.rs @@ -15,7 +15,11 @@ // specific language governing permissions and limitations // under the License. -//! Provide SQL's LIKE operators for Arrow's string arrays +//! String predicate kernels for Arrow arrays. +//! +//! Provides SQL `LIKE`/`ILIKE` kernels as well as related +//! string predicates such as `contains`, `starts_with`, `ends_with`, and +//! ASCII case-insensitive equality. use crate::predicate::Predicate; @@ -189,7 +193,7 @@ pub fn contains(left: &dyn Datum, right: &dyn Datum) -> Result