Skip to content

Commit 2bfed6a

Browse files
andygroveMazterQyou
authored andcommitted
Add top-level Like, ILike, SimilarTo expressions in logical plan (apache#3298)
1 parent e257063 commit 2bfed6a

File tree

13 files changed

+472
-1
lines changed

13 files changed

+472
-1
lines changed

datafusion/core/src/datasource/listing/helpers.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ impl ExpressionVisitor for ApplicabilityVisitor<'_> {
9393
| Expr::BinaryExpr { .. }
9494
| Expr::AnyExpr { .. }
9595
| Expr::Between { .. }
96+
| Expr::Like { .. }
97+
| Expr::ILike { .. }
98+
| Expr::SimilarTo { .. }
9699
| Expr::InList { .. }
97100
| Expr::GetIndexedField { .. }
98101
| Expr::Case { .. } => Recursion::Continue(self),

datafusion/core/src/logical_plan/expr_rewriter.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,39 @@ impl ExprRewritable for Expr {
127127
op,
128128
right: rewrite_boxed(right, rewriter)?,
129129
},
130+
Expr::Like {
131+
negated,
132+
expr,
133+
pattern,
134+
escape_char,
135+
} => Expr::Like {
136+
negated,
137+
expr: rewrite_boxed(expr, rewriter)?,
138+
pattern: rewrite_boxed(pattern, rewriter)?,
139+
escape_char,
140+
},
141+
Expr::ILike {
142+
negated,
143+
expr,
144+
pattern,
145+
escape_char,
146+
} => Expr::ILike {
147+
negated,
148+
expr: rewrite_boxed(expr, rewriter)?,
149+
pattern: rewrite_boxed(pattern, rewriter)?,
150+
escape_char,
151+
},
152+
Expr::SimilarTo {
153+
negated,
154+
expr,
155+
pattern,
156+
escape_char,
157+
} => Expr::SimilarTo {
158+
negated,
159+
expr: rewrite_boxed(expr, rewriter)?,
160+
pattern: rewrite_boxed(pattern, rewriter)?,
161+
escape_char,
162+
},
130163
Expr::Not(expr) => Expr::Not(rewrite_boxed(expr, rewriter)?),
131164
Expr::IsNotNull(expr) => Expr::IsNotNull(rewrite_boxed(expr, rewriter)?),
132165
Expr::IsNull(expr) => Expr::IsNull(rewrite_boxed(expr, rewriter)?),

datafusion/core/src/logical_plan/expr_schema.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ impl ExprSchemable for Expr {
122122
op,
123123
&right.get_type(schema)?,
124124
),
125+
Expr::Like { .. } | Expr::ILike { .. } | Expr::SimilarTo { .. } => {
126+
Ok(DataType::Boolean)
127+
}
125128
Expr::Wildcard => Err(DataFusionError::Internal(
126129
"Wildcard expressions are not valid in a logical query plan".to_owned(),
127130
)),
@@ -195,6 +198,9 @@ impl ExprSchemable for Expr {
195198
ref right,
196199
..
197200
} => Ok(left.nullable(input_schema)? || right.nullable(input_schema)?),
201+
Expr::Like { expr, .. } => expr.nullable(input_schema),
202+
Expr::ILike { expr, .. } => expr.nullable(input_schema),
203+
Expr::SimilarTo { expr, .. } => expr.nullable(input_schema),
198204
Expr::Wildcard => Err(DataFusionError::Internal(
199205
"Wildcard expressions are not valid in a logical query plan".to_owned(),
200206
)),

datafusion/core/src/logical_plan/expr_visitor.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,18 @@ impl ExprVisitable for Expr {
120120
let visitor = left.accept(visitor)?;
121121
right.accept(visitor)
122122
}
123+
Expr::Like { expr, pattern, .. } => {
124+
let visitor = expr.accept(visitor)?;
125+
pattern.accept(visitor)
126+
}
127+
Expr::ILike { expr, pattern, .. } => {
128+
let visitor = expr.accept(visitor)?;
129+
pattern.accept(visitor)
130+
}
131+
Expr::SimilarTo { expr, pattern, .. } => {
132+
let visitor = expr.accept(visitor)?;
133+
pattern.accept(visitor)
134+
}
123135
Expr::Between {
124136
expr, low, high, ..
125137
} => {

datafusion/core/src/optimizer/common_subexpr_eliminate.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,18 @@ impl ExprIdentifierVisitor<'_> {
447447
desc.push_str("Between-");
448448
desc.push_str(&negated.to_string());
449449
}
450+
Expr::Like { negated, .. } => {
451+
desc.push_str("Like-");
452+
desc.push_str(&negated.to_string());
453+
}
454+
Expr::ILike { negated, .. } => {
455+
desc.push_str("ILike-");
456+
desc.push_str(&negated.to_string());
457+
}
458+
Expr::SimilarTo { negated, .. } => {
459+
desc.push_str("SimilarTo-");
460+
desc.push_str(&negated.to_string());
461+
}
450462
Expr::Case { .. } => {
451463
desc.push_str("Case-");
452464
}

datafusion/core/src/optimizer/simplify_expressions.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,9 @@ impl<'a> ConstEvaluator<'a> {
405405
| Expr::IsNull(_)
406406
| Expr::Negative(_)
407407
| Expr::Between { .. }
408+
| Expr::Like { .. }
409+
| Expr::ILike { .. }
410+
| Expr::SimilarTo { .. }
408411
| Expr::Case { .. }
409412
| Expr::Cast { .. }
410413
| Expr::TryCast { .. }

datafusion/core/src/optimizer/utils.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ impl ExpressionVisitor for ColumnNameVisitor<'_> {
7474
| Expr::Literal(_)
7575
| Expr::BinaryExpr { .. }
7676
| Expr::AnyExpr { .. }
77+
| Expr::Like { .. }
78+
| Expr::ILike { .. }
79+
| Expr::SimilarTo { .. }
7780
| Expr::Not(_)
7881
| Expr::IsNotNull(_)
7982
| Expr::IsNull(_)
@@ -313,6 +316,11 @@ pub fn expr_sub_expressions(expr: &Expr) -> Result<Vec<Expr>> {
313316
Expr::AnyExpr { left, right, .. } => {
314317
Ok(vec![left.as_ref().to_owned(), right.as_ref().to_owned()])
315318
}
319+
Expr::Like { expr, pattern, .. }
320+
| Expr::ILike { expr, pattern, .. }
321+
| Expr::SimilarTo { expr, pattern, .. } => {
322+
Ok(vec![expr.as_ref().to_owned(), pattern.as_ref().to_owned()])
323+
}
316324
Expr::IsNull(expr)
317325
| Expr::IsNotNull(expr)
318326
| Expr::Cast { expr, .. }
@@ -407,6 +415,36 @@ pub fn rewrite_expression(expr: &Expr, expressions: &[Expr]) -> Result<Expr> {
407415
op: *op,
408416
right: Box::new(expressions[1].clone()),
409417
}),
418+
Expr::Like {
419+
negated,
420+
escape_char,
421+
..
422+
} => Ok(Expr::Like {
423+
negated: *negated,
424+
expr: Box::new(expressions[0].clone()),
425+
pattern: Box::new(expressions[1].clone()),
426+
escape_char: *escape_char,
427+
}),
428+
Expr::ILike {
429+
negated,
430+
escape_char,
431+
..
432+
} => Ok(Expr::ILike {
433+
negated: *negated,
434+
expr: Box::new(expressions[0].clone()),
435+
pattern: Box::new(expressions[1].clone()),
436+
escape_char: *escape_char,
437+
}),
438+
Expr::SimilarTo {
439+
negated,
440+
escape_char,
441+
..
442+
} => Ok(Expr::SimilarTo {
443+
negated: *negated,
444+
expr: Box::new(expressions[0].clone()),
445+
pattern: Box::new(expressions[1].clone()),
446+
escape_char: *escape_char,
447+
}),
410448
Expr::IsNull(_) => Ok(Expr::IsNull(Box::new(expressions[0].clone()))),
411449
Expr::IsNotNull(_) => Ok(Expr::IsNotNull(Box::new(expressions[0].clone()))),
412450
Expr::ScalarFunction { fun, .. } => Ok(Expr::ScalarFunction {

datafusion/core/src/physical_plan/planner.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,63 @@ fn create_physical_name(e: &Expr, is_first_expr: bool) -> Result<String> {
218218
Ok(format!("{} BETWEEN {} AND {}", expr, low, high))
219219
}
220220
}
221+
Expr::Like {
222+
negated,
223+
expr,
224+
pattern,
225+
escape_char,
226+
} => {
227+
let expr = create_physical_name(expr, false)?;
228+
let pattern = create_physical_name(pattern, false)?;
229+
let escape = if let Some(char) = escape_char {
230+
format!("CHAR '{}'", char)
231+
} else {
232+
"".to_string()
233+
};
234+
if *negated {
235+
Ok(format!("{} NOT LIKE {}{}", expr, pattern, escape))
236+
} else {
237+
Ok(format!("{} LIKE {}{}", expr, pattern, escape))
238+
}
239+
}
240+
Expr::ILike {
241+
negated,
242+
expr,
243+
pattern,
244+
escape_char,
245+
} => {
246+
let expr = create_physical_name(expr, false)?;
247+
let pattern = create_physical_name(pattern, false)?;
248+
let escape = if let Some(char) = escape_char {
249+
format!("CHAR '{}'", char)
250+
} else {
251+
"".to_string()
252+
};
253+
if *negated {
254+
Ok(format!("{} NOT ILIKE {}{}", expr, pattern, escape))
255+
} else {
256+
Ok(format!("{} ILIKE {}{}", expr, pattern, escape))
257+
}
258+
}
259+
Expr::SimilarTo {
260+
negated,
261+
expr,
262+
pattern,
263+
escape_char,
264+
} => {
265+
let expr = create_physical_name(expr, false)?;
266+
let pattern = create_physical_name(pattern, false)?;
267+
let escape = if let Some(char) = escape_char {
268+
format!("CHAR '{}'", char)
269+
} else {
270+
"".to_string()
271+
};
272+
if *negated {
273+
Ok(format!("{} NOT SIMILAR TO {}{}", expr, pattern, escape))
274+
} else {
275+
Ok(format!("{} SIMILAR TO {}{}", expr, pattern, escape))
276+
}
277+
}
221278
Expr::Sort { .. } => Err(DataFusionError::Internal(
222279
"Create physical name does not support sort expression".to_string(),
223280
)),

datafusion/core/src/sql/utils.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,39 @@ where
303303
op: *op,
304304
right: Box::new(clone_with_replacement(&**right, replacement_fn)?),
305305
}),
306+
Expr::Like {
307+
negated,
308+
expr,
309+
pattern,
310+
escape_char,
311+
} => Ok(Expr::Like {
312+
negated: *negated,
313+
expr: Box::new(clone_with_replacement(expr, replacement_fn)?),
314+
pattern: Box::new(clone_with_replacement(pattern, replacement_fn)?),
315+
escape_char: *escape_char,
316+
}),
317+
Expr::ILike {
318+
negated,
319+
expr,
320+
pattern,
321+
escape_char,
322+
} => Ok(Expr::ILike {
323+
negated: *negated,
324+
expr: Box::new(clone_with_replacement(expr, replacement_fn)?),
325+
pattern: Box::new(clone_with_replacement(pattern, replacement_fn)?),
326+
escape_char: *escape_char,
327+
}),
328+
Expr::SimilarTo {
329+
negated,
330+
expr,
331+
pattern,
332+
escape_char,
333+
} => Ok(Expr::SimilarTo {
334+
negated: *negated,
335+
expr: Box::new(clone_with_replacement(expr, replacement_fn)?),
336+
pattern: Box::new(clone_with_replacement(pattern, replacement_fn)?),
337+
escape_char: *escape_char,
338+
}),
306339
Expr::Case {
307340
expr: case_expr_opt,
308341
when_then_expr,

0 commit comments

Comments
 (0)