Skip to content

Commit daf5e37

Browse files
authored
parser: fix joins in merge stmts (#722)
Ended up having to remove some leniency in the parser to fix the issues with merge statements.
1 parent 8878dff commit daf5e37

File tree

11 files changed

+853
-821
lines changed

11 files changed

+853
-821
lines changed

crates/squawk_parser/src/grammar.rs

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2952,7 +2952,7 @@ const JOIN_TYPE_FIRST: TokenSet =
29522952
// LEFT [ OUTER ] JOIN
29532953
// RIGHT [ OUTER ] JOIN
29542954
// FULL [ OUTER ] JOIN
2955-
fn join_type(p: &mut Parser<'_>) {
2955+
fn join_type(p: &mut Parser<'_>) -> Option<CompletedMarker> {
29562956
assert!(p.at_ts(JOIN_TYPE_FIRST));
29572957
let m = p.start();
29582958
let kind = match p.current() {
@@ -2987,10 +2987,10 @@ fn join_type(p: &mut Parser<'_>) {
29872987
_ => {
29882988
p.error("expected join type");
29892989
m.abandon(p);
2990-
return;
2990+
return None;
29912991
}
29922992
};
2993-
m.complete(p, kind);
2993+
Some(m.complete(p, kind))
29942994
}
29952995

29962996
const JOIN_FIRST: TokenSet = TokenSet::new(&[NATURAL_KW, CROSS_KW]).union(JOIN_TYPE_FIRST);
@@ -3379,10 +3379,10 @@ fn merge_using_clause(p: &mut Parser<'_>) {
33793379
let m = p.start();
33803380
p.expect(USING_KW);
33813381
opt_from_item(p);
3382-
p.expect(ON_KW);
3383-
// join_condition
3384-
if expr(p).is_none() {
3385-
p.error("expected an expression");
3382+
if p.at(ON_KW) {
3383+
on_clause(p);
3384+
} else {
3385+
p.error("expected on clause");
33863386
}
33873387
m.complete(p, USING_ON_CLAUSE);
33883388
}
@@ -3451,14 +3451,14 @@ fn opt_from_item(p: &mut Parser<'_>) -> bool {
34513451
fn join(p: &mut Parser<'_>) {
34523452
assert!(p.at_ts(JOIN_FIRST));
34533453
let m = p.start();
3454-
p.eat(NATURAL_KW);
3455-
join_type(p);
3454+
let is_natural = p.eat(NATURAL_KW);
3455+
let result = join_type(p);
3456+
let join_kind = result.map(|x| x.kind()).unwrap_or(JOIN_INNER);
34563457
if !opt_from_item(p) {
34573458
p.error("expected from_item");
34583459
}
3459-
// need to check that we're not actually at the on_conflict clause in an
3460-
// insert/update statement
3461-
if p.at(ON_KW) && !(p.nth_at(1, CONFLICT_KW) && matches!(p.nth(2), DO_KW | ON_KW | L_PAREN)) {
3460+
let can_have_on_clause = !is_natural && join_kind != JOIN_CROSS;
3461+
if p.at(ON_KW) && can_have_on_clause {
34623462
on_clause(p);
34633463
} else if p.at(USING_KW) {
34643464
join_using_clause(p);
@@ -3467,6 +3467,7 @@ fn join(p: &mut Parser<'_>) {
34673467
}
34683468

34693469
fn on_clause(p: &mut Parser<'_>) {
3470+
assert!(p.at(ON_KW));
34703471
let m = p.start();
34713472
p.bump(ON_KW);
34723473
if expr(p).is_none() {

crates/squawk_parser/tests/data/ok/merge_pg17.sql

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,3 +145,17 @@ WHEN MATCHED AND w.stock != s.stock THEN
145145
UPDATE SET stock = s.stock
146146
WHEN NOT MATCHED BY SOURCE THEN
147147
DELETE;
148+
149+
-- cross_join_data_source
150+
merge into t
151+
using u cross join v
152+
on t.id = u.id
153+
when matched then
154+
do nothing;
155+
156+
-- natural join
157+
merge into t
158+
using u natural join v
159+
on t.id = u.id
160+
when matched then
161+
do nothing;

crates/squawk_parser/tests/snapshots/tests__explain_ok.snap

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,24 +1061,25 @@ SOURCE_FILE
10611061
NAME_REF
10621062
IDENT "u"
10631063
WHITESPACE "\n "
1064-
ON_KW "on"
1065-
WHITESPACE " "
1066-
BIN_EXPR
1067-
FIELD_EXPR
1068-
NAME_REF
1069-
IDENT "t"
1070-
DOT "."
1071-
NAME_REF
1072-
IDENT "id"
1073-
WHITESPACE " "
1074-
EQ "="
1064+
ON_CLAUSE
1065+
ON_KW "on"
10751066
WHITESPACE " "
1076-
FIELD_EXPR
1077-
NAME_REF
1078-
IDENT "u"
1079-
DOT "."
1080-
NAME_REF
1081-
IDENT "id"
1067+
BIN_EXPR
1068+
FIELD_EXPR
1069+
NAME_REF
1070+
IDENT "t"
1071+
DOT "."
1072+
NAME_REF
1073+
IDENT "id"
1074+
WHITESPACE " "
1075+
EQ "="
1076+
WHITESPACE " "
1077+
FIELD_EXPR
1078+
NAME_REF
1079+
IDENT "u"
1080+
DOT "."
1081+
NAME_REF
1082+
IDENT "id"
10821083
WHITESPACE "\n "
10831084
MERGE_WHEN_MATCHED
10841085
WHEN_KW "when"

0 commit comments

Comments
 (0)