Skip to content

Commit cf56e72

Browse files
authored
feat(parser): parse [!]~~[*] operators to [NOT] [I]LIKE (#11748)
Signed-off-by: TennyZhuang <[email protected]>
1 parent 058deae commit cf56e72

File tree

5 files changed

+65
-14
lines changed

5 files changed

+65
-14
lines changed

src/sqlparser/src/parser.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,6 +1333,10 @@ impl Parser {
13331333
Token::TildeAsterisk => Some(BinaryOperator::PGRegexIMatch),
13341334
Token::ExclamationMarkTilde => Some(BinaryOperator::PGRegexNotMatch),
13351335
Token::ExclamationMarkTildeAsterisk => Some(BinaryOperator::PGRegexNotIMatch),
1336+
Token::DoubleTilde => Some(BinaryOperator::Like),
1337+
Token::DoubleTildeAsterisk => Some(BinaryOperator::ILike),
1338+
Token::ExclamationMarkDoubleTilde => Some(BinaryOperator::NotLike),
1339+
Token::ExclamationMarkDoubleTildeAsterisk => Some(BinaryOperator::NotILike),
13361340
Token::Arrow => Some(BinaryOperator::Arrow),
13371341
Token::LongArrow => Some(BinaryOperator::LongArrow),
13381342
Token::HashArrow => Some(BinaryOperator::HashArrow),
@@ -1625,6 +1629,10 @@ impl Parser {
16251629
| Token::TildeAsterisk
16261630
| Token::ExclamationMarkTilde
16271631
| Token::ExclamationMarkTildeAsterisk
1632+
| Token::DoubleTilde
1633+
| Token::DoubleTildeAsterisk
1634+
| Token::ExclamationMarkDoubleTilde
1635+
| Token::ExclamationMarkDoubleTildeAsterisk
16281636
| Token::Concat
16291637
| Token::Prefix
16301638
| Token::Arrow

src/sqlparser/src/tokenizer.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,14 @@ pub enum Token {
134134
ExclamationMarkTilde,
135135
/// `!~*` , a case insensitive not match regular expression operator in PostgreSQL
136136
ExclamationMarkTildeAsterisk,
137+
/// `~~`, a case sensitive LIKE expression operator in PostgreSQL
138+
DoubleTilde,
139+
/// `~~*` , a case insensitive ILIKE regular expression operator in PostgreSQL
140+
DoubleTildeAsterisk,
141+
/// `!~~` , a case sensitive NOT LIKE regular expression operator in PostgreSQL
142+
ExclamationMarkDoubleTilde,
143+
/// `!~~*` , a case insensitive NOT ILIKE regular expression operator in PostgreSQL
144+
ExclamationMarkDoubleTildeAsterisk,
137145
/// `<<`, a bitwise shift left operator in PostgreSQL
138146
ShiftLeft,
139147
/// `>>`, a bitwise shift right operator in PostgreSQL
@@ -210,6 +218,10 @@ impl fmt::Display for Token {
210218
Token::TildeAsterisk => f.write_str("~*"),
211219
Token::ExclamationMarkTilde => f.write_str("!~"),
212220
Token::ExclamationMarkTildeAsterisk => f.write_str("!~*"),
221+
Token::DoubleTilde => f.write_str("~~"),
222+
Token::DoubleTildeAsterisk => f.write_str("~~*"),
223+
Token::ExclamationMarkDoubleTilde => f.write_str("!~~"),
224+
Token::ExclamationMarkDoubleTildeAsterisk => f.write_str("!~~*"),
213225
Token::AtSign => f.write_str("@"),
214226
Token::ShiftLeft => f.write_str("<<"),
215227
Token::ShiftRight => f.write_str(">>"),
@@ -651,6 +663,16 @@ impl<'a> Tokenizer<'a> {
651663
Some('~') => {
652664
chars.next();
653665
match chars.peek() {
666+
Some('~') => {
667+
chars.next();
668+
match chars.peek() {
669+
Some('*') => self.consume_and_return(
670+
chars,
671+
Token::ExclamationMarkDoubleTildeAsterisk,
672+
),
673+
_ => Ok(Some(Token::ExclamationMarkDoubleTilde)),
674+
}
675+
}
654676
Some('*') => self
655677
.consume_and_return(chars, Token::ExclamationMarkTildeAsterisk),
656678
_ => Ok(Some(Token::ExclamationMarkTilde)),
@@ -707,6 +729,15 @@ impl<'a> Tokenizer<'a> {
707729
'~' => {
708730
chars.next(); // consume
709731
match chars.peek() {
732+
Some('~') => {
733+
chars.next();
734+
match chars.peek() {
735+
Some('*') => {
736+
self.consume_and_return(chars, Token::DoubleTildeAsterisk)
737+
}
738+
_ => Ok(Some(Token::DoubleTilde)),
739+
}
740+
}
710741
Some('*') => self.consume_and_return(chars, Token::TildeAsterisk),
711742
_ => Ok(Some(Token::Tilde)),
712743
}

src/sqlparser/tests/testdata/select.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,15 @@
105105
formatted_ast: 'Query(Query { with: None, body: Select(Select { distinct: All, projection: [UnnamedExpr(Function(Function { name: ObjectName([Ident { value: "percentile_cont", quote_style: None }]), args: [Unnamed(Expr(Value(Number("0.3"))))], over: None, distinct: false, order_by: [], filter: None, within_group: Some(OrderByExpr { expr: Identifier(Ident { value: "x", quote_style: None }), asc: Some(false), nulls_first: None }) }))], from: [TableWithJoins { relation: TableFunction { name: ObjectName([Ident { value: "unnest", quote_style: None }]), alias: Some(TableAlias { name: Ident { value: "x", quote_style: None }, columns: [] }), args: [Unnamed(Expr(Array(Array { elem: [Value(Number("1")), Value(Number("2")), Value(Number("4")), Value(Number("5")), Value(Number("10"))], named: true })))] }, joins: [] }], lateral_views: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None })'
106106
- input: select percentile_cont(0.3) within group (order by x, y desc) from t
107107
error_msg: 'sql parser error: only one arg in order by is expected here'
108+
- input: select 'apple' ~~ 'app%'
109+
formatted_sql: SELECT 'apple' LIKE 'app%'
110+
formatted_ast: 'Query(Query { with: None, body: Select(Select { distinct: All, projection: [UnnamedExpr(BinaryOp { left: Value(SingleQuotedString("apple")), op: Like, right: Value(SingleQuotedString("app%")) })], from: [], lateral_views: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None })'
111+
- input: select 'apple' ~~* 'App%'
112+
formatted_sql: SELECT 'apple' ILIKE 'App%'
113+
formatted_ast: 'Query(Query { with: None, body: Select(Select { distinct: All, projection: [UnnamedExpr(BinaryOp { left: Value(SingleQuotedString("apple")), op: ILike, right: Value(SingleQuotedString("App%")) })], from: [], lateral_views: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None })'
114+
- input: select 'apple' !~~ 'app%'
115+
formatted_sql: SELECT 'apple' NOT LIKE 'app%'
116+
formatted_ast: 'Query(Query { with: None, body: Select(Select { distinct: All, projection: [UnnamedExpr(BinaryOp { left: Value(SingleQuotedString("apple")), op: NotLike, right: Value(SingleQuotedString("app%")) })], from: [], lateral_views: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None })'
117+
- input: select 'apple' !~~* 'app%'
118+
formatted_sql: SELECT 'apple' NOT ILIKE 'app%'
119+
formatted_ast: 'Query(Query { with: None, body: Select(Select { distinct: All, projection: [UnnamedExpr(BinaryOp { left: Value(SingleQuotedString("apple")), op: NotILike, right: Value(SingleQuotedString("app%")) })], from: [], lateral_views: [], selection: None, group_by: [], having: None }), order_by: [], limit: None, offset: None, fetch: None })'

src/tests/regress/data/sql/arrays.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -420,8 +420,8 @@ select 'foo' like all (array['f%', '%o']); -- t
420420
select 'foo' like all (array['f%', '%b']); -- f
421421
select 'foo' not like any (array['%a', '%b']); -- t
422422
select 'foo' not like all (array['%a', '%o']); -- f
423-
--@ select 'foo' ilike any (array['%A', '%O']); -- t
424-
--@ select 'foo' ilike all (array['F%', '%O']); -- t
423+
select 'foo' ilike any (array['%A', '%O']); -- t
424+
select 'foo' ilike all (array['F%', '%O']); -- t
425425

426426
--
427427
-- General array parser tests

src/tests/regress/data/sql/strings.sql

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -369,18 +369,18 @@ SELECT 'indio' NOT LIKE 'in_o' AS "true";
369369
-- Be sure to form every test as an ILIKE/NOT ILIKE pair.
370370
--
371371

372-
--@ SELECT 'hawkeye' ILIKE 'h%' AS "true";
373-
--@ SELECT 'hawkeye' NOT ILIKE 'h%' AS "false";
374-
--@
375-
--@ SELECT 'hawkeye' ILIKE 'H%' AS "true";
376-
--@ SELECT 'hawkeye' NOT ILIKE 'H%' AS "false";
377-
--@
378-
--@ SELECT 'hawkeye' ILIKE 'H%Eye' AS "true";
379-
--@ SELECT 'hawkeye' NOT ILIKE 'H%Eye' AS "false";
380-
--@
381-
--@ SELECT 'Hawkeye' ILIKE 'h%' AS "true";
382-
--@ SELECT 'Hawkeye' NOT ILIKE 'h%' AS "false";
383-
--@
372+
SELECT 'hawkeye' ILIKE 'h%' AS "true";
373+
SELECT 'hawkeye' NOT ILIKE 'h%' AS "false";
374+
375+
SELECT 'hawkeye' ILIKE 'H%' AS "true";
376+
SELECT 'hawkeye' NOT ILIKE 'H%' AS "false";
377+
378+
SELECT 'hawkeye' ILIKE 'H%Eye' AS "true";
379+
SELECT 'hawkeye' NOT ILIKE 'H%Eye' AS "false";
380+
381+
SELECT 'Hawkeye' ILIKE 'h%' AS "true";
382+
SELECT 'Hawkeye' NOT ILIKE 'h%' AS "false";
383+
384384
--@ SELECT 'ABC'::name ILIKE '_b_' AS "true";
385385
--@ SELECT 'ABC'::name NOT ILIKE '_b_' AS "false";
386386

0 commit comments

Comments
 (0)