Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10155,10 +10155,30 @@ impl<'a> Parser<'a> {
};
let relation = self.parse_table_factor()?;
let join_constraint = self.parse_join_constraint(natural)?;
let join_operator = join_operator_type(join_constraint);

let requires_constraint = match join_operator {
JoinOperator::Inner(JoinConstraint::None)
| JoinOperator::LeftOuter(JoinConstraint::None)
| JoinOperator::RightOuter(JoinConstraint::None)
| JoinOperator::FullOuter(JoinConstraint::None)
| JoinOperator::LeftSemi(JoinConstraint::None)
| JoinOperator::RightSemi(JoinConstraint::None)
| JoinOperator::LeftAnti(JoinConstraint::None)
| JoinOperator::RightAnti(JoinConstraint::None)
| JoinOperator::Semi(JoinConstraint::None)
| JoinOperator::Anti(JoinConstraint::None) => !natural,
_ => false,
};

if requires_constraint {
self.expected("ON, or USING after JOIN", self.peek_token())?
}

Join {
relation,
global,
join_operator: join_operator_type(join_constraint),
join_operator,
}
};
joins.push(join);
Expand Down Expand Up @@ -10967,7 +10987,6 @@ impl<'a> Parser<'a> {
Ok(JoinConstraint::Using(columns))
} else {
Ok(JoinConstraint::None)
//self.expected("ON, or USING after JOIN", self.peek_token())
}
}

Expand Down
48 changes: 46 additions & 2 deletions tests/sqlparser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7613,7 +7613,7 @@ fn lateral_derived() {

#[test]
fn lateral_function() {
let sql = "SELECT * FROM customer LEFT JOIN LATERAL generate_series(1, customer.id)";
let sql = "SELECT * FROM customer CROSS JOIN LATERAL generate_series(1, customer.id)";
let actual_select_only = verified_only_select(sql);
let expected = Select {
distinct: None,
Expand Down Expand Up @@ -7654,7 +7654,7 @@ fn lateral_function() {
alias: None,
},
global: false,
join_operator: JoinOperator::LeftOuter(JoinConstraint::None),
join_operator: JoinOperator::CrossJoin,
}],
}],
lateral_views: vec![],
Expand Down Expand Up @@ -12368,6 +12368,50 @@ fn parse_create_table_select() {
}
}

#[test]
fn parse_no_condition_join_strategy() {
let dialects = all_dialects_where(|d| d.supports_create_table_select());

let join_types = vec![
"JOIN",
"INNER JOIN",
"LEFT JOIN",
"LEFT OUTER JOIN",
"RIGHT JOIN",
"RIGHT OUTER JOIN",
"FULL JOIN",
"FULL OUTER JOIN",
"CROSS JOIN",
"NATURAL JOIN",
"LEFT SEMI JOIN",
"RIGHT SEMI JOIN",
"LEFT ANTI JOIN",
"RIGHT ANTI JOIN",
"SEMI JOIN",
"ANTI JOIN",
];

for join in join_types {
let sql = format!(
"SELECT * FROM (SELECT 1 AS id, 'Foo' AS name) AS l {} (SELECT 1 AS id, 'Bar' AS name) AS r",
join
);
let result = dialects.parse_sql_statements(&sql);
if join.starts_with("CROSS") || join.starts_with("NATURAL") {
// CROSS JOIN and NATURAL JOIN don't require ON or USING clauses
assert!(result.is_ok());
} else {
// Other joins require ON or USING clauses
assert_eq!(
result.unwrap_err(),
ParserError::ParserError(
"Expected: ON, or USING after JOIN, found: EOF".to_string()
)
);
}
}
}

#[test]
fn test_reserved_keywords_for_identifiers() {
let dialects = all_dialects_where(|d| d.is_reserved_for_identifier(Keyword::INTERVAL));
Expand Down
2 changes: 1 addition & 1 deletion tests/sqlparser_hive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ fn test_distribute_by() {

#[test]
fn no_join_condition() {
let join = "SELECT a, b FROM db.table_name JOIN a";
let join = "SELECT a, b FROM db.table_name CROSS JOIN a";
hive().verified_stmt(join);
}

Expand Down