Skip to content

Commit 5a157d6

Browse files
authored
feat(optimize): rewrite LIKE '%' (e.g., '%%', '%%%') as IS NOT NULL (#18288)
* optimize like followed by '%' or '%....' literal string
1 parent 394f11e commit 5a157d6

File tree

3 files changed

+43
-3
lines changed

3 files changed

+43
-3
lines changed

โ€Žsrc/query/sql/src/planner/semantic/type_check.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4434,7 +4434,15 @@ impl<'a> TypeChecker<'a> {
44344434
} else {
44354435
Cow::Borrowed(like_str)
44364436
};
4437-
if check_const(&new_like_str) {
4437+
if check_percent(&new_like_str) {
4438+
// Convert to `a is not null`
4439+
let is_not_null = Expr::IsNull {
4440+
span: None,
4441+
expr: Box::new(left.clone()),
4442+
not: true,
4443+
};
4444+
self.resolve(&is_not_null)
4445+
} else if check_const(&new_like_str) {
44384446
// Convert to equal comparison
44394447
self.resolve_binary_op(span, &BinaryOperator::Eq, left, right)
44404448
} else if check_prefix(&new_like_str) {
@@ -5762,6 +5770,11 @@ pub fn validate_function_arg(
57625770
}
57635771
}
57645772

5773+
// optimize special cases for like expression
5774+
fn check_percent(like_str: &str) -> bool {
5775+
!like_str.is_empty() && like_str.chars().all(|c| c == '%')
5776+
}
5777+
57655778
// Some check functions for like expression
57665779
fn check_const(like_str: &str) -> bool {
57675780
for char in like_str.chars() {

โ€Žtests/sqllogictests/suites/mode/standalone/explain/explain_like.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ explain select * from t1 where s like '%';
1919
----
2020
Filter
2121
โ”œโ”€โ”€ output columns: [t1.s (#0)]
22-
โ”œโ”€โ”€ filters: [is_true(like(t1.s (#0), '%'))]
22+
โ”œโ”€โ”€ filters: [is_not_null(t1.s (#0))]
2323
โ”œโ”€โ”€ estimated rows: 5.00
2424
โ””โ”€โ”€ TableScan
2525
โ”œโ”€โ”€ table: default.default.t1
@@ -29,7 +29,7 @@ Filter
2929
โ”œโ”€โ”€ partitions total: 1
3030
โ”œโ”€โ”€ partitions scanned: 1
3131
โ”œโ”€โ”€ pruning stats: [segments: <range pruning: 1 to 1>, blocks: <range pruning: 1 to 1>]
32-
โ”œโ”€โ”€ push downs: [filters: [is_true(like(t1.s (#0), '%'))], limit: NONE]
32+
โ”œโ”€โ”€ push downs: [filters: [is_not_null(t1.s (#0))], limit: NONE]
3333
โ””โ”€โ”€ estimated rows: 5.00
3434

3535
query T

โ€Žtests/sqllogictests/suites/mode/standalone/explain/selectivity/is_not_null.test

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,30 @@ Filter
2424

2525
statement ok
2626
DROP TABLE twocolumn;
27+
28+
statement ok
29+
create table if not exists t_user(id int not null, name varchar(10));
30+
31+
statement ok
32+
insert into t_user values (10, 'alice'), (103, 'bod');
33+
34+
query T
35+
EXPLAIN SELECT * FROM t_user WHERE name LIKE '%';
36+
----
37+
Filter
38+
โ”œโ”€โ”€ output columns: [t_user.id (#0), t_user.name (#1)]
39+
โ”œโ”€โ”€ filters: [is_not_null(t_user.name (#1))]
40+
โ”œโ”€โ”€ estimated rows: 2.00
41+
โ””โ”€โ”€ TableScan
42+
โ”œโ”€โ”€ table: default.default.t_user
43+
โ”œโ”€โ”€ output columns: [id (#0), name (#1)]
44+
โ”œโ”€โ”€ read rows: 2
45+
โ”œโ”€โ”€ read size: < 1 KiB
46+
โ”œโ”€โ”€ partitions total: 1
47+
โ”œโ”€โ”€ partitions scanned: 1
48+
โ”œโ”€โ”€ pruning stats: [segments: <range pruning: 1 to 1>, blocks: <range pruning: 1 to 1>]
49+
โ”œโ”€โ”€ push downs: [filters: [is_not_null(t_user.name (#1))], limit: NONE]
50+
โ””โ”€โ”€ estimated rows: 2.00
51+
52+
statement ok
53+
drop table t_user;

0 commit comments

Comments
ย (0)