Skip to content

Commit dc26c4a

Browse files
authored
Merge pull request #115 from nickolay/pr/followups
Doc improvements and follow-ups to the recent PRs
2 parents 98a06d6 + 5c7ff79 commit dc26c4a

File tree

6 files changed

+86
-153
lines changed

6 files changed

+86
-153
lines changed

src/sqlast/mod.rs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,12 @@ pub type SQLIdent = String;
5656
pub enum ASTNode {
5757
/// Identifier e.g. table name or column name
5858
SQLIdentifier(SQLIdent),
59-
/// Unqualified wildcard (`*`). SQL allows this in limited contexts (such as right
60-
/// after `SELECT` or as part of an aggregate function, e.g. `COUNT(*)`, but we
61-
/// currently accept it in contexts where it doesn't make sense, such as `* + *`
59+
/// Unqualified wildcard (`*`). SQL allows this in limited contexts, such as:
60+
/// - right after `SELECT` (which is represented as a [SQLSelectItem::Wildcard] instead)
61+
/// - or as part of an aggregate function, e.g. `COUNT(*)`,
62+
///
63+
/// ...but we currently also accept it in contexts where it doesn't make
64+
/// sense, such as `* + *`
6265
SQLWildcard,
6366
/// Qualified wildcard, e.g. `alias.*` or `schema.table.*`.
6467
/// (Same caveats apply to SQLQualifiedWildcard as to SQLWildcard.)
@@ -119,10 +122,11 @@ pub enum ASTNode {
119122
SQLValue(Value),
120123
/// Scalar function call e.g. `LEFT(foo, 5)`
121124
SQLFunction(SQLFunction),
122-
/// CASE [<operand>] WHEN <condition> THEN <result> ... [ELSE <result>] END
123-
/// Note we only recognize a complete single expression as <condition>, not
124-
/// `< 0` nor `1, 2, 3` as allowed in a <simple when clause> per
125-
/// https://jakewheat.github.io/sql-overview/sql-2011-foundation-grammar.html#simple-when-clause
125+
/// `CASE [<operand>] WHEN <condition> THEN <result> ... [ELSE <result>] END`
126+
///
127+
/// Note we only recognize a complete single expression as `<condition>`,
128+
/// not `< 0` nor `1, 2, 3` as allowed in a `<simple when clause>` per
129+
/// <https://jakewheat.github.io/sql-overview/sql-2011-foundation-grammar.html#simple-when-clause>
126130
SQLCase {
127131
operand: Option<Box<ASTNode>>,
128132
conditions: Vec<ASTNode>,
@@ -413,13 +417,13 @@ pub enum SQLStatement {
413417
names: Vec<SQLObjectName>,
414418
cascade: bool,
415419
},
416-
/// { BEGIN [ TRANSACTION | WORK ] | START TRANSACTION } ...
420+
/// `{ BEGIN [ TRANSACTION | WORK ] | START TRANSACTION } ...`
417421
SQLStartTransaction { modes: Vec<TransactionMode> },
418-
/// SET TRANSACTION ...
422+
/// `SET TRANSACTION ...`
419423
SQLSetTransaction { modes: Vec<TransactionMode> },
420-
/// COMMIT [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]
424+
/// `COMMIT [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]`
421425
SQLCommit { chain: bool },
422-
/// ROLLBACK [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]
426+
/// `ROLLBACK [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]`
423427
SQLRollback { chain: bool },
424428
}
425429

src/sqlast/query.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ pub struct SQLQuery {
2222
pub body: SQLSetExpr,
2323
/// ORDER BY
2424
pub order_by: Vec<SQLOrderByExpr>,
25-
/// LIMIT { <N> | ALL }
25+
/// `LIMIT { <N> | ALL }`
2626
pub limit: Option<ASTNode>,
27-
/// OFFSET <N> { ROW | ROWS }
27+
/// `OFFSET <N> { ROW | ROWS }`
2828
pub offset: Option<ASTNode>,
29-
/// FETCH { FIRST | NEXT } <N> [ PERCENT ] { ROW | ROWS } | { ONLY | WITH TIES }
29+
/// `FETCH { FIRST | NEXT } <N> [ PERCENT ] { ROW | ROWS } | { ONLY | WITH TIES }`
3030
pub fetch: Option<Fetch>,
3131
}
3232

src/sqlast/table_key.rs

Lines changed: 0 additions & 73 deletions
This file was deleted.

src/sqlast/value.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,32 @@ pub enum Value {
2727
HexStringLiteral(String),
2828
/// Boolean value true or false
2929
Boolean(bool),
30-
/// Date literals
30+
/// `DATE '...'` literals
3131
Date(String),
32-
/// Time literals
32+
/// `TIME '...'` literals
3333
Time(String),
34-
/// Timestamp literals, which include both a date and time
34+
/// `TIMESTAMP '...'` literals
3535
Timestamp(String),
36-
/// INTERVAL literals, e.g. INTERVAL '12:34.56' MINUTE TO SECOND (2)
36+
/// INTERVAL literals, roughly in the following format:
37+
/// `INTERVAL '<value>' <leading_field> [ (<leading_precision>) ]
38+
/// [ TO <last_field> [ (<fractional_seconds_precision>) ] ]`,
39+
/// e.g. `INTERVAL '123:45.67' MINUTE(3) TO SECOND(2)`.
40+
///
41+
/// The parser does not validate the `<value>`, nor does it ensure
42+
/// that the `<leading_field>` units >= the units in `<last_field>`,
43+
/// so the user will have to reject intervals like `HOUR TO YEAR`.
3744
Interval {
3845
value: String,
3946
leading_field: SQLDateTimeField,
4047
leading_precision: Option<u64>,
4148
last_field: Option<SQLDateTimeField>,
49+
/// The seconds precision can be specified in SQL source as
50+
/// `INTERVAL '__' SECOND(_, x)` (in which case the `leading_field`
51+
/// will be `Second` and the `last_field` will be `None`),
52+
/// or as `__ TO SECOND(x)`.
4253
fractional_seconds_precision: Option<u64>,
4354
},
44-
/// NULL value in insert statements,
55+
/// `NULL` value
4556
Null,
4657
}
4758

src/sqlparser.rs

Lines changed: 40 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -896,10 +896,7 @@ impl Parser {
896896
} else if self.parse_keyword("VIEW") {
897897
SQLObjectType::View
898898
} else {
899-
return parser_err!(format!(
900-
"Unexpected token after DROP: {:?}",
901-
self.peek_token()
902-
));
899+
return self.expected("TABLE or VIEW after DROP", self.peek_token());
903900
};
904901
let if_exists = self.parse_keywords(vec!["IF", "EXISTS"]);
905902
let mut names = vec![];
@@ -1021,10 +1018,7 @@ impl Parser {
10211018
self.expect_token(&Token::RParen)?;
10221019
ColumnOption::Check(expr)
10231020
} else {
1024-
return parser_err!(format!(
1025-
"Unexpected token in column definition: {:?}",
1026-
self.peek_token()
1027-
));
1021+
return self.expected("column option", self.peek_token());
10281022
};
10291023

10301024
Ok(ColumnOptionDef { name, option })
@@ -1218,21 +1212,11 @@ impl Parser {
12181212
}
12191213
}
12201214

1221-
/// Parse a literal double
1222-
pub fn parse_literal_double(&mut self) -> Result<f64, ParserError> {
1223-
match self.next_token() {
1224-
Some(Token::Number(s)) => s.parse::<f64>().map_err(|e| {
1225-
ParserError::ParserError(format!("Could not parse '{}' as f64: {}", s, e))
1226-
}),
1227-
other => parser_err!(format!("Expected literal number, found {:?}", other)),
1228-
}
1229-
}
1230-
12311215
/// Parse a literal string
12321216
pub fn parse_literal_string(&mut self) -> Result<String, ParserError> {
12331217
match self.next_token() {
12341218
Some(Token::SingleQuotedString(ref s)) => Ok(s.clone()),
1235-
other => parser_err!(format!("Expected literal string, found {:?}", other)),
1219+
other => self.expected("literal string", other),
12361220
}
12371221
}
12381222

@@ -1623,51 +1607,47 @@ impl Parser {
16231607
let relation = self.parse_table_factor()?;
16241608
let mut joins = vec![];
16251609
loop {
1626-
let join = match &self.peek_token() {
1627-
Some(Token::SQLWord(kw)) if kw.keyword == "CROSS" => {
1628-
self.next_token();
1629-
self.expect_keyword("JOIN")?;
1630-
Join {
1631-
relation: self.parse_table_factor()?,
1632-
join_operator: JoinOperator::Cross,
1633-
}
1610+
let join = if self.parse_keyword("CROSS") {
1611+
self.expect_keyword("JOIN")?;
1612+
Join {
1613+
relation: self.parse_table_factor()?,
1614+
join_operator: JoinOperator::Cross,
16341615
}
1635-
_ => {
1636-
let natural = self.parse_keyword("NATURAL");
1637-
let peek_keyword = if let Some(Token::SQLWord(kw)) = self.peek_token() {
1638-
kw.keyword
1639-
} else {
1640-
String::default()
1641-
};
1616+
} else {
1617+
let natural = self.parse_keyword("NATURAL");
1618+
let peek_keyword = if let Some(Token::SQLWord(kw)) = self.peek_token() {
1619+
kw.keyword
1620+
} else {
1621+
String::default()
1622+
};
16421623

1643-
let join_operator_type = match peek_keyword.as_ref() {
1644-
"INNER" | "JOIN" => {
1645-
let _ = self.parse_keyword("INNER");
1646-
self.expect_keyword("JOIN")?;
1647-
JoinOperator::Inner
1648-
}
1649-
kw @ "LEFT" | kw @ "RIGHT" | kw @ "FULL" => {
1650-
let _ = self.next_token();
1651-
let _ = self.parse_keyword("OUTER");
1652-
self.expect_keyword("JOIN")?;
1653-
match kw {
1654-
"LEFT" => JoinOperator::LeftOuter,
1655-
"RIGHT" => JoinOperator::RightOuter,
1656-
"FULL" => JoinOperator::FullOuter,
1657-
_ => unreachable!(),
1658-
}
1659-
}
1660-
_ if natural => {
1661-
return self.expected("a join type after NATURAL", self.peek_token());
1624+
let join_operator_type = match peek_keyword.as_ref() {
1625+
"INNER" | "JOIN" => {
1626+
let _ = self.parse_keyword("INNER");
1627+
self.expect_keyword("JOIN")?;
1628+
JoinOperator::Inner
1629+
}
1630+
kw @ "LEFT" | kw @ "RIGHT" | kw @ "FULL" => {
1631+
let _ = self.next_token();
1632+
let _ = self.parse_keyword("OUTER");
1633+
self.expect_keyword("JOIN")?;
1634+
match kw {
1635+
"LEFT" => JoinOperator::LeftOuter,
1636+
"RIGHT" => JoinOperator::RightOuter,
1637+
"FULL" => JoinOperator::FullOuter,
1638+
_ => unreachable!(),
16621639
}
1663-
_ => break,
1664-
};
1665-
let relation = self.parse_table_factor()?;
1666-
let join_constraint = self.parse_join_constraint(natural)?;
1667-
Join {
1668-
relation,
1669-
join_operator: join_operator_type(join_constraint),
16701640
}
1641+
_ if natural => {
1642+
return self.expected("a join type after NATURAL", self.peek_token());
1643+
}
1644+
_ => break,
1645+
};
1646+
let relation = self.parse_table_factor()?;
1647+
let join_constraint = self.parse_join_constraint(natural)?;
1648+
Join {
1649+
relation,
1650+
join_operator: join_operator_type(join_constraint),
16711651
}
16721652
};
16731653
joins.push(join);

tests/sqlparser_common.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,16 @@ fn parse_projection_nested_type() {
401401
//TODO: add assertions
402402
}
403403

404+
#[test]
405+
fn parse_null_in_select() {
406+
let sql = "SELECT NULL";
407+
let select = verified_only_select(sql);
408+
assert_eq!(
409+
&ASTNode::SQLValue(Value::Null),
410+
expr_from_projection(only(&select.projection)),
411+
);
412+
}
413+
404414
#[test]
405415
fn parse_escaped_single_quote_string_predicate() {
406416
use self::ASTNode::*;
@@ -949,7 +959,7 @@ fn parse_create_table() {
949959
assert!(res
950960
.unwrap_err()
951961
.to_string()
952-
.contains("Unexpected token in column definition"));
962+
.contains("Expected column option, found: GARBAGE"));
953963
}
954964

955965
#[test]
@@ -1904,6 +1914,7 @@ fn parse_union() {
19041914
verified_stmt("SELECT 1 UNION (SELECT 2 ORDER BY 1 LIMIT 1)");
19051915
verified_stmt("SELECT 1 UNION SELECT 2 INTERSECT SELECT 3"); // Union[1, Intersect[2,3]]
19061916
verified_stmt("SELECT foo FROM tab UNION SELECT bar FROM TAB");
1917+
verified_stmt("(SELECT * FROM new EXCEPT SELECT * FROM old) UNION ALL (SELECT * FROM old EXCEPT SELECT * FROM new) ORDER BY 1");
19071918
}
19081919

19091920
#[test]

0 commit comments

Comments
 (0)