Support IN UNNEST(expression) (#426)

* feat: support `IN UNNEST(expression)`

* Add test for NOT IN UNNEST
This commit is contained in:
Yoshiyuki Komazaki 2022-03-01 22:53:01 +09:00 committed by GitHub
parent 2ebe18a94e
commit 0d1c5d1205
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 50 additions and 0 deletions

View file

@ -189,6 +189,12 @@ pub enum Expr {
subquery: Box<Query>,
negated: bool,
},
/// `[ NOT ] IN UNNEST(array_expression)`
InUnnest {
expr: Box<Expr>,
array_expr: Box<Expr>,
negated: bool,
},
/// `<expr> [ NOT ] BETWEEN <low> AND <high>`
Between {
expr: Box<Expr>,
@ -335,6 +341,17 @@ impl fmt::Display for Expr {
if *negated { "NOT " } else { "" },
subquery
),
Expr::InUnnest {
expr,
array_expr,
negated,
} => write!(
f,
"{} {}IN UNNEST({})",
expr,
if *negated { "NOT " } else { "" },
array_expr
),
Expr::Between {
expr,
negated,

View file

@ -1143,6 +1143,18 @@ impl<'a> Parser<'a> {
/// Parses the parens following the `[ NOT ] IN` operator
pub fn parse_in(&mut self, expr: Expr, negated: bool) -> Result<Expr, ParserError> {
// BigQuery allows `IN UNNEST(array_expression)`
// https://cloud.google.com/bigquery/docs/reference/standard-sql/operators#in_operators
if self.parse_keyword(Keyword::UNNEST) {
self.expect_token(&Token::LParen)?;
let array_expr = self.parse_expr()?;
self.expect_token(&Token::RParen)?;
return Ok(Expr::InUnnest {
expr: Box::new(expr),
array_expr: Box::new(array_expr),
negated,
});
}
self.expect_token(&Token::LParen)?;
let in_op = if self.parse_keyword(Keyword::SELECT) || self.parse_keyword(Keyword::WITH) {
self.prev_token();

View file

@ -903,6 +903,27 @@ fn parse_in_subquery() {
);
}
#[test]
fn parse_in_unnest() {
fn chk(negated: bool) {
let sql = &format!(
"SELECT * FROM customers WHERE segment {}IN UNNEST(expr)",
if negated { "NOT " } else { "" }
);
let select = verified_only_select(sql);
assert_eq!(
Expr::InUnnest {
expr: Box::new(Expr::Identifier(Ident::new("segment"))),
array_expr: Box::new(verified_expr("expr")),
negated,
},
select.selection.unwrap()
);
}
chk(false);
chk(true);
}
#[test]
fn parse_string_agg() {
let sql = "SELECT a || b";