mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-10-12 15:02:07 +00:00
Add Postgres operators for the LIKE expression variants (#1096)
This commit is contained in:
parent
d72f0a966b
commit
c7d2903c6d
4 changed files with 111 additions and 0 deletions
|
@ -131,6 +131,14 @@ pub enum BinaryOperator {
|
||||||
PGRegexNotMatch,
|
PGRegexNotMatch,
|
||||||
/// String does not match regular expression (case insensitively), e.g. `a !~* b` (PostgreSQL-specific)
|
/// String does not match regular expression (case insensitively), e.g. `a !~* b` (PostgreSQL-specific)
|
||||||
PGRegexNotIMatch,
|
PGRegexNotIMatch,
|
||||||
|
/// String matches pattern (case sensitively), e.g. `a ~~ b` (PostgreSQL-specific)
|
||||||
|
PGLikeMatch,
|
||||||
|
/// String matches pattern (case insensitively), e.g. `a ~~* b` (PostgreSQL-specific)
|
||||||
|
PGILikeMatch,
|
||||||
|
/// String does not match pattern (case sensitively), e.g. `a !~~ b` (PostgreSQL-specific)
|
||||||
|
PGNotLikeMatch,
|
||||||
|
/// String does not match pattern (case insensitively), e.g. `a !~~* b` (PostgreSQL-specific)
|
||||||
|
PGNotILikeMatch,
|
||||||
/// String "starts with", eg: `a ^@ b` (PostgreSQL-specific)
|
/// String "starts with", eg: `a ^@ b` (PostgreSQL-specific)
|
||||||
PGStartsWith,
|
PGStartsWith,
|
||||||
/// PostgreSQL-specific custom operator.
|
/// PostgreSQL-specific custom operator.
|
||||||
|
@ -174,6 +182,10 @@ impl fmt::Display for BinaryOperator {
|
||||||
BinaryOperator::PGRegexIMatch => f.write_str("~*"),
|
BinaryOperator::PGRegexIMatch => f.write_str("~*"),
|
||||||
BinaryOperator::PGRegexNotMatch => f.write_str("!~"),
|
BinaryOperator::PGRegexNotMatch => f.write_str("!~"),
|
||||||
BinaryOperator::PGRegexNotIMatch => f.write_str("!~*"),
|
BinaryOperator::PGRegexNotIMatch => f.write_str("!~*"),
|
||||||
|
BinaryOperator::PGLikeMatch => f.write_str("~~"),
|
||||||
|
BinaryOperator::PGILikeMatch => f.write_str("~~*"),
|
||||||
|
BinaryOperator::PGNotLikeMatch => f.write_str("!~~"),
|
||||||
|
BinaryOperator::PGNotILikeMatch => f.write_str("!~~*"),
|
||||||
BinaryOperator::PGStartsWith => f.write_str("^@"),
|
BinaryOperator::PGStartsWith => f.write_str("^@"),
|
||||||
BinaryOperator::PGCustomBinaryOperator(idents) => {
|
BinaryOperator::PGCustomBinaryOperator(idents) => {
|
||||||
write!(f, "OPERATOR({})", display_separated(idents, "."))
|
write!(f, "OPERATOR({})", display_separated(idents, "."))
|
||||||
|
|
|
@ -2205,6 +2205,10 @@ impl<'a> Parser<'a> {
|
||||||
Token::TildeAsterisk => Some(BinaryOperator::PGRegexIMatch),
|
Token::TildeAsterisk => Some(BinaryOperator::PGRegexIMatch),
|
||||||
Token::ExclamationMarkTilde => Some(BinaryOperator::PGRegexNotMatch),
|
Token::ExclamationMarkTilde => Some(BinaryOperator::PGRegexNotMatch),
|
||||||
Token::ExclamationMarkTildeAsterisk => Some(BinaryOperator::PGRegexNotIMatch),
|
Token::ExclamationMarkTildeAsterisk => Some(BinaryOperator::PGRegexNotIMatch),
|
||||||
|
Token::DoubleTilde => Some(BinaryOperator::PGLikeMatch),
|
||||||
|
Token::DoubleTildeAsterisk => Some(BinaryOperator::PGILikeMatch),
|
||||||
|
Token::ExclamationMarkDoubleTilde => Some(BinaryOperator::PGNotLikeMatch),
|
||||||
|
Token::ExclamationMarkDoubleTildeAsterisk => Some(BinaryOperator::PGNotILikeMatch),
|
||||||
Token::Word(w) => match w.keyword {
|
Token::Word(w) => match w.keyword {
|
||||||
Keyword::AND => Some(BinaryOperator::And),
|
Keyword::AND => Some(BinaryOperator::And),
|
||||||
Keyword::OR => Some(BinaryOperator::Or),
|
Keyword::OR => Some(BinaryOperator::Or),
|
||||||
|
@ -2620,6 +2624,10 @@ impl<'a> Parser<'a> {
|
||||||
| Token::TildeAsterisk
|
| Token::TildeAsterisk
|
||||||
| Token::ExclamationMarkTilde
|
| Token::ExclamationMarkTilde
|
||||||
| Token::ExclamationMarkTildeAsterisk
|
| Token::ExclamationMarkTildeAsterisk
|
||||||
|
| Token::DoubleTilde
|
||||||
|
| Token::DoubleTildeAsterisk
|
||||||
|
| Token::ExclamationMarkDoubleTilde
|
||||||
|
| Token::ExclamationMarkDoubleTildeAsterisk
|
||||||
| Token::Spaceship => Ok(20),
|
| Token::Spaceship => Ok(20),
|
||||||
Token::Pipe => Ok(21),
|
Token::Pipe => Ok(21),
|
||||||
Token::Caret | Token::Sharp | Token::ShiftRight | Token::ShiftLeft => Ok(22),
|
Token::Caret | Token::Sharp | Token::ShiftRight | Token::ShiftLeft => Ok(22),
|
||||||
|
|
|
@ -149,6 +149,14 @@ pub enum Token {
|
||||||
ExclamationMarkTilde,
|
ExclamationMarkTilde,
|
||||||
/// `!~*` , a case insensitive not match regular expression operator in PostgreSQL
|
/// `!~*` , a case insensitive not match regular expression operator in PostgreSQL
|
||||||
ExclamationMarkTildeAsterisk,
|
ExclamationMarkTildeAsterisk,
|
||||||
|
/// `~~`, a case sensitive match pattern operator in PostgreSQL
|
||||||
|
DoubleTilde,
|
||||||
|
/// `~~*`, a case insensitive match pattern operator in PostgreSQL
|
||||||
|
DoubleTildeAsterisk,
|
||||||
|
/// `!~~`, a case sensitive not match pattern operator in PostgreSQL
|
||||||
|
ExclamationMarkDoubleTilde,
|
||||||
|
/// `!~~*`, a case insensitive not match pattern operator in PostgreSQL
|
||||||
|
ExclamationMarkDoubleTildeAsterisk,
|
||||||
/// `<<`, a bitwise shift left operator in PostgreSQL
|
/// `<<`, a bitwise shift left operator in PostgreSQL
|
||||||
ShiftLeft,
|
ShiftLeft,
|
||||||
/// `>>`, a bitwise shift right operator in PostgreSQL
|
/// `>>`, a bitwise shift right operator in PostgreSQL
|
||||||
|
@ -249,6 +257,10 @@ impl fmt::Display for Token {
|
||||||
Token::TildeAsterisk => f.write_str("~*"),
|
Token::TildeAsterisk => f.write_str("~*"),
|
||||||
Token::ExclamationMarkTilde => f.write_str("!~"),
|
Token::ExclamationMarkTilde => f.write_str("!~"),
|
||||||
Token::ExclamationMarkTildeAsterisk => f.write_str("!~*"),
|
Token::ExclamationMarkTildeAsterisk => f.write_str("!~*"),
|
||||||
|
Token::DoubleTilde => f.write_str("~~"),
|
||||||
|
Token::DoubleTildeAsterisk => f.write_str("~~*"),
|
||||||
|
Token::ExclamationMarkDoubleTilde => f.write_str("!~~"),
|
||||||
|
Token::ExclamationMarkDoubleTildeAsterisk => f.write_str("!~~*"),
|
||||||
Token::AtSign => f.write_str("@"),
|
Token::AtSign => f.write_str("@"),
|
||||||
Token::CaretAt => f.write_str("^@"),
|
Token::CaretAt => f.write_str("^@"),
|
||||||
Token::ShiftLeft => f.write_str("<<"),
|
Token::ShiftLeft => f.write_str("<<"),
|
||||||
|
@ -903,6 +915,16 @@ impl<'a> Tokenizer<'a> {
|
||||||
match chars.peek() {
|
match chars.peek() {
|
||||||
Some('*') => self
|
Some('*') => self
|
||||||
.consume_and_return(chars, Token::ExclamationMarkTildeAsterisk),
|
.consume_and_return(chars, Token::ExclamationMarkTildeAsterisk),
|
||||||
|
Some('~') => {
|
||||||
|
chars.next();
|
||||||
|
match chars.peek() {
|
||||||
|
Some('*') => self.consume_and_return(
|
||||||
|
chars,
|
||||||
|
Token::ExclamationMarkDoubleTildeAsterisk,
|
||||||
|
),
|
||||||
|
_ => Ok(Some(Token::ExclamationMarkDoubleTilde)),
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => Ok(Some(Token::ExclamationMarkTilde)),
|
_ => Ok(Some(Token::ExclamationMarkTilde)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -974,6 +996,15 @@ impl<'a> Tokenizer<'a> {
|
||||||
chars.next(); // consume
|
chars.next(); // consume
|
||||||
match chars.peek() {
|
match chars.peek() {
|
||||||
Some('*') => self.consume_and_return(chars, Token::TildeAsterisk),
|
Some('*') => self.consume_and_return(chars, Token::TildeAsterisk),
|
||||||
|
Some('~') => {
|
||||||
|
chars.next();
|
||||||
|
match chars.peek() {
|
||||||
|
Some('*') => {
|
||||||
|
self.consume_and_return(chars, Token::DoubleTildeAsterisk)
|
||||||
|
}
|
||||||
|
_ => Ok(Some(Token::DoubleTilde)),
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => Ok(Some(Token::Tilde)),
|
_ => Ok(Some(Token::Tilde)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1994,6 +2025,44 @@ mod tests {
|
||||||
compare(expected, tokens);
|
compare(expected, tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tokenize_pg_like_match() {
|
||||||
|
let sql = "SELECT col ~~ '_a%', col ~~* '_a%', col !~~ '_a%', col !~~* '_a%'";
|
||||||
|
let dialect = GenericDialect {};
|
||||||
|
let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap();
|
||||||
|
let expected = vec![
|
||||||
|
Token::make_keyword("SELECT"),
|
||||||
|
Token::Whitespace(Whitespace::Space),
|
||||||
|
Token::make_word("col", None),
|
||||||
|
Token::Whitespace(Whitespace::Space),
|
||||||
|
Token::DoubleTilde,
|
||||||
|
Token::Whitespace(Whitespace::Space),
|
||||||
|
Token::SingleQuotedString("_a%".into()),
|
||||||
|
Token::Comma,
|
||||||
|
Token::Whitespace(Whitespace::Space),
|
||||||
|
Token::make_word("col", None),
|
||||||
|
Token::Whitespace(Whitespace::Space),
|
||||||
|
Token::DoubleTildeAsterisk,
|
||||||
|
Token::Whitespace(Whitespace::Space),
|
||||||
|
Token::SingleQuotedString("_a%".into()),
|
||||||
|
Token::Comma,
|
||||||
|
Token::Whitespace(Whitespace::Space),
|
||||||
|
Token::make_word("col", None),
|
||||||
|
Token::Whitespace(Whitespace::Space),
|
||||||
|
Token::ExclamationMarkDoubleTilde,
|
||||||
|
Token::Whitespace(Whitespace::Space),
|
||||||
|
Token::SingleQuotedString("_a%".into()),
|
||||||
|
Token::Comma,
|
||||||
|
Token::Whitespace(Whitespace::Space),
|
||||||
|
Token::make_word("col", None),
|
||||||
|
Token::Whitespace(Whitespace::Space),
|
||||||
|
Token::ExclamationMarkDoubleTildeAsterisk,
|
||||||
|
Token::Whitespace(Whitespace::Space),
|
||||||
|
Token::SingleQuotedString("_a%".into()),
|
||||||
|
];
|
||||||
|
compare(expected, tokens);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tokenize_quoted_identifier() {
|
fn tokenize_quoted_identifier() {
|
||||||
let sql = r#" "a "" b" "a """ "c """"" "#;
|
let sql = r#" "a "" b" "a """ "c """"" "#;
|
||||||
|
|
|
@ -1804,6 +1804,28 @@ fn parse_pg_regex_match_ops() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_pg_like_match_ops() {
|
||||||
|
let pg_like_match_ops = &[
|
||||||
|
("~~", BinaryOperator::PGLikeMatch),
|
||||||
|
("~~*", BinaryOperator::PGILikeMatch),
|
||||||
|
("!~~", BinaryOperator::PGNotLikeMatch),
|
||||||
|
("!~~*", BinaryOperator::PGNotILikeMatch),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (str_op, op) in pg_like_match_ops {
|
||||||
|
let select = pg().verified_only_select(&format!("SELECT 'abc' {} 'a_c%'", &str_op));
|
||||||
|
assert_eq!(
|
||||||
|
SelectItem::UnnamedExpr(Expr::BinaryOp {
|
||||||
|
left: Box::new(Expr::Value(Value::SingleQuotedString("abc".into()))),
|
||||||
|
op: op.clone(),
|
||||||
|
right: Box::new(Expr::Value(Value::SingleQuotedString("a_c%".into()))),
|
||||||
|
}),
|
||||||
|
select.projection[0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_array_index_expr() {
|
fn parse_array_index_expr() {
|
||||||
let num: Vec<Expr> = (0..=10)
|
let num: Vec<Expr> = (0..=10)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue