diff --git a/src/dialect/keywords.rs b/src/dialect/keywords.rs index c3489ac67..4a50c7fce 100644 --- a/src/dialect/keywords.rs +++ b/src/dialect/keywords.rs @@ -422,7 +422,12 @@ pub const RESERVED_FOR_TABLE_ALIAS: &[&str] = &[ // Reserved as both a table and a column alias: WITH, SELECT, WHERE, GROUP, HAVING, ORDER, LIMIT, OFFSET, FETCH, UNION, EXCEPT, INTERSECT, // Reserved only as a table alias in the `FROM`/`JOIN` clauses: - ON, JOIN, INNER, CROSS, FULL, LEFT, RIGHT, NATURAL, USING, + ON, JOIN, INNER, CROSS, FULL, LEFT, RIGHT, NATURAL, USING, LIMIT, OFFSET, FETCH, + // Reserved not because of ambiguity, but so that parsing `SELECT * FROM a + // OUTER JOIN b` causes a syntax error, rather than silently parsing to an + // inner join where table `a` is aliased as `OUTER`, which is certainly not + // what the user intended and also not valid according to the SQL standard. + OUTER, ]; /// Can't be used as a column alias, so that `SELECT alias` diff --git a/src/sqlparser.rs b/src/sqlparser.rs index 6924a9329..7802e5b85 100644 --- a/src/sqlparser.rs +++ b/src/sqlparser.rs @@ -1648,6 +1648,7 @@ impl Parser { _ => unreachable!(), } } + "OUTER" => return self.expected("LEFT, RIGHT, or FULL", self.peek_token()), _ if natural => { return self.expected("a join type after NATURAL", self.peek_token()); } diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 7b4341fb8..139c91e07 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -1801,6 +1801,12 @@ fn parse_join_syntax_variants() { "SELECT c1 FROM t1 FULL OUTER JOIN t2 USING(c1)", "SELECT c1 FROM t1 FULL JOIN t2 USING(c1)", ); + + let res = parse_sql_statements("SELECT * FROM a OUTER JOIN b ON 1"); + assert_eq!( + ParserError::ParserError("Expected LEFT, RIGHT, or FULL, found: OUTER".to_string()), + res.unwrap_err() + ); } #[test]