Skip to content

Commit 7857543

Browse files
authored
Merge pull request #116 from nickolay/pr/more-followups
Support HAVING/LIMIT/OFFSET/FETCH without FROM and other follow-ups
2 parents dc26c4a + a37ba08 commit 7857543

File tree

4 files changed

+72
-95
lines changed

4 files changed

+72
-95
lines changed

src/dialect/keywords.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -420,16 +420,16 @@ define_keywords!(
420420
/// can be parsed unambiguously without looking ahead.
421421
pub const RESERVED_FOR_TABLE_ALIAS: &[&str] = &[
422422
// Reserved as both a table and a column alias:
423-
WITH, SELECT, WHERE, GROUP, ORDER, UNION, EXCEPT, INTERSECT,
423+
WITH, SELECT, WHERE, GROUP, HAVING, ORDER, LIMIT, OFFSET, FETCH, UNION, EXCEPT, INTERSECT,
424424
// Reserved only as a table alias in the `FROM`/`JOIN` clauses:
425-
ON, JOIN, INNER, CROSS, FULL, LEFT, RIGHT, NATURAL, USING, LIMIT, OFFSET, FETCH,
425+
ON, JOIN, INNER, CROSS, FULL, LEFT, RIGHT, NATURAL, USING,
426426
];
427427

428428
/// Can't be used as a column alias, so that `SELECT <expr> alias`
429429
/// can be parsed unambiguously without looking ahead.
430430
pub const RESERVED_FOR_COLUMN_ALIAS: &[&str] = &[
431431
// Reserved as both a table and a column alias:
432-
WITH, SELECT, WHERE, GROUP, ORDER, UNION, EXCEPT, INTERSECT,
432+
WITH, SELECT, WHERE, GROUP, HAVING, ORDER, LIMIT, OFFSET, FETCH, UNION, EXCEPT, INTERSECT,
433433
// Reserved only as a column alias in the `SELECT` clause:
434434
FROM,
435435
];

src/sqlast/query.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -163,18 +163,13 @@ impl ToString for SQLSelect {
163163
/// number of columns in the query matches the number of columns in the query.
164164
#[derive(Debug, Clone, PartialEq, Hash)]
165165
pub struct Cte {
166-
pub alias: SQLIdent,
166+
pub alias: TableAlias,
167167
pub query: SQLQuery,
168-
pub renamed_columns: Vec<SQLIdent>,
169168
}
170169

171170
impl ToString for Cte {
172171
fn to_string(&self) -> String {
173-
let mut s = self.alias.clone();
174-
if !self.renamed_columns.is_empty() {
175-
s += &format!(" ({})", comma_separated_string(&self.renamed_columns));
176-
}
177-
s + &format!(" AS ({})", self.query.to_string())
172+
format!("{} AS ({})", self.alias.to_string(), self.query.to_string())
178173
}
179174
}
180175

src/sqlparser.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -574,13 +574,13 @@ impl Parser {
574574
self.expected("IN or BETWEEN after NOT", self.peek_token())
575575
}
576576
}
577-
// Can only happen if `get_precedence` got out of sync with this function
577+
// Can only happen if `get_next_precedence` got out of sync with this function
578578
_ => panic!("No infix parser for token {:?}", tok),
579579
}
580580
} else if Token::DoubleColon == tok {
581581
self.parse_pg_cast(expr)
582582
} else {
583-
// Can only happen if `get_precedence` got out of sync with this function
583+
// Can only happen if `get_next_precedence` got out of sync with this function
584584
panic!("No infix parser for token {:?}", tok)
585585
}
586586
}
@@ -636,7 +636,7 @@ impl Parser {
636636
/// Get the precedence of the next token
637637
pub fn get_next_precedence(&self) -> Result<u8, ParserError> {
638638
if let Some(token) = self.peek_token() {
639-
debug!("get_precedence() {:?}", token);
639+
debug!("get_next_precedence() {:?}", token);
640640

641641
match &token {
642642
Token::SQLWord(k) if k.keyword == "OR" => Ok(5),
@@ -1475,14 +1475,15 @@ impl Parser {
14751475
fn parse_cte_list(&mut self) -> Result<Vec<Cte>, ParserError> {
14761476
let mut cte = vec![];
14771477
loop {
1478-
let alias = self.parse_identifier()?;
1479-
let renamed_columns = self.parse_parenthesized_column_list(Optional)?;
1478+
let alias = TableAlias {
1479+
name: self.parse_identifier()?,
1480+
columns: self.parse_parenthesized_column_list(Optional)?,
1481+
};
14801482
self.expect_keyword("AS")?;
14811483
self.expect_token(&Token::LParen)?;
14821484
cte.push(Cte {
14831485
alias,
14841486
query: self.parse_query()?,
1485-
renamed_columns,
14861487
});
14871488
self.expect_token(&Token::RParen)?;
14881489
if !self.consume_token(&Token::Comma) {
@@ -1565,6 +1566,11 @@ impl Parser {
15651566
}
15661567
let projection = self.parse_select_list()?;
15671568

1569+
// Note that for keywords to be properly handled here, they need to be
1570+
// added to `RESERVED_FOR_COLUMN_ALIAS` / `RESERVED_FOR_TABLE_ALIAS`,
1571+
// otherwise they may be parsed as an alias as part of the `projection`
1572+
// or `from`.
1573+
15681574
let mut from = vec![];
15691575
if self.parse_keyword("FROM") {
15701576
loop {
@@ -1605,6 +1611,10 @@ impl Parser {
16051611

16061612
pub fn parse_table_and_joins(&mut self) -> Result<TableWithJoins, ParserError> {
16071613
let relation = self.parse_table_factor()?;
1614+
1615+
// Note that for keywords to be properly handled here, they need to be
1616+
// added to `RESERVED_FOR_TABLE_ALIAS`, otherwise they may be parsed as
1617+
// a table alias.
16081618
let mut joins = vec![];
16091619
loop {
16101620
let join = if self.parse_keyword("CROSS") {

tests/sqlparser_common.rs

Lines changed: 51 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -212,13 +212,13 @@ fn parse_simple_select() {
212212
}
213213

214214
#[test]
215-
fn parse_select_with_limit_but_no_where() {
216-
let sql = "SELECT id, fname, lname FROM customer LIMIT 5";
217-
let select = verified_only_select(sql);
218-
assert_eq!(false, select.distinct);
219-
assert_eq!(3, select.projection.len());
220-
let select = verified_query(sql);
221-
assert_eq!(Some(ASTNode::SQLValue(Value::Long(5))), select.limit);
215+
fn parse_limit_is_not_an_alias() {
216+
// In dialects supporting LIMIT it shouldn't be parsed as a table alias
217+
let ast = verified_query("SELECT id FROM customer LIMIT 1");
218+
assert_eq!(Some(ASTNode::SQLValue(Value::Long(1))), ast.limit);
219+
220+
let ast = verified_query("SELECT 1 LIMIT 5");
221+
assert_eq!(Some(ASTNode::SQLValue(Value::Long(5))), ast.limit);
222222
}
223223

224224
#[test]
@@ -789,6 +789,29 @@ fn parse_select_group_by() {
789789
);
790790
}
791791

792+
#[test]
793+
fn parse_select_having() {
794+
let sql = "SELECT foo FROM bar GROUP BY foo HAVING COUNT(*) > 1";
795+
let select = verified_only_select(sql);
796+
assert_eq!(
797+
Some(ASTNode::SQLBinaryOp {
798+
left: Box::new(ASTNode::SQLFunction(SQLFunction {
799+
name: SQLObjectName(vec!["COUNT".to_string()]),
800+
args: vec![ASTNode::SQLWildcard],
801+
over: None,
802+
distinct: false
803+
})),
804+
op: SQLBinaryOperator::Gt,
805+
right: Box::new(ASTNode::SQLValue(Value::Long(1)))
806+
}),
807+
select.having
808+
);
809+
810+
let sql = "SELECT 'foo' HAVING 1 = 1";
811+
let select = verified_only_select(sql);
812+
assert!(select.having.is_some());
813+
}
814+
792815
#[test]
793816
fn parse_limit_accepts_all() {
794817
one_statement_parses_to(
@@ -1791,14 +1814,10 @@ fn parse_ctes() {
17911814
fn assert_ctes_in_select(expected: &[&str], sel: &SQLQuery) {
17921815
let mut i = 0;
17931816
for exp in expected {
1794-
let Cte {
1795-
query,
1796-
alias,
1797-
renamed_columns,
1798-
} = &sel.ctes[i];
1817+
let Cte { alias, query } = &sel.ctes[i];
17991818
assert_eq!(*exp, query.to_string());
1800-
assert_eq!(if i == 0 { "a" } else { "b" }, alias);
1801-
assert!(renamed_columns.is_empty());
1819+
assert_eq!(if i == 0 { "a" } else { "b" }, alias.name);
1820+
assert!(alias.columns.is_empty());
18021821
i += 1;
18031822
}
18041823
}
@@ -1841,7 +1860,7 @@ fn parse_cte_renamed_columns() {
18411860
let query = all_dialects().verified_query(sql);
18421861
assert_eq!(
18431862
vec!["col1", "col2"],
1844-
query.ctes.first().unwrap().renamed_columns
1863+
query.ctes.first().unwrap().alias.columns
18451864
);
18461865
}
18471866

@@ -2201,6 +2220,8 @@ fn parse_offset() {
22012220
},
22022221
_ => panic!("Test broke"),
22032222
}
2223+
let ast = verified_query("SELECT 'foo' OFFSET 0 ROWS");
2224+
assert_eq!(ast.offset, Some(ASTNode::SQLValue(Value::Long(0))));
22042225
}
22052226

22062227
#[test]
@@ -2213,15 +2234,15 @@ fn parse_singular_row_offset() {
22132234

22142235
#[test]
22152236
fn parse_fetch() {
2237+
const FETCH_FIRST_TWO_ROWS_ONLY: Fetch = Fetch {
2238+
with_ties: false,
2239+
percent: false,
2240+
quantity: Some(ASTNode::SQLValue(Value::Long(2))),
2241+
};
22162242
let ast = verified_query("SELECT foo FROM bar FETCH FIRST 2 ROWS ONLY");
2217-
assert_eq!(
2218-
ast.fetch,
2219-
Some(Fetch {
2220-
with_ties: false,
2221-
percent: false,
2222-
quantity: Some(ASTNode::SQLValue(Value::Long(2))),
2223-
})
2224-
);
2243+
assert_eq!(ast.fetch, Some(FETCH_FIRST_TWO_ROWS_ONLY));
2244+
let ast = verified_query("SELECT 'foo' FETCH FIRST 2 ROWS ONLY");
2245+
assert_eq!(ast.fetch, Some(FETCH_FIRST_TWO_ROWS_ONLY));
22252246
let ast = verified_query("SELECT foo FROM bar FETCH FIRST ROWS ONLY");
22262247
assert_eq!(
22272248
ast.fetch,
@@ -2232,23 +2253,9 @@ fn parse_fetch() {
22322253
})
22332254
);
22342255
let ast = verified_query("SELECT foo FROM bar WHERE foo = 4 FETCH FIRST 2 ROWS ONLY");
2235-
assert_eq!(
2236-
ast.fetch,
2237-
Some(Fetch {
2238-
with_ties: false,
2239-
percent: false,
2240-
quantity: Some(ASTNode::SQLValue(Value::Long(2))),
2241-
})
2242-
);
2256+
assert_eq!(ast.fetch, Some(FETCH_FIRST_TWO_ROWS_ONLY));
22432257
let ast = verified_query("SELECT foo FROM bar ORDER BY baz FETCH FIRST 2 ROWS ONLY");
2244-
assert_eq!(
2245-
ast.fetch,
2246-
Some(Fetch {
2247-
with_ties: false,
2248-
percent: false,
2249-
quantity: Some(ASTNode::SQLValue(Value::Long(2))),
2250-
})
2251-
);
2258+
assert_eq!(ast.fetch, Some(FETCH_FIRST_TWO_ROWS_ONLY));
22522259
let ast = verified_query(
22532260
"SELECT foo FROM bar WHERE foo = 4 ORDER BY baz FETCH FIRST 2 ROWS WITH TIES",
22542261
);
@@ -2273,63 +2280,28 @@ fn parse_fetch() {
22732280
"SELECT foo FROM bar WHERE foo = 4 ORDER BY baz OFFSET 2 ROWS FETCH FIRST 2 ROWS ONLY",
22742281
);
22752282
assert_eq!(ast.offset, Some(ASTNode::SQLValue(Value::Long(2))));
2276-
assert_eq!(
2277-
ast.fetch,
2278-
Some(Fetch {
2279-
with_ties: false,
2280-
percent: false,
2281-
quantity: Some(ASTNode::SQLValue(Value::Long(2))),
2282-
})
2283-
);
2283+
assert_eq!(ast.fetch, Some(FETCH_FIRST_TWO_ROWS_ONLY));
22842284
let ast = verified_query(
22852285
"SELECT foo FROM (SELECT * FROM bar FETCH FIRST 2 ROWS ONLY) FETCH FIRST 2 ROWS ONLY",
22862286
);
2287-
assert_eq!(
2288-
ast.fetch,
2289-
Some(Fetch {
2290-
with_ties: false,
2291-
percent: false,
2292-
quantity: Some(ASTNode::SQLValue(Value::Long(2))),
2293-
})
2294-
);
2287+
assert_eq!(ast.fetch, Some(FETCH_FIRST_TWO_ROWS_ONLY));
22952288
match ast.body {
22962289
SQLSetExpr::Select(s) => match only(s.from).relation {
22972290
TableFactor::Derived { subquery, .. } => {
2298-
assert_eq!(
2299-
subquery.fetch,
2300-
Some(Fetch {
2301-
with_ties: false,
2302-
percent: false,
2303-
quantity: Some(ASTNode::SQLValue(Value::Long(2))),
2304-
})
2305-
);
2291+
assert_eq!(subquery.fetch, Some(FETCH_FIRST_TWO_ROWS_ONLY));
23062292
}
23072293
_ => panic!("Test broke"),
23082294
},
23092295
_ => panic!("Test broke"),
23102296
}
23112297
let ast = verified_query("SELECT foo FROM (SELECT * FROM bar OFFSET 2 ROWS FETCH FIRST 2 ROWS ONLY) OFFSET 2 ROWS FETCH FIRST 2 ROWS ONLY");
23122298
assert_eq!(ast.offset, Some(ASTNode::SQLValue(Value::Long(2))));
2313-
assert_eq!(
2314-
ast.fetch,
2315-
Some(Fetch {
2316-
with_ties: false,
2317-
percent: false,
2318-
quantity: Some(ASTNode::SQLValue(Value::Long(2))),
2319-
})
2320-
);
2299+
assert_eq!(ast.fetch, Some(FETCH_FIRST_TWO_ROWS_ONLY));
23212300
match ast.body {
23222301
SQLSetExpr::Select(s) => match only(s.from).relation {
23232302
TableFactor::Derived { subquery, .. } => {
23242303
assert_eq!(subquery.offset, Some(ASTNode::SQLValue(Value::Long(2))));
2325-
assert_eq!(
2326-
subquery.fetch,
2327-
Some(Fetch {
2328-
with_ties: false,
2329-
percent: false,
2330-
quantity: Some(ASTNode::SQLValue(Value::Long(2))),
2331-
})
2332-
);
2304+
assert_eq!(subquery.fetch, Some(FETCH_FIRST_TWO_ROWS_ONLY));
23332305
}
23342306
_ => panic!("Test broke"),
23352307
},

0 commit comments

Comments
 (0)