Skip to content

Commit ff558ee

Browse files
authored
Add Expr::tuple + parsing (#414)
1 parent 8a3544a commit ff558ee

File tree

3 files changed

+68
-13
lines changed

3 files changed

+68
-13
lines changed

src/ast/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,8 @@ pub enum Expr {
284284
Cube(Vec<Vec<Expr>>),
285285
/// The `ROLLUP` expr.
286286
Rollup(Vec<Vec<Expr>>),
287+
/// ROW / TUPLE a single value, such as `SELECT (1, 2)`
288+
Tuple(Vec<Expr>),
287289
}
288290

289291
impl fmt::Display for Expr {
@@ -445,6 +447,9 @@ impl fmt::Display for Expr {
445447

446448
write!(f, ")")
447449
}
450+
Expr::Tuple(exprs) => {
451+
write!(f, "({})", display_comma_separated(exprs))
452+
}
448453
}
449454
}
450455
}

src/parser.rs

+6-12
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,12 @@ impl<'a> Parser<'a> {
494494
self.prev_token();
495495
Expr::Subquery(Box::new(self.parse_query()?))
496496
} else {
497-
Expr::Nested(Box::new(self.parse_expr()?))
497+
let exprs = self.parse_comma_separated(Parser::parse_expr)?;
498+
match exprs.len() {
499+
0 => unreachable!(), // parse_comma_separated ensures 1 or more
500+
1 => Expr::Nested(Box::new(exprs.into_iter().next().unwrap())),
501+
_ => Expr::Tuple(exprs),
502+
}
498503
};
499504
self.expect_token(&Token::RParen)?;
500505
Ok(expr)
@@ -2739,19 +2744,8 @@ impl<'a> Parser<'a> {
27392744
None
27402745
};
27412746

2742-
// Not Sure if Top should be cheked here as well. Trino doesn't support TOP.
2743-
let is_l_paren = if distinct {
2744-
self.consume_token(&Token::LParen)
2745-
} else {
2746-
false
2747-
};
2748-
27492747
let projection = self.parse_comma_separated(Parser::parse_select_item)?;
27502748

2751-
if is_l_paren && !self.consume_token(&Token::RParen) {
2752-
return self.expected(")", self.peek_token());
2753-
}
2754-
27552749
// Note that for keywords to be properly handled here, they need to be
27562750
// added to `RESERVED_FOR_COLUMN_ALIAS` / `RESERVED_FOR_TABLE_ALIAS`,
27572751
// otherwise they may be parsed as an alias as part of the `projection`

tests/sqlparser_common.rs

+57-1
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,6 @@ fn parse_select_distinct_two_fields() {
332332
let sql = "SELECT DISTINCT name, id FROM customer";
333333
let select = verified_only_select(sql);
334334
assert!(select.distinct);
335-
one_statement_parses_to("SELECT DISTINCT (name, id) FROM customer", sql);
336335
assert_eq!(
337336
&SelectItem::UnnamedExpr(Expr::Identifier(Ident::new("name"))),
338337
&select.projection[0]
@@ -343,6 +342,19 @@ fn parse_select_distinct_two_fields() {
343342
);
344343
}
345344

345+
#[test]
346+
fn parse_select_distinct_tuple() {
347+
let sql = "SELECT DISTINCT (name, id) FROM customer";
348+
let select = verified_only_select(sql);
349+
assert_eq!(
350+
&vec![SelectItem::UnnamedExpr(Expr::Tuple(vec![
351+
Expr::Identifier(Ident::new("name")),
352+
Expr::Identifier(Ident::new("id")),
353+
]))],
354+
&select.projection
355+
);
356+
}
357+
346358
#[test]
347359
fn parse_select_distinct_missing_paren() {
348360
let result = parse_sql_statements("SELECT DISTINCT (name, id FROM customer");
@@ -1033,6 +1045,44 @@ fn parse_between_with_expr() {
10331045
)
10341046
}
10351047

1048+
#[test]
1049+
fn parse_tuples() {
1050+
let sql = "SELECT (1, 2), (1), ('foo', 3, baz)";
1051+
let select = verified_only_select(sql);
1052+
assert_eq!(
1053+
vec![
1054+
SelectItem::UnnamedExpr(Expr::Tuple(vec![
1055+
Expr::Value(number("1")),
1056+
Expr::Value(number("2"))
1057+
])),
1058+
SelectItem::UnnamedExpr(Expr::Nested(Box::new(Expr::Value(number("1"))))),
1059+
SelectItem::UnnamedExpr(Expr::Tuple(vec![
1060+
Expr::Value(Value::SingleQuotedString("foo".into())),
1061+
Expr::Value(number("3")),
1062+
Expr::Identifier(Ident::new("baz"))
1063+
]))
1064+
],
1065+
select.projection
1066+
);
1067+
}
1068+
1069+
#[test]
1070+
fn parse_tuple_invalid() {
1071+
let sql = "select (1";
1072+
let res = parse_sql_statements(sql);
1073+
assert_eq!(
1074+
ParserError::ParserError("Expected ), found: EOF".to_string()),
1075+
res.unwrap_err()
1076+
);
1077+
1078+
let sql = "select (), 2";
1079+
let res = parse_sql_statements(sql);
1080+
assert_eq!(
1081+
ParserError::ParserError("Expected an expression:, found: )".to_string()),
1082+
res.unwrap_err()
1083+
);
1084+
}
1085+
10361086
#[test]
10371087
fn parse_select_order_by() {
10381088
fn chk(sql: &str) {
@@ -1121,6 +1171,12 @@ fn parse_select_group_by() {
11211171
],
11221172
select.group_by
11231173
);
1174+
1175+
// Tuples can also be in the set
1176+
one_statement_parses_to(
1177+
"SELECT id, fname, lname FROM customer GROUP BY (lname, fname)",
1178+
"SELECT id, fname, lname FROM customer GROUP BY (lname, fname)",
1179+
);
11241180
}
11251181

11261182
#[test]

0 commit comments

Comments
 (0)