Add Expr::tuple + parsing (#414)

This commit is contained in:
Andrew Lamb 2022-02-08 10:54:27 -05:00 committed by GitHub
parent 8a3544abae
commit ff558eeb61
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 68 additions and 13 deletions

View file

@ -284,6 +284,8 @@ pub enum Expr {
Cube(Vec<Vec<Expr>>),
/// The `ROLLUP` expr.
Rollup(Vec<Vec<Expr>>),
/// ROW / TUPLE a single value, such as `SELECT (1, 2)`
Tuple(Vec<Expr>),
}
impl fmt::Display for Expr {
@ -445,6 +447,9 @@ impl fmt::Display for Expr {
write!(f, ")")
}
Expr::Tuple(exprs) => {
write!(f, "({})", display_comma_separated(exprs))
}
}
}
}

View file

@ -494,7 +494,12 @@ impl<'a> Parser<'a> {
self.prev_token();
Expr::Subquery(Box::new(self.parse_query()?))
} else {
Expr::Nested(Box::new(self.parse_expr()?))
let exprs = self.parse_comma_separated(Parser::parse_expr)?;
match exprs.len() {
0 => unreachable!(), // parse_comma_separated ensures 1 or more
1 => Expr::Nested(Box::new(exprs.into_iter().next().unwrap())),
_ => Expr::Tuple(exprs),
}
};
self.expect_token(&Token::RParen)?;
Ok(expr)
@ -2739,19 +2744,8 @@ impl<'a> Parser<'a> {
None
};
// Not Sure if Top should be cheked here as well. Trino doesn't support TOP.
let is_l_paren = if distinct {
self.consume_token(&Token::LParen)
} else {
false
};
let projection = self.parse_comma_separated(Parser::parse_select_item)?;
if is_l_paren && !self.consume_token(&Token::RParen) {
return self.expected(")", self.peek_token());
}
// Note that for keywords to be properly handled here, they need to be
// added to `RESERVED_FOR_COLUMN_ALIAS` / `RESERVED_FOR_TABLE_ALIAS`,
// otherwise they may be parsed as an alias as part of the `projection`

View file

@ -332,7 +332,6 @@ fn parse_select_distinct_two_fields() {
let sql = "SELECT DISTINCT name, id FROM customer";
let select = verified_only_select(sql);
assert!(select.distinct);
one_statement_parses_to("SELECT DISTINCT (name, id) FROM customer", sql);
assert_eq!(
&SelectItem::UnnamedExpr(Expr::Identifier(Ident::new("name"))),
&select.projection[0]
@ -343,6 +342,19 @@ fn parse_select_distinct_two_fields() {
);
}
#[test]
fn parse_select_distinct_tuple() {
let sql = "SELECT DISTINCT (name, id) FROM customer";
let select = verified_only_select(sql);
assert_eq!(
&vec![SelectItem::UnnamedExpr(Expr::Tuple(vec![
Expr::Identifier(Ident::new("name")),
Expr::Identifier(Ident::new("id")),
]))],
&select.projection
);
}
#[test]
fn parse_select_distinct_missing_paren() {
let result = parse_sql_statements("SELECT DISTINCT (name, id FROM customer");
@ -1033,6 +1045,44 @@ fn parse_between_with_expr() {
)
}
#[test]
fn parse_tuples() {
let sql = "SELECT (1, 2), (1), ('foo', 3, baz)";
let select = verified_only_select(sql);
assert_eq!(
vec![
SelectItem::UnnamedExpr(Expr::Tuple(vec![
Expr::Value(number("1")),
Expr::Value(number("2"))
])),
SelectItem::UnnamedExpr(Expr::Nested(Box::new(Expr::Value(number("1"))))),
SelectItem::UnnamedExpr(Expr::Tuple(vec![
Expr::Value(Value::SingleQuotedString("foo".into())),
Expr::Value(number("3")),
Expr::Identifier(Ident::new("baz"))
]))
],
select.projection
);
}
#[test]
fn parse_tuple_invalid() {
let sql = "select (1";
let res = parse_sql_statements(sql);
assert_eq!(
ParserError::ParserError("Expected ), found: EOF".to_string()),
res.unwrap_err()
);
let sql = "select (), 2";
let res = parse_sql_statements(sql);
assert_eq!(
ParserError::ParserError("Expected an expression:, found: )".to_string()),
res.unwrap_err()
);
}
#[test]
fn parse_select_order_by() {
fn chk(sql: &str) {
@ -1121,6 +1171,12 @@ fn parse_select_group_by() {
],
select.group_by
);
// Tuples can also be in the set
one_statement_parses_to(
"SELECT id, fname, lname FROM customer GROUP BY (lname, fname)",
"SELECT id, fname, lname FROM customer GROUP BY (lname, fname)",
);
}
#[test]