diff --git a/PLAN.md b/PLAN.md index 1aad7b8f..ab0c3961 100644 --- a/PLAN.md +++ b/PLAN.md @@ -296,6 +296,36 @@ LINE 1: set session query_max_run_time = 10m; ^ ``` +### Rule: cross join + +```sql +select * from t join u on true; +select * from t1, t2; +``` + +suggests / autofixes to: + +```sql +select * from t cross join u; +select * from t1 cross join t2; +``` + +with config to change desired destination format + +### Rule: natural join + +warn about natural joins and autofix to the equivalent + +```sql +select * from t natural join u; +``` + +suggests / autofixes to: + +```sql +select * from t join u using (id, name, ip, description, meta); +``` + ### Rule: using unsupported lambdas This actually parsers in Postgres, but could work off a heuristic diff --git a/crates/squawk_parser/src/generated/syntax_kind.rs b/crates/squawk_parser/src/generated/syntax_kind.rs index 91d4e22c..7076217d 100644 --- a/crates/squawk_parser/src/generated/syntax_kind.rs +++ b/crates/squawk_parser/src/generated/syntax_kind.rs @@ -799,6 +799,13 @@ pub enum SyntaxKind { IS_NOT_JSON_VALUE, IS_NOT_NORMALIZED, JOIN, + JOIN_CROSS, + JOIN_EXPR, + JOIN_FULL, + JOIN_INNER, + JOIN_LEFT, + JOIN_RIGHT, + JOIN_TYPE, JSON_BEHAVIOR_CLAUSE, JSON_FORMAT_CLAUSE, JSON_KEYS_UNIQUE_CLAUSE, @@ -848,6 +855,7 @@ pub enum SyntaxKind { NULL_CONSTRAINT, OFFSET_CLAUSE, OF_TYPE, + ON_CLAUSE, ON_DELETE_ACTION, ON_UPDATE_ACTION, OP, diff --git a/crates/squawk_parser/src/grammar.rs b/crates/squawk_parser/src/grammar.rs index 60d4dd7f..4108790d 100644 --- a/crates/squawk_parser/src/grammar.rs +++ b/crates/squawk_parser/src/grammar.rs @@ -2625,32 +2625,61 @@ fn opt_order_by_clause(p: &mut Parser<'_>) -> bool { true } -const JOIN_TYPE_FIRST: TokenSet = TokenSet::new(&[INNER_KW, JOIN_KW, LEFT_KW, RIGHT_KW, FULL_KW]); +const JOIN_TYPE_FIRST: TokenSet = + TokenSet::new(&[INNER_KW, JOIN_KW, LEFT_KW, RIGHT_KW, FULL_KW, CROSS_KW]); // where join_type is: // [ INNER ] JOIN // LEFT [ OUTER ] JOIN // RIGHT [ OUTER ] JOIN // FULL [ OUTER ] JOIN -fn join_type(p: &mut Parser<'_>) -> bool { +fn join_type(p: &mut Parser<'_>) -> Option { assert!(p.at_ts(JOIN_TYPE_FIRST)); - if p.eat(INNER_KW) { - p.expect(JOIN_KW) - } else if p.eat(LEFT_KW) || p.eat(RIGHT_KW) || p.eat(FULL_KW) { - p.eat(OUTER_KW); - p.expect(JOIN_KW) - } else { - p.expect(JOIN_KW) - } + let m = p.start(); + let kind = match p.current() { + CROSS_KW => { + p.bump(CROSS_KW); + p.expect(JOIN_KW); + JOIN_CROSS + } + INNER_KW | JOIN_KW => { + p.eat(INNER_KW); + p.expect(JOIN_KW); + JOIN_INNER + } + LEFT_KW => { + p.bump(LEFT_KW); + p.eat(OUTER_KW); + p.expect(JOIN_KW); + JOIN_LEFT + } + RIGHT_KW => { + p.bump(RIGHT_KW); + p.eat(OUTER_KW); + p.expect(JOIN_KW); + JOIN_RIGHT + } + FULL_KW => { + p.bump(FULL_KW); + p.eat(OUTER_KW); + p.expect(JOIN_KW); + JOIN_FULL + } + _ => { + p.error("expected join type"); + return None; + } + }; + Some(m.complete(p, kind)) } const JOIN_FIRST: TokenSet = TokenSet::new(&[NATURAL_KW, CROSS_KW]).union(JOIN_TYPE_FIRST); -fn opt_from_clause(p: &mut Parser<'_>) -> bool { +fn opt_from_clause(p: &mut Parser<'_>) -> Option { let m = p.start(); if !p.eat(FROM_KW) { m.abandon(p); - return false; + return None; } if !opt_from_item(p) { p.error(format!("expected from item, got {:?}", p.current())); @@ -2661,8 +2690,7 @@ fn opt_from_clause(p: &mut Parser<'_>) -> bool { break; } } - m.complete(p, FROM_CLAUSE); - true + Some(m.complete(p, FROM_CLAUSE)) } // https://github.com/postgres/postgres/blob/b3219c69fc1e161df8d380c464b3f2cce3b6cab9/src/backend/parser/gram.y#L18042 @@ -2994,7 +3022,7 @@ fn paren_data_source(p: &mut Parser<'_>) -> CompletedMarker { fn merge_using_clause(p: &mut Parser<'_>) { let m = p.start(); p.expect(USING_KW); - data_source(p); + opt_from_item(p); p.expect(ON_KW); // join_condition if expr(p).is_none() { @@ -3037,17 +3065,18 @@ fn merge_using_clause(p: &mut Parser<'_>) { // RIGHT [ OUTER ] JOIN // FULL [ OUTER ] JOIN // -#[must_use] fn opt_from_item(p: &mut Parser<'_>) -> bool { if !p.at_ts(FROM_ITEM_FIRST) { return false; } let m = p.start(); data_source(p); + let mut cm = m.complete(p, FROM_ITEM); while p.at_ts(JOIN_FIRST) { + let m = cm.precede(p); join(p); + cm = m.complete(p, JOIN_EXPR); } - m.complete(p, FROM_ITEM); true } @@ -3066,52 +3095,40 @@ fn opt_from_item(p: &mut Parser<'_>) -> bool { fn join(p: &mut Parser<'_>) { assert!(p.at_ts(JOIN_FIRST)); let m = p.start(); - if p.eat(NATURAL_KW) { - if !join_type(p) { - p.error("expected join type"); - } - if !opt_from_item(p) { - p.error("expected from_item"); - } - } else if p.eat(CROSS_KW) { - p.expect(JOIN_KW); - if !opt_from_item(p) { - p.error("expected from_item"); - } - } else { - if !join_type(p) { - p.error("expected join type"); - } - if !opt_from_item(p) { - p.error("expected from_item"); + p.eat(NATURAL_KW); + if join_type(p).is_none() { + p.error("expected join type"); + } + if !opt_from_item(p) { + p.error("expected from_item"); + } + if p.at(ON_KW) { + let m = p.start(); + p.bump(ON_KW); + if expr(p).is_none() { + p.error("expected an expression"); } - if p.eat(ON_KW) { - if expr(p).is_none() { - p.error("expected an expression"); - } + m.complete(p, ON_CLAUSE); + } else if p.at(USING_KW) { + let m = p.start(); + // USING ( join_column [, ...] ) + p.expect(USING_KW); + if p.at(L_PAREN) { + column_list(p); } else { - { - let m = p.start(); - // USING ( join_column [, ...] ) - p.expect(USING_KW); - if p.at(L_PAREN) { - column_list(p); - } else { - p.error("expected L_PAREN"); - } - m.complete(p, USING_CLAUSE); - } - { - let m = p.start(); - // [ AS join_using_alias ] - if p.eat(AS_KW) { - name(p); - m.complete(p, ALIAS); - } else { - m.abandon(p); - } + p.error("expected L_PAREN"); + } + { + let m = p.start(); + // [ AS join_using_alias ] + if p.eat(AS_KW) { + name(p); + m.complete(p, ALIAS); + } else { + m.abandon(p); } } + m.complete(p, USING_CLAUSE); } m.complete(p, JOIN); } diff --git a/crates/squawk_parser/tests/data/regression_suite/tuplesort.sql b/crates/squawk_parser/tests/data/regression_suite/tuplesort.sql index 96f8483d..00f57fab 100644 --- a/crates/squawk_parser/tests/data/regression_suite/tuplesort.sql +++ b/crates/squawk_parser/tests/data/regression_suite/tuplesort.sql @@ -296,12 +296,12 @@ SELECT $$ $$ AS qry ; -- test mark/restore with in-memory sorts -EXPLAIN (COSTS OFF) 'qry'; -'qry'; +-- EXPLAIN (COSTS OFF) 'qry'; +-- 'qry'; -- test mark/restore with on-disk sorts SET LOCAL work_mem = '100kB'; -EXPLAIN (COSTS OFF) 'qry'; -'qry'; +-- EXPLAIN (COSTS OFF) 'qry'; +-- 'qry'; COMMIT; diff --git a/crates/squawk_parser/tests/snapshots/tests__delete_ok.snap b/crates/squawk_parser/tests/snapshots/tests__delete_ok.snap index 20535e34..d4460da1 100644 --- a/crates/squawk_parser/tests/snapshots/tests__delete_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__delete_ok.snap @@ -208,18 +208,20 @@ SOURCE_FILE USING_CLAUSE USING_KW "using" WHITESPACE " " - FROM_ITEM - NAME_REF - IDENT "order_items" - WHITESPACE " " - ALIAS - NAME - IDENT "oi" + JOIN_EXPR + FROM_ITEM + NAME_REF + IDENT "order_items" + WHITESPACE " " + ALIAS + NAME + IDENT "oi" WHITESPACE "\n " JOIN - LEFT_KW "left" - WHITESPACE " " - JOIN_KW "join" + JOIN_LEFT + LEFT_KW "left" + WHITESPACE " " + JOIN_KW "join" WHITESPACE " " FROM_ITEM NAME_REF @@ -229,24 +231,25 @@ SOURCE_FILE NAME IDENT "o" WHITESPACE " " - ON_KW "on" - WHITESPACE " " - BIN_EXPR - FIELD_EXPR - NAME_REF - IDENT "oi" - DOT "." - NAME_REF - IDENT "order_id" + ON_CLAUSE + ON_KW "on" WHITESPACE " " - EQ "=" - WHITESPACE " " - FIELD_EXPR - NAME_REF - IDENT "o" - DOT "." - NAME_REF - IDENT "id" + BIN_EXPR + FIELD_EXPR + NAME_REF + IDENT "oi" + DOT "." + NAME_REF + IDENT "order_id" + WHITESPACE " " + EQ "=" + WHITESPACE " " + FIELD_EXPR + NAME_REF + IDENT "o" + DOT "." + NAME_REF + IDENT "id" SEMICOLON ";" WHITESPACE "\n\n" DELETE diff --git a/crates/squawk_parser/tests/snapshots/tests__merge_ok.snap b/crates/squawk_parser/tests/snapshots/tests__merge_ok.snap index 4558de72..8d66a93a 100644 --- a/crates/squawk_parser/tests/snapshots/tests__merge_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__merge_ok.snap @@ -19,8 +19,9 @@ SOURCE_FILE USING_CLAUSE USING_KW "using" WHITESPACE " " - NAME_REF - IDENT "u" + FROM_ITEM + NAME_REF + IDENT "u" WHITESPACE "\n " ON_KW "on" WHITESPACE " " @@ -74,14 +75,15 @@ SOURCE_FILE USING_CLAUSE USING_KW "using" WHITESPACE " " - NAME_REF - IDENT "bar" - WHITESPACE " " - ALIAS - AS_KW "as" + FROM_ITEM + NAME_REF + IDENT "bar" WHITESPACE " " - NAME - IDENT "b" + ALIAS + AS_KW "as" + WHITESPACE " " + NAME + IDENT "b" WHITESPACE "\n " ON_KW "on" WHITESPACE " " @@ -129,24 +131,25 @@ SOURCE_FILE USING_CLAUSE USING_KW "using" WHITESPACE " " - ROWS_KW "rows" - WHITESPACE " " - FROM_KW "from" - WHITESPACE " " - L_PAREN "(" - CALL_EXPR - NAME_REF - IDENT "f" - ARG_LIST - L_PAREN "(" - LITERAL - INT_NUMBER "1" - COMMA "," - WHITESPACE " " - LITERAL - INT_NUMBER "2" - R_PAREN ")" - R_PAREN ")" + FROM_ITEM + ROWS_KW "rows" + WHITESPACE " " + FROM_KW "from" + WHITESPACE " " + L_PAREN "(" + CALL_EXPR + NAME_REF + IDENT "f" + ARG_LIST + L_PAREN "(" + LITERAL + INT_NUMBER "1" + COMMA "," + WHITESPACE " " + LITERAL + INT_NUMBER "2" + R_PAREN ")" + R_PAREN ")" WHITESPACE "\n " ON_KW "on" WHITESPACE " " @@ -184,12 +187,13 @@ SOURCE_FILE USING_CLAUSE USING_KW "using" WHITESPACE " " - NAME_REF - IDENT "bar" - WHITESPACE " " - ALIAS - NAME - IDENT "b" + FROM_ITEM + NAME_REF + IDENT "bar" + WHITESPACE " " + ALIAS + NAME + IDENT "b" WHITESPACE "\n " ON_KW "on" WHITESPACE " " @@ -239,8 +243,9 @@ SOURCE_FILE USING_CLAUSE USING_KW "using" WHITESPACE " " - NAME_REF - IDENT "u" + FROM_ITEM + NAME_REF + IDENT "u" WHITESPACE "\n " ON_KW "on" WHITESPACE " " @@ -290,8 +295,9 @@ SOURCE_FILE USING_CLAUSE USING_KW "using" WHITESPACE " " - NAME_REF - IDENT "u" + FROM_ITEM + NAME_REF + IDENT "u" WHITESPACE "\n " ON_KW "on" WHITESPACE " " @@ -341,24 +347,25 @@ SOURCE_FILE USING_CLAUSE USING_KW "using" WHITESPACE " " - PAREN_SELECT - L_PAREN "(" - SELECT - SELECT_CLAUSE - SELECT_KW "select" + FROM_ITEM + PAREN_SELECT + L_PAREN "(" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + NAME_REF + IDENT "id" WHITESPACE " " - TARGET_LIST - TARGET + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + FROM_ITEM NAME_REF - IDENT "id" - WHITESPACE " " - FROM_CLAUSE - FROM_KW "from" - WHITESPACE " " - FROM_ITEM - NAME_REF - IDENT "bar" - R_PAREN ")" + IDENT "bar" + R_PAREN ")" WHITESPACE "\n " ON_KW "on" WHITESPACE " " @@ -406,62 +413,66 @@ SOURCE_FILE USING_CLAUSE USING_KW "using" WHITESPACE " " - PAREN_SELECT - L_PAREN "(" - SELECT - SELECT_CLAUSE - SELECT_KW "select" - WHITESPACE " " - TARGET_LIST - TARGET - NAME_REF - IDENT "id" - WHITESPACE " " - FROM_CLAUSE - FROM_KW "from" + FROM_ITEM + PAREN_SELECT + L_PAREN "(" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + NAME_REF + IDENT "id" WHITESPACE " " - FROM_ITEM - NAME_REF - IDENT "bar" + FROM_CLAUSE + FROM_KW "from" WHITESPACE " " - JOIN - JOIN_KW "join" - WHITESPACE " " + JOIN_EXPR FROM_ITEM NAME_REF - IDENT "foo" - WHITESPACE " " - ALIAS - AS_KW "as" - WHITESPACE " " - NAME - IDENT "f" + IDENT "bar" WHITESPACE " " - ON_KW "on" - WHITESPACE " " - BIN_EXPR - FIELD_EXPR - NAME_REF - IDENT "f" - DOT "." - NAME_REF - IDENT "id" - WHITESPACE " " - EQ "=" + JOIN + JOIN_INNER + JOIN_KW "join" WHITESPACE " " - FIELD_EXPR - NAME_REF - IDENT "bar" - DOT "." + FROM_ITEM NAME_REF - IDENT "id" - R_PAREN ")" - WHITESPACE " " - ALIAS - AS_KW "as" + IDENT "foo" + WHITESPACE " " + ALIAS + AS_KW "as" + WHITESPACE " " + NAME + IDENT "f" + WHITESPACE " " + ON_CLAUSE + ON_KW "on" + WHITESPACE " " + BIN_EXPR + FIELD_EXPR + NAME_REF + IDENT "f" + DOT "." + NAME_REF + IDENT "id" + WHITESPACE " " + EQ "=" + WHITESPACE " " + FIELD_EXPR + NAME_REF + IDENT "bar" + DOT "." + NAME_REF + IDENT "id" + R_PAREN ")" WHITESPACE " " - NAME - IDENT "u" + ALIAS + AS_KW "as" + WHITESPACE " " + NAME + IDENT "u" WHITESPACE "\n " ON_KW "on" WHITESPACE " " @@ -509,28 +520,29 @@ SOURCE_FILE USING_CLAUSE USING_KW "using" WHITESPACE " " - PAREN_SELECT - L_PAREN "(" - SELECT - SELECT_CLAUSE - SELECT_KW "select" + FROM_ITEM + PAREN_SELECT + L_PAREN "(" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + NAME_REF + IDENT "id" WHITESPACE " " - TARGET_LIST - TARGET + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + FROM_ITEM NAME_REF - IDENT "id" - WHITESPACE " " - FROM_CLAUSE - FROM_KW "from" - WHITESPACE " " - FROM_ITEM - NAME_REF - IDENT "bar" - R_PAREN ")" - WHITESPACE " " - ALIAS - NAME - IDENT "u" + IDENT "bar" + R_PAREN ")" + WHITESPACE " " + ALIAS + NAME + IDENT "u" WHITESPACE "\n " ON_KW "on" WHITESPACE " " @@ -578,8 +590,9 @@ SOURCE_FILE USING_CLAUSE USING_KW "using" WHITESPACE " " - NAME_REF - IDENT "u" + FROM_ITEM + NAME_REF + IDENT "u" WHITESPACE "\n " ON_KW "on" WHITESPACE " " diff --git a/crates/squawk_parser/tests/snapshots/tests__merge_pg17_ok.snap b/crates/squawk_parser/tests/snapshots/tests__merge_pg17_ok.snap index d86a5e6f..000232f9 100644 --- a/crates/squawk_parser/tests/snapshots/tests__merge_pg17_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__merge_pg17_ok.snap @@ -20,8 +20,9 @@ SOURCE_FILE USING_CLAUSE USING_KW "using" WHITESPACE " " - NAME_REF - IDENT "u" + FROM_ITEM + NAME_REF + IDENT "u" WHITESPACE "\n " ON_KW "on" WHITESPACE " " @@ -97,8 +98,9 @@ SOURCE_FILE USING_CLAUSE USING_KW "using" WHITESPACE " " - NAME_REF - IDENT "u" + FROM_ITEM + NAME_REF + IDENT "u" WHITESPACE " " ON_KW "on" WHITESPACE " " @@ -193,8 +195,9 @@ SOURCE_FILE USING_CLAUSE USING_KW "using" WHITESPACE " " - NAME_REF - IDENT "u" + FROM_ITEM + NAME_REF + IDENT "u" WHITESPACE "\n " ON_KW "on" WHITESPACE " " @@ -277,8 +280,9 @@ SOURCE_FILE USING_CLAUSE USING_KW "using" WHITESPACE " " - NAME_REF - IDENT "u" + FROM_ITEM + NAME_REF + IDENT "u" WHITESPACE "\n " ON_KW "on" WHITESPACE " " @@ -396,8 +400,9 @@ SOURCE_FILE USING_CLAUSE USING_KW "using" WHITESPACE " " - NAME_REF - IDENT "u" + FROM_ITEM + NAME_REF + IDENT "u" WHITESPACE "\n " ON_KW "on" WHITESPACE " " @@ -449,8 +454,9 @@ SOURCE_FILE USING_CLAUSE USING_KW "using" WHITESPACE " " - NAME_REF - IDENT "u" + FROM_ITEM + NAME_REF + IDENT "u" WHITESPACE "\n " ON_KW "on" WHITESPACE " " @@ -525,8 +531,9 @@ SOURCE_FILE USING_CLAUSE USING_KW "using" WHITESPACE " " - NAME_REF - IDENT "u" + FROM_ITEM + NAME_REF + IDENT "u" WHITESPACE "\n " ON_KW "on" WHITESPACE " " @@ -616,8 +623,9 @@ SOURCE_FILE USING_CLAUSE USING_KW "using" WHITESPACE " " - NAME_REF - IDENT "u" + FROM_ITEM + NAME_REF + IDENT "u" WHITESPACE "\n " ON_KW "on" WHITESPACE " " @@ -787,8 +795,9 @@ SOURCE_FILE USING_CLAUSE USING_KW "using" WHITESPACE " " - NAME_REF - IDENT "u" + FROM_ITEM + NAME_REF + IDENT "u" WHITESPACE "\n " ON_KW "on" WHITESPACE " " @@ -878,8 +887,9 @@ SOURCE_FILE USING_CLAUSE USING_KW "using" WHITESPACE " " - NAME_REF - IDENT "t" + FROM_ITEM + NAME_REF + IDENT "t" WHITESPACE "\n " ON_KW "on" WHITESPACE " " @@ -931,12 +941,13 @@ SOURCE_FILE USING_CLAUSE USING_KW "USING" WHITESPACE " " - NAME_REF - IDENT "recent_transactions" - WHITESPACE " " - ALIAS - NAME - IDENT "t" + FROM_ITEM + NAME_REF + IDENT "recent_transactions" + WHITESPACE " " + ALIAS + NAME + IDENT "t" WHITESPACE "\n" ON_KW "ON" WHITESPACE " " @@ -1044,35 +1055,36 @@ SOURCE_FILE USING_CLAUSE USING_KW "USING" WHITESPACE " " - PAREN_SELECT - L_PAREN "(" - SELECT - SELECT_CLAUSE - SELECT_KW "SELECT" + FROM_ITEM + PAREN_SELECT + L_PAREN "(" + SELECT + SELECT_CLAUSE + SELECT_KW "SELECT" + WHITESPACE " " + TARGET_LIST + TARGET + NAME_REF + IDENT "customer_id" + COMMA "," + WHITESPACE " " + TARGET + NAME_REF + IDENT "transaction_value" WHITESPACE " " - TARGET_LIST - TARGET - NAME_REF - IDENT "customer_id" - COMMA "," + FROM_CLAUSE + FROM_KW "FROM" WHITESPACE " " - TARGET + FROM_ITEM NAME_REF - IDENT "transaction_value" - WHITESPACE " " - FROM_CLAUSE - FROM_KW "FROM" - WHITESPACE " " - FROM_ITEM - NAME_REF - IDENT "recent_transactions" - R_PAREN ")" - WHITESPACE " " - ALIAS - AS_KW "AS" + IDENT "recent_transactions" + R_PAREN ")" WHITESPACE " " - NAME - IDENT "t" + ALIAS + AS_KW "AS" + WHITESPACE " " + NAME + IDENT "t" WHITESPACE "\n" ON_KW "ON" WHITESPACE " " @@ -1180,12 +1192,13 @@ SOURCE_FILE USING_CLAUSE USING_KW "USING" WHITESPACE " " - NAME_REF - IDENT "wine_stock_changes" - WHITESPACE " " - ALIAS - NAME - IDENT "s" + FROM_ITEM + NAME_REF + IDENT "wine_stock_changes" + WHITESPACE " " + ALIAS + NAME + IDENT "s" WHITESPACE "\n" ON_KW "ON" WHITESPACE " " @@ -1356,12 +1369,13 @@ SOURCE_FILE USING_CLAUSE USING_KW "USING" WHITESPACE " " - NAME_REF - IDENT "new_wine_list" - WHITESPACE " " - ALIAS - NAME - IDENT "s" + FROM_ITEM + NAME_REF + IDENT "new_wine_list" + WHITESPACE " " + ALIAS + NAME + IDENT "s" WHITESPACE "\n" ON_KW "ON" WHITESPACE " " diff --git a/crates/squawk_parser/tests/snapshots/tests__misc_ok.snap b/crates/squawk_parser/tests/snapshots/tests__misc_ok.snap index be263685..6259fd37 100644 --- a/crates/squawk_parser/tests/snapshots/tests__misc_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__misc_ok.snap @@ -3027,16 +3027,18 @@ SOURCE_FILE FROM_CLAUSE FROM_KW "from" WHITESPACE " " - FROM_ITEM - FIELD_EXPR - NAME_REF - IDENT "cron" - DOT "." - NAME_REF - IDENT "job_run_details" + JOIN_EXPR + FROM_ITEM + FIELD_EXPR + NAME_REF + IDENT "cron" + DOT "." + NAME_REF + IDENT "job_run_details" WHITESPACE " " JOIN - JOIN_KW "join" + JOIN_INNER + JOIN_KW "join" WHITESPACE " " FROM_ITEM FIELD_EXPR @@ -3357,56 +3359,60 @@ SOURCE_FILE FROM_CLAUSE FROM_KW "from" WHITESPACE " " - FROM_ITEM - FIELD_EXPR - NAME_REF - IDENT "cal" - DOT "." - NAME_REF - IDENT "event_types" - WHITESPACE " " - ALIAS - NAME - IDENT "t" + JOIN_EXPR + FROM_ITEM + FIELD_EXPR + NAME_REF + IDENT "cal" + DOT "." + NAME_REF + IDENT "event_types" + WHITESPACE " " + ALIAS + NAME + IDENT "t" WHITESPACE "\n " JOIN - CROSS_KW "cross" - WHITESPACE " " - JOIN_KW "join" - WHITESPACE " " - FROM_ITEM - CALL_EXPR - NAME_REF - IDENT "json_array_elements" - ARG_LIST - L_PAREN "(" - CAST_EXPR - PAREN_EXPR - L_PAREN "(" - BIN_EXPR - NAME_REF - IDENT "attrs" - CUSTOM_OP - MINUS "-" - R_ANGLE ">" - LITERAL - STRING "'eventTypeGroups'" - R_PAREN ")" - COLON_COLON - COLON ":" - COLON ":" - NAME_REF - JSON_KW "json" - R_PAREN ")" + JOIN_CROSS + CROSS_KW "cross" WHITESPACE " " - ALIAS - NAME - IDENT "etg" + JOIN_KW "join" + WHITESPACE " " + JOIN_EXPR + FROM_ITEM + CALL_EXPR + NAME_REF + IDENT "json_array_elements" + ARG_LIST + L_PAREN "(" + CAST_EXPR + PAREN_EXPR + L_PAREN "(" + BIN_EXPR + NAME_REF + IDENT "attrs" + CUSTOM_OP + MINUS "-" + R_ANGLE ">" + LITERAL + STRING "'eventTypeGroups'" + R_PAREN ")" + COLON_COLON + COLON ":" + COLON ":" + NAME_REF + JSON_KW "json" + R_PAREN ")" + WHITESPACE " " + ALIAS + NAME + IDENT "etg" WHITESPACE "\n " JOIN - CROSS_KW "cross" - WHITESPACE " " - JOIN_KW "join" + JOIN_CROSS + CROSS_KW "cross" + WHITESPACE " " + JOIN_KW "join" WHITESPACE " " FROM_ITEM CALL_EXPR @@ -3521,22 +3527,24 @@ SOURCE_FILE FROM_CLAUSE FROM_KW "from" WHITESPACE " " - FROM_ITEM - FIELD_EXPR - NAME_REF - IDENT "cal" - DOT "." - NAME_REF - IDENT "bookings" - WHITESPACE " " - ALIAS - NAME - IDENT "t" + JOIN_EXPR + FROM_ITEM + FIELD_EXPR + NAME_REF + IDENT "cal" + DOT "." + NAME_REF + IDENT "bookings" + WHITESPACE " " + ALIAS + NAME + IDENT "t" WHITESPACE "\n " JOIN - CROSS_KW "cross" - WHITESPACE " " - JOIN_KW "join" + JOIN_CROSS + CROSS_KW "cross" + WHITESPACE " " + JOIN_KW "join" WHITESPACE " " FROM_ITEM CALL_EXPR @@ -4100,50 +4108,55 @@ SOURCE_FILE FROM_CLAUSE FROM_KW "FROM" WHITESPACE " " - FROM_ITEM - NAME_REF - IDENT "events_core" - WHITESPACE " " - ALIAS - NAME - IDENT "e" - WHITESPACE "\n" - JOIN - LEFT_KW "LEFT" - WHITESPACE " " - JOIN_KW "JOIN" - WHITESPACE " " + JOIN_EXPR + JOIN_EXPR FROM_ITEM NAME_REF - IDENT "events_metadata" + IDENT "events_core" WHITESPACE " " ALIAS NAME - IDENT "m" - WHITESPACE "\n " - ON_KW "ON" - WHITESPACE " " - BIN_EXPR - FIELD_EXPR - NAME_REF IDENT "e" - DOT "." - NAME_REF - IDENT "event_id" - WHITESPACE " " - EQ "=" + WHITESPACE "\n" + JOIN + JOIN_LEFT + LEFT_KW "LEFT" + WHITESPACE " " + JOIN_KW "JOIN" WHITESPACE " " - FIELD_EXPR - NAME_REF - IDENT "m" - DOT "." + FROM_ITEM NAME_REF - IDENT "event_id" + IDENT "events_metadata" + WHITESPACE " " + ALIAS + NAME + IDENT "m" + WHITESPACE "\n " + ON_CLAUSE + ON_KW "ON" + WHITESPACE " " + BIN_EXPR + FIELD_EXPR + NAME_REF + IDENT "e" + DOT "." + NAME_REF + IDENT "event_id" + WHITESPACE " " + EQ "=" + WHITESPACE " " + FIELD_EXPR + NAME_REF + IDENT "m" + DOT "." + NAME_REF + IDENT "event_id" WHITESPACE "\n" JOIN - LEFT_KW "LEFT" - WHITESPACE " " - JOIN_KW "JOIN" + JOIN_LEFT + LEFT_KW "LEFT" + WHITESPACE " " + JOIN_KW "JOIN" WHITESPACE " " FROM_ITEM NAME_REF @@ -4153,24 +4166,25 @@ SOURCE_FILE NAME IDENT "p" WHITESPACE "\n " - ON_KW "ON" - WHITESPACE " " - BIN_EXPR - FIELD_EXPR - NAME_REF - IDENT "e" - DOT "." - NAME_REF - IDENT "event_id" - WHITESPACE " " - EQ "=" + ON_CLAUSE + ON_KW "ON" WHITESPACE " " - FIELD_EXPR - NAME_REF - IDENT "p" - DOT "." - NAME_REF - IDENT "event_id" + BIN_EXPR + FIELD_EXPR + NAME_REF + IDENT "e" + DOT "." + NAME_REF + IDENT "event_id" + WHITESPACE " " + EQ "=" + WHITESPACE " " + FIELD_EXPR + NAME_REF + IDENT "p" + DOT "." + NAME_REF + IDENT "event_id" WHITESPACE "\n" WHERE_CLAUSE WHERE_KW "WHERE" @@ -5044,16 +5058,18 @@ SOURCE_FILE FROM_CLAUSE FROM_KW "FROM" WHITESPACE " " - FROM_ITEM - NAME_REF - IDENT "events" - WHITESPACE " " - ALIAS - NAME - IDENT "e" + JOIN_EXPR + FROM_ITEM + NAME_REF + IDENT "events" + WHITESPACE " " + ALIAS + NAME + IDENT "e" WHITESPACE "\n" JOIN - JOIN_KW "JOIN" + JOIN_INNER + JOIN_KW "JOIN" WHITESPACE " " FROM_ITEM NAME_REF @@ -5063,24 +5079,25 @@ SOURCE_FILE NAME IDENT "s" WHITESPACE "\n " - ON_KW "ON" - WHITESPACE " " - BIN_EXPR - FIELD_EXPR - NAME_REF - IDENT "e" - DOT "." - NAME_REF - IDENT "user_id" - WHITESPACE " " - EQ "=" + ON_CLAUSE + ON_KW "ON" WHITESPACE " " - FIELD_EXPR - NAME_REF - IDENT "s" - DOT "." - NAME_REF - IDENT "user_id" + BIN_EXPR + FIELD_EXPR + NAME_REF + IDENT "e" + DOT "." + NAME_REF + IDENT "user_id" + WHITESPACE " " + EQ "=" + WHITESPACE " " + FIELD_EXPR + NAME_REF + IDENT "s" + DOT "." + NAME_REF + IDENT "user_id" WHITESPACE "\n" GROUP_BY_CLAUSE GROUP_KW "GROUP" @@ -5714,46 +5731,51 @@ SOURCE_FILE FROM_CLAUSE FROM_KW "FROM" WHITESPACE "\n " - FROM_ITEM - NAME_REF - IDENT "products" - WHITESPACE " " - ALIAS - NAME - IDENT "p" - WHITESPACE "\n " - JOIN - JOIN_KW "JOIN" - WHITESPACE " " + JOIN_EXPR + JOIN_EXPR FROM_ITEM NAME_REF - IDENT "product_orders" + IDENT "products" WHITESPACE " " ALIAS NAME - IDENT "po" - WHITESPACE " " - ON_KW "ON" - WHITESPACE " " - BIN_EXPR - FIELD_EXPR - NAME_REF IDENT "p" - DOT "." - NAME_REF - IDENT "sku" - WHITESPACE " " - EQ "=" + WHITESPACE "\n " + JOIN + JOIN_INNER + JOIN_KW "JOIN" WHITESPACE " " - FIELD_EXPR - NAME_REF - IDENT "po" - DOT "." + FROM_ITEM NAME_REF - IDENT "sku" + IDENT "product_orders" + WHITESPACE " " + ALIAS + NAME + IDENT "po" + WHITESPACE " " + ON_CLAUSE + ON_KW "ON" + WHITESPACE " " + BIN_EXPR + FIELD_EXPR + NAME_REF + IDENT "p" + DOT "." + NAME_REF + IDENT "sku" + WHITESPACE " " + EQ "=" + WHITESPACE " " + FIELD_EXPR + NAME_REF + IDENT "po" + DOT "." + NAME_REF + IDENT "sku" WHITESPACE "\n " JOIN - JOIN_KW "JOIN" + JOIN_INNER + JOIN_KW "JOIN" WHITESPACE " " FROM_ITEM NAME_REF @@ -5763,24 +5785,25 @@ SOURCE_FILE NAME IDENT "o" WHITESPACE " " - ON_KW "ON" - WHITESPACE " " - BIN_EXPR - FIELD_EXPR - NAME_REF - IDENT "po" - DOT "." - NAME_REF - IDENT "order_id" - WHITESPACE " " - EQ "=" + ON_CLAUSE + ON_KW "ON" WHITESPACE " " - FIELD_EXPR - NAME_REF - IDENT "o" - DOT "." - NAME_REF - IDENT "order_id" + BIN_EXPR + FIELD_EXPR + NAME_REF + IDENT "po" + DOT "." + NAME_REF + IDENT "order_id" + WHITESPACE " " + EQ "=" + WHITESPACE " " + FIELD_EXPR + NAME_REF + IDENT "o" + DOT "." + NAME_REF + IDENT "order_id" WHITESPACE "\n" WHERE_CLAUSE WHERE_KW "WHERE" @@ -6117,27 +6140,30 @@ SOURCE_FILE FROM_CLAUSE FROM_KW "FROM" WHITESPACE " " - FROM_ITEM - NAME_REF - IDENT "solar_panel_metrics" + JOIN_EXPR + FROM_ITEM + NAME_REF + IDENT "solar_panel_metrics" WHITESPACE "\n" JOIN - JOIN_KW "JOIN" + JOIN_INNER + JOIN_KW "JOIN" WHITESPACE " " FROM_ITEM NAME_REF IDENT "panel_specifications" WHITESPACE " " - ON_KW "ON" - WHITESPACE " " - BIN_EXPR - NAME_REF - IDENT "panel_id" - WHITESPACE " " - EQ "=" + ON_CLAUSE + ON_KW "ON" WHITESPACE " " - NAME_REF - IDENT "specs_id" + BIN_EXPR + NAME_REF + IDENT "panel_id" + WHITESPACE " " + EQ "=" + WHITESPACE " " + NAME_REF + IDENT "specs_id" WHITESPACE "\n" WHERE_CLAUSE WHERE_KW "WHERE" @@ -6357,16 +6383,18 @@ SOURCE_FILE FROM_CLAUSE FROM_KW "FROM" WHITESPACE " " - FROM_ITEM - NAME_REF - IDENT "turbine_metrics" - WHITESPACE " " - ALIAS - NAME - IDENT "t" + JOIN_EXPR + FROM_ITEM + NAME_REF + IDENT "turbine_metrics" + WHITESPACE " " + ALIAS + NAME + IDENT "t" WHITESPACE "\n" JOIN - JOIN_KW "JOIN" + JOIN_INNER + JOIN_KW "JOIN" WHITESPACE " " FROM_ITEM NAME_REF diff --git a/crates/squawk_parser/tests/snapshots/tests__prepare_ok.snap b/crates/squawk_parser/tests/snapshots/tests__prepare_ok.snap index c3fdfa7b..40a286b3 100644 --- a/crates/squawk_parser/tests/snapshots/tests__prepare_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__prepare_ok.snap @@ -311,8 +311,9 @@ SOURCE_FILE USING_CLAUSE USING_KW "using" WHITESPACE " " - NAME_REF - IDENT "t2" + FROM_ITEM + NAME_REF + IDENT "t2" WHITESPACE " " ON_KW "on" WHITESPACE " " diff --git a/crates/squawk_parser/tests/snapshots/tests__regression_merge.snap b/crates/squawk_parser/tests/snapshots/tests__regression_merge.snap index 70951aca..ea354046 100644 --- a/crates/squawk_parser/tests/snapshots/tests__regression_merge.snap +++ b/crates/squawk_parser/tests/snapshots/tests__regression_merge.snap @@ -63,192 +63,3 @@ ERROR@2347: expected SEMICOLON ERROR@2348: expected command, found IDENT ERROR@2356: expected command, found EQ ERROR@2358: expected command, found INT_NUMBER -ERROR@41486: expected ON_KW -ERROR@41493: expected WHEN_KW -ERROR@41493: expected MATCHED, or NOT MATCHED -ERROR@41493: expected THEN_KW -ERROR@41493: expected INSERT, UPDATE, DELETE, or DO NOTHING -ERROR@41493: expected SEMICOLON -ERROR@41494: expected command, found JOIN_KW -ERROR@41499: expected command, found IDENT -ERROR@41510: expected command, found IDENT -ERROR@41513: expected command, found ON_KW -ERROR@41516: expected command, found IDENT -ERROR@41521: expected command, found EQ -ERROR@41523: expected command, found IDENT -ERROR@41528: expected command, found ON_KW -ERROR@41531: expected command, found IDENT -ERROR@41532: expected command, found DOT -ERROR@41533: expected command, found IDENT -ERROR@41537: expected command, found EQ -ERROR@41539: expected command, found IDENT -ERROR@41544: expected command, found WHEN_KW -ERROR@41549: expected command, found NOT_KW -ERROR@41553: expected command, found MATCHED_KW -ERROR@41561: expected command, found THEN_KW -ERROR@41573: expected INTO_KW -ERROR@41600: expected select stmt -ERROR@41706: expected ON_KW -ERROR@41713: expected WHEN_KW -ERROR@41713: expected MATCHED, or NOT MATCHED -ERROR@41713: expected THEN_KW -ERROR@41713: expected INSERT, UPDATE, DELETE, or DO NOTHING -ERROR@41713: expected SEMICOLON -ERROR@41714: expected command, found JOIN_KW -ERROR@41719: expected command, found IDENT -ERROR@41730: expected command, found IDENT -ERROR@41733: expected command, found ON_KW -ERROR@41736: expected command, found IDENT -ERROR@41741: expected command, found EQ -ERROR@41743: expected command, found IDENT -ERROR@41748: expected command, found AND_KW -ERROR@41752: expected command, found IDENT -ERROR@41757: expected command, found EQ -ERROR@41759: expected command, found INT_NUMBER -ERROR@41762: expected command, found ON_KW -ERROR@41765: expected command, found IDENT -ERROR@41766: expected command, found DOT -ERROR@41767: expected command, found IDENT -ERROR@41771: expected command, found EQ -ERROR@41773: expected command, found IDENT -ERROR@41778: expected command, found WHEN_KW -ERROR@41783: expected command, found NOT_KW -ERROR@41787: expected command, found MATCHED_KW -ERROR@41795: expected command, found THEN_KW -ERROR@41807: expected INTO_KW -ERROR@41834: expected select stmt -ERROR@41834: expected SEMICOLON -ERROR@41835: expected command, found WHEN_KW -ERROR@41840: expected command, found MATCHED_KW -ERROR@41848: expected command, found THEN_KW -ERROR@41860: expected FROM_KW -ERROR@41860: expected relation name -ERROR@41953: expected ON_KW -ERROR@41960: expected WHEN_KW -ERROR@41960: expected MATCHED, or NOT MATCHED -ERROR@41960: expected THEN_KW -ERROR@41960: expected INSERT, UPDATE, DELETE, or DO NOTHING -ERROR@41960: expected SEMICOLON -ERROR@41961: expected command, found JOIN_KW -ERROR@41966: expected command, found IDENT -ERROR@41977: expected command, found IDENT -ERROR@41980: expected command, found ON_KW -ERROR@41983: expected command, found IDENT -ERROR@41988: expected command, found EQ -ERROR@41990: expected command, found IDENT -ERROR@41995: expected command, found ON_KW -ERROR@41998: expected command, found IDENT -ERROR@41999: expected command, found DOT -ERROR@42000: expected command, found IDENT -ERROR@42004: expected command, found EQ -ERROR@42006: expected command, found IDENT -ERROR@42011: expected command, found WHEN_KW -ERROR@42016: expected command, found NOT_KW -ERROR@42020: expected command, found MATCHED_KW -ERROR@42028: expected command, found THEN_KW -ERROR@42040: expected INTO_KW -ERROR@42060: expected R_PAREN -ERROR@42060: expected select stmt -ERROR@42060: expected SEMICOLON -ERROR@42061: expected command, found PLUS -ERROR@42063: expected command, found IDENT -ERROR@42067: expected command, found COMMA -ERROR@42069: expected command, found IDENT -ERROR@42073: expected command, found R_PAREN -ERROR@42075: expected command, found WHEN_KW -ERROR@42080: expected command, found MATCHED_KW -ERROR@42088: expected command, found THEN_KW -ERROR@42108: expected SET_KW -ERROR@42108: expected name -ERROR@42182: expected ON_KW -ERROR@42189: expected WHEN_KW -ERROR@42189: expected MATCHED, or NOT MATCHED -ERROR@42189: expected THEN_KW -ERROR@42189: expected INSERT, UPDATE, DELETE, or DO NOTHING -ERROR@42189: expected SEMICOLON -ERROR@42190: expected command, found JOIN_KW -ERROR@42195: expected command, found IDENT -ERROR@42206: expected command, found IDENT -ERROR@42209: expected command, found ON_KW -ERROR@42212: expected command, found IDENT -ERROR@42217: expected command, found EQ -ERROR@42219: expected command, found IDENT -ERROR@42224: expected command, found AND_KW -ERROR@42228: expected command, found IDENT -ERROR@42233: expected command, found EQ -ERROR@42235: expected command, found INT_NUMBER -ERROR@42238: expected command, found ON_KW -ERROR@42241: expected command, found IDENT -ERROR@42242: expected command, found DOT -ERROR@42243: expected command, found IDENT -ERROR@42247: expected command, found EQ -ERROR@42249: expected command, found IDENT -ERROR@42254: expected command, found WHEN_KW -ERROR@42259: expected command, found MATCHED_KW -ERROR@42267: expected command, found THEN_KW -ERROR@42287: expected SET_KW -ERROR@42287: expected name -ERROR@42477: expected ON_KW -ERROR@42483: expected WHEN_KW -ERROR@42483: expected MATCHED, or NOT MATCHED -ERROR@42483: expected THEN_KW -ERROR@42483: expected INSERT, UPDATE, DELETE, or DO NOTHING -ERROR@42483: expected SEMICOLON -ERROR@42484: expected command, found JOIN_KW -ERROR@42489: expected command, found IDENT -ERROR@42500: expected command, found IDENT -ERROR@42504: expected command, found ON_KW -ERROR@42507: expected command, found IDENT -ERROR@42509: expected command, found DOT -ERROR@42510: expected command, found IDENT -ERROR@42515: expected command, found EQ -ERROR@42517: expected command, found IDENT -ERROR@42520: expected command, found DOT -ERROR@42521: expected command, found IDENT -ERROR@42526: expected command, found STAR -ERROR@42528: expected command, found INT_NUMBER -ERROR@42531: expected command, found ON_KW -ERROR@42534: expected command, found IDENT -ERROR@42535: expected command, found DOT -ERROR@42536: expected command, found IDENT -ERROR@42540: expected command, found EQ -ERROR@42542: expected command, found IDENT -ERROR@42544: expected command, found DOT -ERROR@42545: expected command, found IDENT -ERROR@42550: expected command, found WHEN_KW -ERROR@42555: expected command, found NOT_KW -ERROR@42559: expected command, found MATCHED_KW -ERROR@42567: expected command, found THEN_KW -ERROR@42579: expected INTO_KW -ERROR@42579: expected path name -ERROR@42827: expected ON_KW -ERROR@42834: expected WHEN_KW -ERROR@42834: expected MATCHED, or NOT MATCHED -ERROR@42834: expected THEN_KW -ERROR@42834: expected INSERT, UPDATE, DELETE, or DO NOTHING -ERROR@42834: expected SEMICOLON -ERROR@42835: expected command, found JOIN_KW -ERROR@42840: expected command, found IDENT -ERROR@42851: expected command, found IDENT -ERROR@42854: expected command, found ON_KW -ERROR@42857: expected command, found IDENT -ERROR@42859: expected command, found DOT -ERROR@42860: expected command, found IDENT -ERROR@42864: expected command, found EQ -ERROR@42866: expected command, found IDENT -ERROR@42868: expected command, found DOT -ERROR@42869: expected command, found IDENT -ERROR@42873: expected command, found ON_KW -ERROR@42876: expected command, found IDENT -ERROR@42877: expected command, found DOT -ERROR@42878: expected command, found IDENT -ERROR@42882: expected command, found EQ -ERROR@42884: expected command, found IDENT -ERROR@42886: expected command, found DOT -ERROR@42887: expected command, found IDENT -ERROR@42891: expected command, found WHEN_KW -ERROR@42896: expected command, found NOT_KW -ERROR@42900: expected command, found MATCHED_KW -ERROR@42908: expected command, found THEN_KW -ERROR@42920: expected INTO_KW -ERROR@42949: expected select stmt diff --git a/crates/squawk_parser/tests/snapshots/tests__regression_partition_prune.snap b/crates/squawk_parser/tests/snapshots/tests__regression_partition_prune.snap index fb7da063..05922e93 100644 --- a/crates/squawk_parser/tests/snapshots/tests__regression_partition_prune.snap +++ b/crates/squawk_parser/tests/snapshots/tests__regression_partition_prune.snap @@ -2,331 +2,4 @@ source: crates/squawk_parser/tests/tests.rs input_file: crates/squawk_parser/tests/data/regression_suite/partition_prune.sql --- -ERROR@60950: expected ON_KW -ERROR@60955: expected WHEN_KW -ERROR@60955: expected MATCHED, or NOT MATCHED -ERROR@60955: expected THEN_KW -ERROR@60955: expected INSERT, UPDATE, DELETE, or DO NOTHING -ERROR@60955: expected SEMICOLON -ERROR@60956: expected command, found IDENT -ERROR@60967: expected command, found IDENT -ERROR@60971: expected command, found ON_KW -ERROR@60975: expected R_PAREN -ERROR@60975: expected SEMICOLON -ERROR@60975: expected command, found IDENT -ERROR@60976: expected command, found DOT -ERROR@60977: expected command, found IDENT -ERROR@60981: expected command, found EQ -ERROR@60983: expected command, found IDENT -ERROR@60986: expected command, found DOT -ERROR@60987: expected command, found IDENT -ERROR@60988: expected command, found R_PAREN -ERROR@60990: expected command, found ON_KW -ERROR@60993: expected command, found IDENT -ERROR@60995: expected command, found DOT -ERROR@60996: expected command, found IDENT -ERROR@60998: expected command, found EQ -ERROR@61000: expected command, found IDENT -ERROR@61003: expected command, found DOT -ERROR@61004: expected command, found IDENT -ERROR@61006: expected command, found WHEN_KW -ERROR@61011: expected command, found MATCHED_KW -ERROR@61019: expected command, found THEN_KW -ERROR@61030: expected FROM_KW -ERROR@61043: expected SEMICOLON -ERROR@61043: expected command, found DOT -ERROR@61044: expected command, found IDENT -ERROR@61114: expected ON_KW -ERROR@61119: expected WHEN_KW -ERROR@61119: expected MATCHED, or NOT MATCHED -ERROR@61119: expected THEN_KW -ERROR@61119: expected INSERT, UPDATE, DELETE, or DO NOTHING -ERROR@61119: expected SEMICOLON -ERROR@61120: expected command, found IDENT -ERROR@61131: expected command, found IDENT -ERROR@61135: expected command, found ON_KW -ERROR@61139: expected R_PAREN -ERROR@61139: expected SEMICOLON -ERROR@61139: expected command, found IDENT -ERROR@61140: expected command, found DOT -ERROR@61141: expected command, found IDENT -ERROR@61145: expected command, found EQ -ERROR@61147: expected command, found IDENT -ERROR@61150: expected command, found DOT -ERROR@61151: expected command, found IDENT -ERROR@61152: expected command, found R_PAREN -ERROR@61154: expected command, found ON_KW -ERROR@61157: expected command, found IDENT -ERROR@61159: expected command, found DOT -ERROR@61160: expected command, found IDENT -ERROR@61162: expected command, found EQ -ERROR@61164: expected command, found IDENT -ERROR@61167: expected command, found DOT -ERROR@61168: expected command, found IDENT -ERROR@61170: expected command, found WHEN_KW -ERROR@61175: expected command, found MATCHED_KW -ERROR@61183: expected command, found THEN_KW -ERROR@61194: expected FROM_KW -ERROR@61207: expected SEMICOLON -ERROR@61207: expected command, found DOT -ERROR@61208: expected command, found IDENT -ERROR@61339: expected ON_KW -ERROR@61344: expected WHEN_KW -ERROR@61344: expected MATCHED, or NOT MATCHED -ERROR@61344: expected THEN_KW -ERROR@61344: expected INSERT, UPDATE, DELETE, or DO NOTHING -ERROR@61344: expected SEMICOLON -ERROR@61345: expected command, found IDENT -ERROR@61356: expected command, found IDENT -ERROR@61360: expected command, found ON_KW -ERROR@61364: expected R_PAREN -ERROR@61364: expected SEMICOLON -ERROR@61364: expected command, found IDENT -ERROR@61365: expected command, found DOT -ERROR@61366: expected command, found IDENT -ERROR@61370: expected command, found EQ -ERROR@61372: expected command, found IDENT -ERROR@61375: expected command, found DOT -ERROR@61376: expected command, found IDENT -ERROR@61377: expected command, found R_PAREN -ERROR@61379: expected command, found ON_KW -ERROR@61382: expected command, found IDENT -ERROR@61384: expected command, found DOT -ERROR@61385: expected command, found IDENT -ERROR@61387: expected command, found EQ -ERROR@61389: expected command, found IDENT -ERROR@61392: expected command, found DOT -ERROR@61393: expected command, found IDENT -ERROR@61395: expected command, found WHEN_KW -ERROR@61400: expected command, found MATCHED_KW -ERROR@61408: expected command, found THEN_KW -ERROR@61419: expected FROM_KW -ERROR@61432: expected SEMICOLON -ERROR@61432: expected command, found DOT -ERROR@61433: expected command, found IDENT -ERROR@61507: expected ON_KW -ERROR@61512: expected WHEN_KW -ERROR@61512: expected MATCHED, or NOT MATCHED -ERROR@61512: expected THEN_KW -ERROR@61512: expected INSERT, UPDATE, DELETE, or DO NOTHING -ERROR@61512: expected SEMICOLON -ERROR@61513: expected command, found IDENT -ERROR@61524: expected command, found IDENT -ERROR@61528: expected command, found ON_KW -ERROR@61532: expected R_PAREN -ERROR@61532: expected SEMICOLON -ERROR@61532: expected command, found IDENT -ERROR@61533: expected command, found DOT -ERROR@61534: expected command, found IDENT -ERROR@61538: expected command, found EQ -ERROR@61540: expected command, found IDENT -ERROR@61543: expected command, found DOT -ERROR@61544: expected command, found IDENT -ERROR@61545: expected command, found R_PAREN -ERROR@61547: expected command, found ON_KW -ERROR@61550: expected command, found IDENT -ERROR@61552: expected command, found DOT -ERROR@61553: expected command, found IDENT -ERROR@61555: expected command, found EQ -ERROR@61557: expected command, found IDENT -ERROR@61560: expected command, found DOT -ERROR@61561: expected command, found IDENT -ERROR@61563: expected command, found WHEN_KW -ERROR@61568: expected command, found MATCHED_KW -ERROR@61576: expected command, found THEN_KW -ERROR@61587: expected FROM_KW -ERROR@61600: expected SEMICOLON -ERROR@61600: expected command, found DOT -ERROR@61601: expected command, found IDENT -ERROR@61779: expected ON_KW -ERROR@61784: expected WHEN_KW -ERROR@61784: expected MATCHED, or NOT MATCHED -ERROR@61784: expected THEN_KW -ERROR@61784: expected INSERT, UPDATE, DELETE, or DO NOTHING -ERROR@61784: expected SEMICOLON -ERROR@61785: expected command, found IDENT -ERROR@61796: expected command, found IDENT -ERROR@61800: expected command, found ON_KW -ERROR@61804: expected R_PAREN -ERROR@61804: expected SEMICOLON -ERROR@61804: expected command, found IDENT -ERROR@61805: expected command, found DOT -ERROR@61806: expected command, found IDENT -ERROR@61810: expected command, found EQ -ERROR@61812: expected command, found IDENT -ERROR@61815: expected command, found DOT -ERROR@61816: expected command, found IDENT -ERROR@61817: expected command, found R_PAREN -ERROR@61819: expected command, found ON_KW -ERROR@61822: expected command, found IDENT -ERROR@61824: expected command, found DOT -ERROR@61825: expected command, found IDENT -ERROR@61827: expected command, found EQ -ERROR@61829: expected command, found IDENT -ERROR@61841: expected SEMICOLON -ERROR@61842: expected command, found PLUS -ERROR@61844: expected command, found INT_NUMBER -ERROR@61846: expected command, found WHEN_KW -ERROR@61851: expected command, found NOT_KW -ERROR@61855: expected command, found MATCHED_KW -ERROR@61863: expected command, found THEN_KW -ERROR@61874: expected INTO_KW -ERROR@61883: expected R_PAREN -ERROR@61883: expected SEMICOLON -ERROR@61883: expected command, found INT_NUMBER -ERROR@61884: expected command, found COMMA -ERROR@61886: expected command, found STRING -ERROR@61889: expected command, found COMMA -ERROR@61891: expected command, found FALSE_KW -ERROR@61896: expected command, found R_PAREN -ERROR@61898: expected command, found RETURNING_KW -ERROR@61908: expected command, found IDENT -ERROR@61910: expected command, found DOT -ERROR@61911: expected command, found IDENT -ERROR@61985: expected ON_KW -ERROR@61990: expected WHEN_KW -ERROR@61990: expected MATCHED, or NOT MATCHED -ERROR@61990: expected THEN_KW -ERROR@61990: expected INSERT, UPDATE, DELETE, or DO NOTHING -ERROR@61990: expected SEMICOLON -ERROR@61991: expected command, found IDENT -ERROR@62002: expected command, found IDENT -ERROR@62006: expected command, found ON_KW -ERROR@62010: expected R_PAREN -ERROR@62010: expected SEMICOLON -ERROR@62010: expected command, found IDENT -ERROR@62011: expected command, found DOT -ERROR@62012: expected command, found IDENT -ERROR@62016: expected command, found EQ -ERROR@62018: expected command, found IDENT -ERROR@62021: expected command, found DOT -ERROR@62022: expected command, found IDENT -ERROR@62023: expected command, found R_PAREN -ERROR@62025: expected command, found ON_KW -ERROR@62028: expected command, found IDENT -ERROR@62030: expected command, found DOT -ERROR@62031: expected command, found IDENT -ERROR@62033: expected command, found EQ -ERROR@62035: expected command, found IDENT -ERROR@62047: expected SEMICOLON -ERROR@62048: expected command, found PLUS -ERROR@62050: expected command, found INT_NUMBER -ERROR@62052: expected command, found WHEN_KW -ERROR@62057: expected command, found NOT_KW -ERROR@62061: expected command, found MATCHED_KW -ERROR@62069: expected command, found THEN_KW -ERROR@62080: expected INTO_KW -ERROR@62089: expected R_PAREN -ERROR@62089: expected SEMICOLON -ERROR@62089: expected command, found INT_NUMBER -ERROR@62090: expected command, found COMMA -ERROR@62092: expected command, found STRING -ERROR@62095: expected command, found COMMA -ERROR@62097: expected command, found FALSE_KW -ERROR@62102: expected command, found R_PAREN -ERROR@62104: expected command, found RETURNING_KW -ERROR@62114: expected command, found IDENT -ERROR@62116: expected command, found DOT -ERROR@62117: expected command, found IDENT -ERROR@62373: expected ON_KW -ERROR@62378: expected WHEN_KW -ERROR@62378: expected MATCHED, or NOT MATCHED -ERROR@62378: expected THEN_KW -ERROR@62378: expected INSERT, UPDATE, DELETE, or DO NOTHING -ERROR@62378: expected R_PAREN -ERROR@62378: expected DELETE, SELECT, TABLE, UPDATE, VALUES, or MERGE, got: IDENT -ERROR@62378: expected SELECT, INSERT, UPDATE, DELETE, MERGE, VALUES, EXECUTE, DECLARE, CREATE TABLE AS, or CREATE MATERIALIZED VIEW AS -ERROR@62378: expected SEMICOLON -ERROR@62379: expected command, found IDENT -ERROR@62390: expected command, found IDENT -ERROR@62394: expected command, found ON_KW -ERROR@62398: expected R_PAREN -ERROR@62398: expected SEMICOLON -ERROR@62398: expected command, found IDENT -ERROR@62399: expected command, found DOT -ERROR@62400: expected command, found IDENT -ERROR@62404: expected command, found EQ -ERROR@62406: expected command, found IDENT -ERROR@62409: expected command, found DOT -ERROR@62410: expected command, found IDENT -ERROR@62411: expected command, found R_PAREN -ERROR@62413: expected command, found ON_KW -ERROR@62416: expected command, found IDENT -ERROR@62418: expected command, found DOT -ERROR@62419: expected command, found IDENT -ERROR@62421: expected command, found EQ -ERROR@62423: expected command, found IDENT -ERROR@62435: expected SEMICOLON -ERROR@62436: expected command, found PLUS -ERROR@62438: expected command, found INT_NUMBER -ERROR@62442: expected command, found WHEN_KW -ERROR@62447: expected command, found NOT_KW -ERROR@62451: expected command, found MATCHED_KW -ERROR@62459: expected command, found THEN_KW -ERROR@62470: expected INTO_KW -ERROR@62479: expected R_PAREN -ERROR@62479: expected SEMICOLON -ERROR@62479: expected command, found INT_NUMBER -ERROR@62480: expected command, found COMMA -ERROR@62482: expected command, found STRING -ERROR@62485: expected command, found COMMA -ERROR@62487: expected command, found FALSE_KW -ERROR@62492: expected command, found R_PAREN -ERROR@62494: expected command, found RETURNING_KW -ERROR@62504: expected command, found MERGE_ACTION_KW -ERROR@62518: expected SEMICOLON -ERROR@62518: expected command, found COMMA -ERROR@62520: expected command, found IDENT -ERROR@62522: expected command, found DOT -ERROR@62523: expected command, found STAR -ERROR@62525: expected command, found R_PAREN -ERROR@62668: expected ON_KW -ERROR@62673: expected WHEN_KW -ERROR@62673: expected MATCHED, or NOT MATCHED -ERROR@62673: expected THEN_KW -ERROR@62673: expected INSERT, UPDATE, DELETE, or DO NOTHING -ERROR@62673: expected R_PAREN -ERROR@62673: expected DELETE, SELECT, TABLE, UPDATE, VALUES, or MERGE, got: IDENT -ERROR@62674: expected command, found IDENT -ERROR@62685: expected command, found IDENT -ERROR@62689: expected command, found ON_KW -ERROR@62693: expected R_PAREN -ERROR@62693: expected SEMICOLON -ERROR@62693: expected command, found IDENT -ERROR@62694: expected command, found DOT -ERROR@62695: expected command, found IDENT -ERROR@62699: expected command, found EQ -ERROR@62701: expected command, found IDENT -ERROR@62704: expected command, found DOT -ERROR@62705: expected command, found IDENT -ERROR@62706: expected command, found R_PAREN -ERROR@62708: expected command, found ON_KW -ERROR@62711: expected command, found IDENT -ERROR@62713: expected command, found DOT -ERROR@62714: expected command, found IDENT -ERROR@62716: expected command, found EQ -ERROR@62718: expected command, found IDENT -ERROR@62730: expected SEMICOLON -ERROR@62731: expected command, found PLUS -ERROR@62733: expected command, found INT_NUMBER -ERROR@62737: expected command, found WHEN_KW -ERROR@62742: expected command, found NOT_KW -ERROR@62746: expected command, found MATCHED_KW -ERROR@62754: expected command, found THEN_KW -ERROR@62765: expected INTO_KW -ERROR@62774: expected R_PAREN -ERROR@62774: expected SEMICOLON -ERROR@62774: expected command, found INT_NUMBER -ERROR@62775: expected command, found COMMA -ERROR@62777: expected command, found STRING -ERROR@62780: expected command, found COMMA -ERROR@62782: expected command, found FALSE_KW -ERROR@62787: expected command, found R_PAREN -ERROR@62789: expected command, found RETURNING_KW -ERROR@62799: expected command, found MERGE_ACTION_KW -ERROR@62813: expected SEMICOLON -ERROR@62813: expected command, found COMMA -ERROR@62815: expected command, found IDENT -ERROR@62817: expected command, found DOT -ERROR@62818: expected command, found STAR -ERROR@62820: expected command, found R_PAREN + diff --git a/crates/squawk_parser/tests/snapshots/tests__regression_suite_errors.snap b/crates/squawk_parser/tests/snapshots/tests__regression_suite_errors.snap index 90bea2a2..f8134089 100644 --- a/crates/squawk_parser/tests/snapshots/tests__regression_suite_errors.snap +++ b/crates/squawk_parser/tests/snapshots/tests__regression_suite_errors.snap @@ -4,10 +4,8 @@ expression: "out.join(\"\\n\")" --- tests/snapshots/tests__regression_inherit.snap:34 tests/snapshots/tests__regression_join.snap:20 -tests/snapshots/tests__regression_merge.snap:250 -tests/snapshots/tests__regression_partition_prune.snap:328 +tests/snapshots/tests__regression_merge.snap:61 tests/snapshots/tests__regression_privileges.snap:14 tests/snapshots/tests__regression_strings.snap:49 -tests/snapshots/tests__regression_tuplesort.snap:6 tests/snapshots/tests__regression_union.snap:15 tests/snapshots/tests__regression_xml.snap:382 diff --git a/crates/squawk_parser/tests/snapshots/tests__regression_tuplesort.snap b/crates/squawk_parser/tests/snapshots/tests__regression_tuplesort.snap index 3f2a804d..f0cdd1bd 100644 --- a/crates/squawk_parser/tests/snapshots/tests__regression_tuplesort.snap +++ b/crates/squawk_parser/tests/snapshots/tests__regression_tuplesort.snap @@ -2,9 +2,4 @@ source: crates/squawk_parser/tests/tests.rs input_file: crates/squawk_parser/tests/data/regression_suite/tuplesort.sql --- -ERROR@10543: expected command, found STRING -ERROR@10548: expected SELECT, INSERT, UPDATE, DELETE, MERGE, VALUES, EXECUTE, DECLARE, CREATE TABLE AS, or CREATE MATERIALIZED VIEW AS -ERROR@10550: expected command, found STRING -ERROR@10648: expected command, found STRING -ERROR@10653: expected SELECT, INSERT, UPDATE, DELETE, MERGE, VALUES, EXECUTE, DECLARE, CREATE TABLE AS, or CREATE MATERIALIZED VIEW AS -ERROR@10655: expected command, found STRING + diff --git a/crates/squawk_parser/tests/snapshots/tests__select_cte_ok.snap b/crates/squawk_parser/tests/snapshots/tests__select_cte_ok.snap index 74473581..801f33e7 100644 --- a/crates/squawk_parser/tests/snapshots/tests__select_cte_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__select_cte_ok.snap @@ -31,8 +31,9 @@ SOURCE_FILE USING_CLAUSE USING_KW "using" WHITESPACE " " - NAME_REF - IDENT "u" + FROM_ITEM + NAME_REF + IDENT "u" WHITESPACE " " ON_KW "on" WHITESPACE " " diff --git a/crates/squawk_parser/tests/snapshots/tests__select_ok.snap b/crates/squawk_parser/tests/snapshots/tests__select_ok.snap index 459e3710..a252f072 100644 --- a/crates/squawk_parser/tests/snapshots/tests__select_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__select_ok.snap @@ -4400,12 +4400,14 @@ SOURCE_FILE FROM_CLAUSE FROM_KW "from" WHITESPACE " " - FROM_ITEM - NAME_REF - IDENT "t" + JOIN_EXPR + FROM_ITEM + NAME_REF + IDENT "t" WHITESPACE " " JOIN - JOIN_KW "join" + JOIN_INNER + JOIN_KW "join" WHITESPACE " " FROM_ITEM NAME_REF @@ -4417,24 +4419,25 @@ SOURCE_FILE NAME IDENT "tb" WHITESPACE " " - ON_KW "on" - WHITESPACE " " - BIN_EXPR - FIELD_EXPR - NAME_REF - IDENT "tb" - DOT "." - NAME_REF - IDENT "id" - WHITESPACE " " - EQ "=" + ON_CLAUSE + ON_KW "on" WHITESPACE " " - FIELD_EXPR - NAME_REF - IDENT "t" - DOT "." - NAME_REF - IDENT "id" + BIN_EXPR + FIELD_EXPR + NAME_REF + IDENT "tb" + DOT "." + NAME_REF + IDENT "id" + WHITESPACE " " + EQ "=" + WHITESPACE " " + FIELD_EXPR + NAME_REF + IDENT "t" + DOT "." + NAME_REF + IDENT "id" SEMICOLON ";" WHITESPACE "\n\n" COMMENT "-- left" @@ -4450,14 +4453,16 @@ SOURCE_FILE FROM_CLAUSE FROM_KW "from" WHITESPACE " " - FROM_ITEM - NAME_REF - IDENT "t" + JOIN_EXPR + FROM_ITEM + NAME_REF + IDENT "t" WHITESPACE " " JOIN - LEFT_KW "left" - WHITESPACE " " - JOIN_KW "join" + JOIN_LEFT + LEFT_KW "left" + WHITESPACE " " + JOIN_KW "join" WHITESPACE " " FROM_ITEM NAME_REF @@ -4485,14 +4490,16 @@ SOURCE_FILE FROM_CLAUSE FROM_KW "from" WHITESPACE " " - FROM_ITEM - NAME_REF - IDENT "t" + JOIN_EXPR + FROM_ITEM + NAME_REF + IDENT "t" WHITESPACE " " JOIN - LEFT_KW "left" - WHITESPACE " " - JOIN_KW "join" + JOIN_LEFT + LEFT_KW "left" + WHITESPACE " " + JOIN_KW "join" WHITESPACE " " FROM_ITEM NAME_REF @@ -4527,14 +4534,16 @@ SOURCE_FILE FROM_CLAUSE FROM_KW "from" WHITESPACE " " - FROM_ITEM - NAME_REF - IDENT "t" + JOIN_EXPR + FROM_ITEM + NAME_REF + IDENT "t" WHITESPACE " " JOIN - RIGHT_KW "right" - WHITESPACE " " - JOIN_KW "join" + JOIN_RIGHT + RIGHT_KW "right" + WHITESPACE " " + JOIN_KW "join" WHITESPACE " " FROM_ITEM NAME_REF @@ -4564,14 +4573,16 @@ SOURCE_FILE FROM_CLAUSE FROM_KW "from" WHITESPACE " " - FROM_ITEM - NAME_REF - IDENT "t" + JOIN_EXPR + FROM_ITEM + NAME_REF + IDENT "t" WHITESPACE " " JOIN - FULL_KW "full" - WHITESPACE " " - JOIN_KW "join" + JOIN_FULL + FULL_KW "full" + WHITESPACE " " + JOIN_KW "join" WHITESPACE " " FROM_ITEM NAME_REF @@ -4601,55 +4612,58 @@ SOURCE_FILE FROM_CLAUSE FROM_KW "from" WHITESPACE " " - FROM_ITEM - NAME_REF - IDENT "t" + JOIN_EXPR + FROM_ITEM + NAME_REF + IDENT "t" WHITESPACE " " JOIN - JOIN_KW "join" + JOIN_INNER + JOIN_KW "join" WHITESPACE " " FROM_ITEM NAME_REF IDENT "t2" WHITESPACE " " - ON_KW "on" - WHITESPACE " " - BIN_EXPR - BIN_EXPR - FIELD_EXPR - NAME_REF - IDENT "t2" - DOT "." - NAME_REF - IDENT "team_id" - WHITESPACE " " - EQ "=" - WHITESPACE " " - FIELD_EXPR - NAME_REF - IDENT "t" - DOT "." - NAME_REF - IDENT "team_id" - WHITESPACE " " - AND_KW "and" + ON_CLAUSE + ON_KW "on" WHITESPACE " " BIN_EXPR - FIELD_EXPR - NAME_REF - IDENT "t2" - DOT "." - NAME_REF - IDENT "id" + BIN_EXPR + FIELD_EXPR + NAME_REF + IDENT "t2" + DOT "." + NAME_REF + IDENT "team_id" + WHITESPACE " " + EQ "=" + WHITESPACE " " + FIELD_EXPR + NAME_REF + IDENT "t" + DOT "." + NAME_REF + IDENT "team_id" WHITESPACE " " - EQ "=" + AND_KW "and" WHITESPACE " " - FIELD_EXPR - NAME_REF - IDENT "t" - DOT "." - NAME_REF - IDENT "org_id" + BIN_EXPR + FIELD_EXPR + NAME_REF + IDENT "t2" + DOT "." + NAME_REF + IDENT "id" + WHITESPACE " " + EQ "=" + WHITESPACE " " + FIELD_EXPR + NAME_REF + IDENT "t" + DOT "." + NAME_REF + IDENT "org_id" SEMICOLON ";" WHITESPACE "\n\n" COMMENT "-- nested joins" @@ -4676,40 +4690,44 @@ SOURCE_FILE FROM_CLAUSE FROM_KW "from" WHITESPACE "\n" - FROM_ITEM - NAME_REF - IDENT "t1" - WHITESPACE " " - ALIAS - NAME - IDENT "x" - COLUMN_LIST - L_PAREN "(" - COLUMN - NAME - IDENT "x0" - COMMA "," - COLUMN - NAME - IDENT "x1" - R_PAREN ")" + JOIN_EXPR + FROM_ITEM + NAME_REF + IDENT "t1" + WHITESPACE " " + ALIAS + NAME + IDENT "x" + COLUMN_LIST + L_PAREN "(" + COLUMN + NAME + IDENT "x0" + COMMA "," + COLUMN + NAME + IDENT "x1" + R_PAREN ")" WHITESPACE " " JOIN - LEFT_KW "left" - WHITESPACE " " - JOIN_KW "join" + JOIN_LEFT + LEFT_KW "left" + WHITESPACE " " + JOIN_KW "join" WHITESPACE " " FROM_ITEM PAREN_EXPR L_PAREN "(" - FROM_ITEM - NAME_REF - IDENT "t1" + JOIN_EXPR + FROM_ITEM + NAME_REF + IDENT "t1" WHITESPACE " " JOIN - LEFT_KW "left" - WHITESPACE " " - JOIN_KW "join" + JOIN_LEFT + LEFT_KW "left" + WHITESPACE " " + JOIN_KW "join" WHITESPACE " " FROM_ITEM NAME_REF @@ -4725,19 +4743,20 @@ SOURCE_FILE R_PAREN ")" R_PAREN ")" WHITESPACE " " - ON_KW "on" - WHITESPACE " " - PAREN_EXPR - L_PAREN "(" - BIN_EXPR - NAME_REF - IDENT "x0" - WHITESPACE " " - EQ "=" - WHITESPACE " " - LITERAL - INT_NUMBER "0" - R_PAREN ")" + ON_CLAUSE + ON_KW "on" + WHITESPACE " " + PAREN_EXPR + L_PAREN "(" + BIN_EXPR + NAME_REF + IDENT "x0" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + INT_NUMBER "0" + R_PAREN ")" WHITESPACE "\n" GROUP_BY_CLAUSE GROUP_KW "group" @@ -4762,12 +4781,14 @@ SOURCE_FILE FROM_CLAUSE FROM_KW "from" WHITESPACE " " - FROM_ITEM - NAME_REF - IDENT "t" + JOIN_EXPR + FROM_ITEM + NAME_REF + IDENT "t" WHITESPACE " " JOIN - JOIN_KW "join" + JOIN_INNER + JOIN_KW "join" WHITESPACE " " FROM_ITEM NAME_REF @@ -4782,12 +4803,12 @@ SOURCE_FILE NAME_REF IDENT "id" R_PAREN ")" - WHITESPACE " " - ALIAS - AS_KW "as" WHITESPACE " " - NAME - IDENT "foo" + ALIAS + AS_KW "as" + WHITESPACE " " + NAME + IDENT "foo" SEMICOLON ";" WHITESPACE "\n\n" COMMENT "-- cross join" @@ -4817,18 +4838,20 @@ SOURCE_FILE FROM_CLAUSE FROM_KW "FROM" WHITESPACE " " - FROM_ITEM - NAME_REF - IDENT "colors" - WHITESPACE " " - ALIAS - NAME - IDENT "c" + JOIN_EXPR + FROM_ITEM + NAME_REF + IDENT "colors" + WHITESPACE " " + ALIAS + NAME + IDENT "c" WHITESPACE " " JOIN - CROSS_KW "CROSS" - WHITESPACE " " - JOIN_KW "JOIN" + JOIN_CROSS + CROSS_KW "CROSS" + WHITESPACE " " + JOIN_KW "JOIN" WHITESPACE " " FROM_ITEM NAME_REF @@ -4866,18 +4889,20 @@ SOURCE_FILE FROM_CLAUSE FROM_KW "from" WHITESPACE " " - FROM_ITEM - NAME_REF - IDENT "fruits" - WHITESPACE " " - ALIAS - NAME - IDENT "f" + JOIN_EXPR + FROM_ITEM + NAME_REF + IDENT "fruits" + WHITESPACE " " + ALIAS + NAME + IDENT "f" WHITESPACE " " JOIN - INNER_KW "inner" - WHITESPACE " " - JOIN_KW "join" + JOIN_INNER + INNER_KW "inner" + WHITESPACE " " + JOIN_KW "join" WHITESPACE " " FROM_ITEM NAME_REF @@ -4887,13 +4912,14 @@ SOURCE_FILE NAME IDENT "c" WHITESPACE " " - ON_KW "on" - WHITESPACE " " - PAREN_EXPR - L_PAREN "(" - LITERAL - TRUE_KW "true" - R_PAREN ")" + ON_CLAUSE + ON_KW "on" + WHITESPACE " " + PAREN_EXPR + L_PAREN "(" + LITERAL + TRUE_KW "true" + R_PAREN ")" SEMICOLON ";" WHITESPACE "\n\n" COMMENT "-- multiple join clauses" @@ -4910,33 +4936,37 @@ SOURCE_FILE FROM_CLAUSE FROM_KW "from" WHITESPACE " " - FROM_ITEM - NAME_REF - IDENT "t" - WHITESPACE "\n" - JOIN - LEFT_KW "left" - WHITESPACE " " - JOIN_KW "join" - WHITESPACE " " + JOIN_EXPR + JOIN_EXPR FROM_ITEM NAME_REF - IDENT "u" - WHITESPACE " " - USING_CLAUSE - USING_KW "using" + IDENT "t" + WHITESPACE "\n" + JOIN + JOIN_LEFT + LEFT_KW "left" + WHITESPACE " " + JOIN_KW "join" WHITESPACE " " - COLUMN_LIST - L_PAREN "(" - COLUMN - NAME_REF - IDENT "id" - R_PAREN ")" + FROM_ITEM + NAME_REF + IDENT "u" + WHITESPACE " " + USING_CLAUSE + USING_KW "using" + WHITESPACE " " + COLUMN_LIST + L_PAREN "(" + COLUMN + NAME_REF + IDENT "id" + R_PAREN ")" WHITESPACE "\n" JOIN - LEFT_KW "left" - WHITESPACE " " - JOIN_KW "join" + JOIN_LEFT + LEFT_KW "left" + WHITESPACE " " + JOIN_KW "join" WHITESPACE " " FROM_ITEM NAME_REF @@ -4966,14 +4996,16 @@ SOURCE_FILE FROM_CLAUSE FROM_KW "FROM" WHITESPACE " " - FROM_ITEM - NAME_REF - IDENT "employees" + JOIN_EXPR + FROM_ITEM + NAME_REF + IDENT "employees" WHITESPACE " " JOIN NATURAL_KW "NATURAL" WHITESPACE " " - JOIN_KW "JOIN" + JOIN_INNER + JOIN_KW "JOIN" WHITESPACE " " FROM_ITEM NAME_REF @@ -5036,16 +5068,18 @@ SOURCE_FILE FROM_CLAUSE FROM_KW "FROM" WHITESPACE " " - FROM_ITEM - NAME_REF - IDENT "distributors" - WHITESPACE " " - ALIAS - NAME - IDENT "d" + JOIN_EXPR + FROM_ITEM + NAME_REF + IDENT "distributors" + WHITESPACE " " + ALIAS + NAME + IDENT "d" WHITESPACE " " JOIN - JOIN_KW "JOIN" + JOIN_INNER + JOIN_KW "JOIN" WHITESPACE " " FROM_ITEM NAME_REF @@ -5077,12 +5111,14 @@ SOURCE_FILE FROM_CLAUSE FROM_KW "from" WHITESPACE " " - FROM_ITEM - NAME_REF - IDENT "t" + JOIN_EXPR + FROM_ITEM + NAME_REF + IDENT "t" WHITESPACE " " JOIN - JOIN_KW "join" + JOIN_INNER + JOIN_KW "join" WHITESPACE " " FROM_ITEM NAME_REF @@ -5127,18 +5163,20 @@ SOURCE_FILE FROM_CLAUSE FROM_KW "FROM" WHITESPACE " " - FROM_ITEM - NAME_REF - IDENT "manufacturers" - WHITESPACE " " - ALIAS - NAME - IDENT "m" + JOIN_EXPR + FROM_ITEM + NAME_REF + IDENT "manufacturers" + WHITESPACE " " + ALIAS + NAME + IDENT "m" WHITESPACE " " JOIN - LEFT_KW "LEFT" - WHITESPACE " " - JOIN_KW "JOIN" + JOIN_LEFT + LEFT_KW "LEFT" + WHITESPACE " " + JOIN_KW "JOIN" WHITESPACE " " FROM_ITEM LATERAL_KW "LATERAL" @@ -5160,10 +5198,11 @@ SOURCE_FILE NAME IDENT "pname" WHITESPACE " " - ON_KW "ON" - WHITESPACE " " - LITERAL - TRUE_KW "true" + ON_CLAUSE + ON_KW "ON" + WHITESPACE " " + LITERAL + TRUE_KW "true" SEMICOLON ";" WHITESPACE "\n\n" COMMENT "-- table" diff --git a/crates/squawk_syntax/src/ast/generated/nodes.rs b/crates/squawk_syntax/src/ast/generated/nodes.rs index 6ebc9a0c..e1bfce78 100644 --- a/crates/squawk_syntax/src/ast/generated/nodes.rs +++ b/crates/squawk_syntax/src/ast/generated/nodes.rs @@ -4577,6 +4577,10 @@ impl FromClause { support::children(&self.syntax) } #[inline] + pub fn join_exprs(&self) -> AstChildren { + support::children(&self.syntax) + } + #[inline] pub fn from_token(&self) -> Option { support::token(&self.syntax, SyntaxKind::FROM_KW) } @@ -4587,6 +4591,10 @@ pub struct FromItem { pub(crate) syntax: SyntaxNode, } impl FromItem { + #[inline] + pub fn name_ref(&self) -> Option { + support::child(&self.syntax) + } #[inline] pub fn only_token(&self) -> Option { support::token(&self.syntax, SyntaxKind::ONLY_KW) @@ -5362,14 +5370,82 @@ pub struct Join { pub(crate) syntax: SyntaxNode, } impl Join { + #[inline] + pub fn join_type(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn on_clause(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn using_clause(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn natural_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::NATURAL_KW) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct JoinCross { + pub(crate) syntax: SyntaxNode, +} +impl JoinCross { #[inline] pub fn cross_token(&self) -> Option { support::token(&self.syntax, SyntaxKind::CROSS_KW) } + #[inline] + pub fn join_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::JOIN_KW) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct JoinExpr { + pub(crate) syntax: SyntaxNode, +} +impl JoinExpr { + #[inline] + pub fn from_item(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn join(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn join_expr(&self) -> Option { + support::child(&self.syntax) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct JoinFull { + pub(crate) syntax: SyntaxNode, +} +impl JoinFull { #[inline] pub fn full_token(&self) -> Option { support::token(&self.syntax, SyntaxKind::FULL_KW) } + #[inline] + pub fn join_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::JOIN_KW) + } + #[inline] + pub fn outer_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::OUTER_KW) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct JoinInner { + pub(crate) syntax: SyntaxNode, +} +impl JoinInner { #[inline] pub fn inner_token(&self) -> Option { support::token(&self.syntax, SyntaxKind::INNER_KW) @@ -5378,18 +5454,44 @@ impl Join { pub fn join_token(&self) -> Option { support::token(&self.syntax, SyntaxKind::JOIN_KW) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct JoinLeft { + pub(crate) syntax: SyntaxNode, +} +impl JoinLeft { + #[inline] + pub fn join_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::JOIN_KW) + } #[inline] pub fn left_token(&self) -> Option { support::token(&self.syntax, SyntaxKind::LEFT_KW) } #[inline] - pub fn natural_token(&self) -> Option { - support::token(&self.syntax, SyntaxKind::NATURAL_KW) + pub fn outer_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::OUTER_KW) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct JoinRight { + pub(crate) syntax: SyntaxNode, +} +impl JoinRight { + #[inline] + pub fn join_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::JOIN_KW) } #[inline] pub fn outer_token(&self) -> Option { support::token(&self.syntax, SyntaxKind::OUTER_KW) } + #[inline] + pub fn right_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::RIGHT_KW) + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -6171,6 +6273,21 @@ impl OffsetClause { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct OnClause { + pub(crate) syntax: SyntaxNode, +} +impl OnClause { + #[inline] + pub fn expr(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn on_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::ON_KW) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct OnDeleteAction { pub(crate) syntax: SyntaxNode, @@ -8810,6 +8927,15 @@ pub enum FuncOption { WindowFuncOption(WindowFuncOption), } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum JoinType { + JoinCross(JoinCross), + JoinFull(JoinFull), + JoinInner(JoinInner), + JoinLeft(JoinLeft), + JoinRight(JoinRight), +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum MatchType { MatchFull(MatchFull), @@ -13588,6 +13714,114 @@ impl AstNode for Join { &self.syntax } } +impl AstNode for JoinCross { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::JOIN_CROSS + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +impl AstNode for JoinExpr { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::JOIN_EXPR + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +impl AstNode for JoinFull { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::JOIN_FULL + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +impl AstNode for JoinInner { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::JOIN_INNER + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +impl AstNode for JoinLeft { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::JOIN_LEFT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +impl AstNode for JoinRight { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::JOIN_RIGHT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} impl AstNode for JsonBehaviorClause { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -14452,6 +14686,24 @@ impl AstNode for OffsetClause { &self.syntax } } +impl AstNode for OnClause { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::ON_CLAUSE + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} impl AstNode for OnDeleteAction { #[inline] fn can_cast(kind: SyntaxKind) -> bool { @@ -17961,6 +18213,73 @@ impl From for FuncOption { FuncOption::WindowFuncOption(node) } } +impl AstNode for JoinType { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + matches!( + kind, + SyntaxKind::JOIN_CROSS + | SyntaxKind::JOIN_FULL + | SyntaxKind::JOIN_INNER + | SyntaxKind::JOIN_LEFT + | SyntaxKind::JOIN_RIGHT + ) + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + SyntaxKind::JOIN_CROSS => JoinType::JoinCross(JoinCross { syntax }), + SyntaxKind::JOIN_FULL => JoinType::JoinFull(JoinFull { syntax }), + SyntaxKind::JOIN_INNER => JoinType::JoinInner(JoinInner { syntax }), + SyntaxKind::JOIN_LEFT => JoinType::JoinLeft(JoinLeft { syntax }), + SyntaxKind::JOIN_RIGHT => JoinType::JoinRight(JoinRight { syntax }), + _ => { + return None; + } + }; + Some(res) + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + match self { + JoinType::JoinCross(it) => &it.syntax, + JoinType::JoinFull(it) => &it.syntax, + JoinType::JoinInner(it) => &it.syntax, + JoinType::JoinLeft(it) => &it.syntax, + JoinType::JoinRight(it) => &it.syntax, + } + } +} +impl From for JoinType { + #[inline] + fn from(node: JoinCross) -> JoinType { + JoinType::JoinCross(node) + } +} +impl From for JoinType { + #[inline] + fn from(node: JoinFull) -> JoinType { + JoinType::JoinFull(node) + } +} +impl From for JoinType { + #[inline] + fn from(node: JoinInner) -> JoinType { + JoinType::JoinInner(node) + } +} +impl From for JoinType { + #[inline] + fn from(node: JoinLeft) -> JoinType { + JoinType::JoinLeft(node) + } +} +impl From for JoinType { + #[inline] + fn from(node: JoinRight) -> JoinType { + JoinType::JoinRight(node) + } +} impl AstNode for MatchType { #[inline] fn can_cast(kind: SyntaxKind) -> bool { diff --git a/crates/squawk_syntax/src/postgresql.ungram b/crates/squawk_syntax/src/postgresql.ungram index 97cb6d77..d0446aba 100644 --- a/crates/squawk_syntax/src/postgresql.ungram +++ b/crates/squawk_syntax/src/postgresql.ungram @@ -560,13 +560,36 @@ ConstraintStorageParams = ConstraintIndexTablespace = 'using' 'index' 'tablespace' NameRef +JoinInner = + 'inner'? 'join' + +JoinLeft = + 'left' 'outer'? 'join' + +JoinRight = + 'right' 'outer'? 'join' + +JoinFull = + 'full' 'outer'? 'join' + +JoinCross = + 'cross' 'join' + +JoinType = + JoinInner +| JoinLeft +| JoinRight +| JoinFull +| JoinCross + +OnClause = + 'on' Expr + Join = - 'natural' -| 'cross' 'join' -| 'inner'? 'join' -| 'left' 'outer'? 'join' -| 'full' 'outer'? 'join' + 'natural'? JoinType (UsingClause | OnClause)? +JoinExpr = + (FromItem | JoinExpr) Join // TODO: do we even want this in the AST? ParenSelect = @@ -927,11 +950,12 @@ OrderByClause = 'order' 'by' FromItem = - 'only'? + 'only'? NameRef FromClause = 'from' - (FromItem (',' FromItem)*) + (FromItem (',' FromItem)*)? + (JoinExpr (',' JoinExpr)*)? ConstraintIndexMethod = 'using' diff --git a/crates/squawk_syntax/src/snapshots/squawk_syntax__test__join_clauses_validation.snap b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__join_clauses_validation.snap new file mode 100644 index 00000000..0a32a84a --- /dev/null +++ b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__join_clauses_validation.snap @@ -0,0 +1,537 @@ +--- +source: crates/squawk_syntax/src/test.rs +input_file: crates/squawk_syntax/test_data/validation/join_clauses.sql +--- +SOURCE_FILE@0..653 + COMMENT@0..7 "-- errs" + WHITESPACE@7..8 "\n" + SELECT@8..30 + SELECT_CLAUSE@8..16 + SELECT_KW@8..14 "select" + WHITESPACE@14..15 " " + TARGET_LIST@15..16 + TARGET@15..16 + STAR@15..16 "*" + WHITESPACE@16..17 " " + FROM_CLAUSE@17..30 + FROM_KW@17..21 "from" + WHITESPACE@21..22 " " + JOIN_EXPR@22..30 + FROM_ITEM@22..23 + NAME_REF@22..23 + IDENT@22..23 "t" + WHITESPACE@23..24 " " + JOIN@24..30 + JOIN_INNER@24..28 + JOIN_KW@24..28 "join" + WHITESPACE@28..29 " " + FROM_ITEM@29..30 + NAME_REF@29..30 + IDENT@29..30 "u" + SEMICOLON@30..31 ";" + WHITESPACE@31..33 "\n\n" + SELECT@33..60 + SELECT_CLAUSE@33..41 + SELECT_KW@33..39 "select" + WHITESPACE@39..40 " " + TARGET_LIST@40..41 + TARGET@40..41 + STAR@40..41 "*" + WHITESPACE@41..42 " " + FROM_CLAUSE@42..60 + FROM_KW@42..46 "from" + WHITESPACE@46..47 " " + JOIN_EXPR@47..60 + FROM_ITEM@47..48 + NAME_REF@47..48 + IDENT@47..48 "t" + WHITESPACE@48..49 " " + JOIN@49..60 + JOIN_LEFT@49..58 + LEFT_KW@49..53 "left" + WHITESPACE@53..54 " " + JOIN_KW@54..58 "join" + WHITESPACE@58..59 " " + FROM_ITEM@59..60 + NAME_REF@59..60 + IDENT@59..60 "u" + SEMICOLON@60..61 ";" + WHITESPACE@61..63 "\n\n" + SELECT@63..91 + SELECT_CLAUSE@63..71 + SELECT_KW@63..69 "select" + WHITESPACE@69..70 " " + TARGET_LIST@70..71 + TARGET@70..71 + STAR@70..71 "*" + WHITESPACE@71..72 " " + FROM_CLAUSE@72..91 + FROM_KW@72..76 "from" + WHITESPACE@76..77 " " + JOIN_EXPR@77..91 + FROM_ITEM@77..78 + NAME_REF@77..78 + IDENT@77..78 "t" + WHITESPACE@78..79 " " + JOIN@79..91 + JOIN_RIGHT@79..89 + RIGHT_KW@79..84 "right" + WHITESPACE@84..85 " " + JOIN_KW@85..89 "join" + WHITESPACE@89..90 " " + FROM_ITEM@90..91 + NAME_REF@90..91 + IDENT@90..91 "u" + SEMICOLON@91..92 ";" + WHITESPACE@92..94 "\n\n" + SELECT@94..121 + SELECT_CLAUSE@94..102 + SELECT_KW@94..100 "select" + WHITESPACE@100..101 " " + TARGET_LIST@101..102 + TARGET@101..102 + STAR@101..102 "*" + WHITESPACE@102..103 " " + FROM_CLAUSE@103..121 + FROM_KW@103..107 "from" + WHITESPACE@107..108 " " + JOIN_EXPR@108..121 + FROM_ITEM@108..109 + NAME_REF@108..109 + IDENT@108..109 "t" + WHITESPACE@109..110 " " + JOIN@110..121 + JOIN_FULL@110..119 + FULL_KW@110..114 "full" + WHITESPACE@114..115 " " + JOIN_KW@115..119 "join" + WHITESPACE@119..120 " " + FROM_ITEM@120..121 + NAME_REF@120..121 + IDENT@120..121 "u" + SEMICOLON@121..122 ";" + WHITESPACE@122..125 "\n\n\n" + COMMENT@125..171 "-- err, can't use con ..." + WHITESPACE@171..172 "\n" + SELECT@172..213 + SELECT_CLAUSE@172..180 + SELECT_KW@172..178 "select" + WHITESPACE@178..179 " " + TARGET_LIST@179..180 + TARGET@179..180 + STAR@179..180 "*" + WHITESPACE@180..181 " " + FROM_CLAUSE@181..213 + FROM_KW@181..185 "from" + WHITESPACE@185..186 " " + JOIN_EXPR@186..213 + FROM_ITEM@186..187 + NAME_REF@186..187 + IDENT@186..187 "t" + WHITESPACE@187..188 " " + JOIN@188..213 + NATURAL_KW@188..195 "natural" + WHITESPACE@195..196 " " + JOIN_INNER@196..200 + JOIN_KW@196..200 "join" + WHITESPACE@200..201 " " + FROM_ITEM@201..202 + NAME_REF@201..202 + IDENT@201..202 "u" + WHITESPACE@202..203 " " + USING_CLAUSE@203..213 + USING_KW@203..208 "using" + WHITESPACE@208..209 " " + COLUMN_LIST@209..213 + L_PAREN@209..210 "(" + COLUMN@210..212 + NAME_REF@210..212 + IDENT@210..212 "id" + R_PAREN@212..213 ")" + SEMICOLON@213..214 ";" + WHITESPACE@214..215 "\n" + SELECT@215..260 + SELECT_CLAUSE@215..223 + SELECT_KW@215..221 "select" + WHITESPACE@221..222 " " + TARGET_LIST@222..223 + TARGET@222..223 + STAR@222..223 "*" + WHITESPACE@223..224 " " + FROM_CLAUSE@224..260 + FROM_KW@224..228 "from" + WHITESPACE@228..229 " " + JOIN_EXPR@229..260 + FROM_ITEM@229..230 + NAME_REF@229..230 + IDENT@229..230 "t" + WHITESPACE@230..231 " " + JOIN@231..260 + NATURAL_KW@231..238 "natural" + WHITESPACE@238..239 " " + JOIN_INNER@239..243 + JOIN_KW@239..243 "join" + WHITESPACE@243..244 " " + FROM_ITEM@244..245 + NAME_REF@244..245 + IDENT@244..245 "u" + WHITESPACE@245..246 " " + ON_CLAUSE@246..260 + ON_KW@246..248 "on" + WHITESPACE@248..249 " " + BIN_EXPR@249..260 + FIELD_EXPR@249..253 + NAME_REF@249..250 + IDENT@249..250 "u" + DOT@250..251 "." + NAME_REF@251..253 + IDENT@251..253 "id" + WHITESPACE@253..254 " " + EQ@254..255 "=" + WHITESPACE@255..256 " " + FIELD_EXPR@256..260 + NAME_REF@256..257 + IDENT@256..257 "t" + DOT@257..258 "." + NAME_REF@258..260 + IDENT@258..260 "id" + SEMICOLON@260..261 ";" + WHITESPACE@261..263 "\n\n" + COMMENT@263..308 "-- err, can't use con ..." + WHITESPACE@308..309 "\n" + SELECT@309..348 + SELECT_CLAUSE@309..317 + SELECT_KW@309..315 "select" + WHITESPACE@315..316 " " + TARGET_LIST@316..317 + TARGET@316..317 + STAR@316..317 "*" + WHITESPACE@317..318 " " + FROM_CLAUSE@318..348 + FROM_KW@318..322 "from" + WHITESPACE@322..323 " " + JOIN_EXPR@323..348 + FROM_ITEM@323..324 + NAME_REF@323..324 + IDENT@323..324 "t" + WHITESPACE@324..325 " " + JOIN@325..348 + JOIN_CROSS@325..335 + CROSS_KW@325..330 "cross" + WHITESPACE@330..331 " " + JOIN_KW@331..335 "join" + WHITESPACE@335..336 " " + FROM_ITEM@336..337 + NAME_REF@336..337 + IDENT@336..337 "u" + WHITESPACE@337..338 " " + USING_CLAUSE@338..348 + USING_KW@338..343 "using" + WHITESPACE@343..344 " " + COLUMN_LIST@344..348 + L_PAREN@344..345 "(" + COLUMN@345..347 + NAME_REF@345..347 + IDENT@345..347 "id" + R_PAREN@347..348 ")" + SEMICOLON@348..349 ";" + WHITESPACE@349..350 "\n" + SELECT@350..393 + SELECT_CLAUSE@350..358 + SELECT_KW@350..356 "select" + WHITESPACE@356..357 " " + TARGET_LIST@357..358 + TARGET@357..358 + STAR@357..358 "*" + WHITESPACE@358..359 " " + FROM_CLAUSE@359..393 + FROM_KW@359..363 "from" + WHITESPACE@363..364 " " + JOIN_EXPR@364..393 + FROM_ITEM@364..365 + NAME_REF@364..365 + IDENT@364..365 "t" + WHITESPACE@365..366 " " + JOIN@366..393 + JOIN_CROSS@366..376 + CROSS_KW@366..371 "cross" + WHITESPACE@371..372 " " + JOIN_KW@372..376 "join" + WHITESPACE@376..377 " " + FROM_ITEM@377..378 + NAME_REF@377..378 + IDENT@377..378 "u" + WHITESPACE@378..379 " " + ON_CLAUSE@379..393 + ON_KW@379..381 "on" + WHITESPACE@381..382 " " + BIN_EXPR@382..393 + FIELD_EXPR@382..386 + NAME_REF@382..383 + IDENT@382..383 "u" + DOT@383..384 "." + NAME_REF@384..386 + IDENT@384..386 "id" + WHITESPACE@386..387 " " + EQ@387..388 "=" + WHITESPACE@388..389 " " + FIELD_EXPR@389..393 + NAME_REF@389..390 + IDENT@389..390 "t" + DOT@390..391 "." + NAME_REF@391..393 + IDENT@391..393 "id" + SEMICOLON@393..394 ";" + WHITESPACE@394..395 "\n" + SELECT@395..431 + SELECT_CLAUSE@395..403 + SELECT_KW@395..401 "select" + WHITESPACE@401..402 " " + TARGET_LIST@402..403 + TARGET@402..403 + STAR@402..403 "*" + WHITESPACE@403..404 " " + FROM_CLAUSE@404..431 + FROM_KW@404..408 "from" + WHITESPACE@408..409 " " + JOIN_EXPR@409..431 + FROM_ITEM@409..410 + NAME_REF@409..410 + IDENT@409..410 "t" + WHITESPACE@410..411 " " + JOIN@411..431 + JOIN_CROSS@411..421 + CROSS_KW@411..416 "cross" + WHITESPACE@416..417 " " + JOIN_KW@417..421 "join" + WHITESPACE@421..422 " " + FROM_ITEM@422..423 + NAME_REF@422..423 + IDENT@422..423 "u" + WHITESPACE@423..424 " " + ON_CLAUSE@424..431 + ON_KW@424..426 "on" + WHITESPACE@426..427 " " + LITERAL@427..431 + TRUE_KW@427..431 "true" + SEMICOLON@431..432 ";" + WHITESPACE@432..434 "\n\n" + COMMENT@434..439 "-- ok" + WHITESPACE@439..440 "\n" + SELECT@440..470 + SELECT_CLAUSE@440..448 + SELECT_KW@440..446 "select" + WHITESPACE@446..447 " " + TARGET_LIST@447..448 + TARGET@447..448 + STAR@447..448 "*" + WHITESPACE@448..449 " " + FROM_CLAUSE@449..470 + FROM_KW@449..453 "from" + WHITESPACE@453..454 " " + JOIN_EXPR@454..470 + FROM_ITEM@454..455 + NAME_REF@454..455 + IDENT@454..455 "t" + WHITESPACE@455..456 " " + JOIN@456..470 + NATURAL_KW@456..463 "natural" + WHITESPACE@463..464 " " + JOIN_INNER@464..468 + JOIN_KW@464..468 "join" + WHITESPACE@468..469 " " + FROM_ITEM@469..470 + NAME_REF@469..470 + IDENT@469..470 "u" + SEMICOLON@470..471 ";" + WHITESPACE@471..473 "\n\n" + SELECT@473..521 + SELECT_CLAUSE@473..481 + SELECT_KW@473..479 "select" + WHITESPACE@479..480 " " + TARGET_LIST@480..481 + TARGET@480..481 + STAR@480..481 "*" + WHITESPACE@481..482 " " + FROM_CLAUSE@482..521 + FROM_KW@482..486 "from" + WHITESPACE@486..487 " " + JOIN_EXPR@487..501 + FROM_ITEM@487..488 + NAME_REF@487..488 + IDENT@487..488 "t" + WHITESPACE@488..489 " " + JOIN@489..501 + JOIN_CROSS@489..499 + CROSS_KW@489..494 "cross" + WHITESPACE@494..495 " " + JOIN_KW@495..499 "join" + WHITESPACE@499..500 " " + FROM_ITEM@500..501 + NAME_REF@500..501 + IDENT@500..501 "u" + COMMA@501..502 "," + WHITESPACE@502..503 " " + JOIN_EXPR@503..521 + FROM_ITEM@503..504 + NAME_REF@503..504 + IDENT@503..504 "b" + WHITESPACE@504..505 " " + JOIN@505..521 + JOIN_INNER@505..509 + JOIN_KW@505..509 "join" + WHITESPACE@509..510 " " + FROM_ITEM@510..511 + NAME_REF@510..511 + IDENT@510..511 "c" + WHITESPACE@511..512 " " + USING_CLAUSE@512..521 + USING_KW@512..517 "using" + COLUMN_LIST@517..521 + L_PAREN@517..518 "(" + COLUMN@518..520 + NAME_REF@518..520 + IDENT@518..520 "id" + R_PAREN@520..521 ")" + SEMICOLON@521..522 ";" + WHITESPACE@522..524 "\n\n" + SELECT@524..561 + SELECT_CLAUSE@524..532 + SELECT_KW@524..530 "select" + WHITESPACE@530..531 " " + TARGET_LIST@531..532 + TARGET@531..532 + STAR@531..532 "*" + WHITESPACE@532..533 " " + FROM_CLAUSE@533..561 + FROM_KW@533..537 "from" + WHITESPACE@537..538 " " + JOIN_EXPR@538..561 + FROM_ITEM@538..539 + NAME_REF@538..539 + IDENT@538..539 "t" + WHITESPACE@539..540 " " + JOIN@540..561 + JOIN_INNER@540..544 + JOIN_KW@540..544 "join" + WHITESPACE@544..545 " " + FROM_ITEM@545..546 + NAME_REF@545..546 + IDENT@545..546 "u" + WHITESPACE@546..547 " " + ON_CLAUSE@547..561 + ON_KW@547..549 "on" + WHITESPACE@549..550 " " + BIN_EXPR@550..561 + FIELD_EXPR@550..554 + NAME_REF@550..551 + IDENT@550..551 "u" + DOT@551..552 "." + NAME_REF@552..554 + IDENT@552..554 "id" + WHITESPACE@554..555 " " + EQ@555..556 "=" + WHITESPACE@556..557 " " + FIELD_EXPR@557..561 + NAME_REF@557..558 + IDENT@557..558 "t" + DOT@558..559 "." + NAME_REF@559..561 + IDENT@559..561 "id" + SEMICOLON@561..562 ";" + WHITESPACE@562..563 "\n" + SELECT@563..593 + SELECT_CLAUSE@563..571 + SELECT_KW@563..569 "select" + WHITESPACE@569..570 " " + TARGET_LIST@570..571 + TARGET@570..571 + STAR@570..571 "*" + WHITESPACE@571..572 " " + FROM_CLAUSE@572..593 + FROM_KW@572..576 "from" + WHITESPACE@576..577 " " + JOIN_EXPR@577..593 + FROM_ITEM@577..578 + NAME_REF@577..578 + IDENT@577..578 "t" + WHITESPACE@578..579 " " + JOIN@579..593 + JOIN_INNER@579..583 + JOIN_KW@579..583 "join" + WHITESPACE@583..584 " " + FROM_ITEM@584..585 + NAME_REF@584..585 + IDENT@584..585 "u" + WHITESPACE@585..586 " " + ON_CLAUSE@586..593 + ON_KW@586..588 "on" + WHITESPACE@588..589 " " + LITERAL@589..593 + TRUE_KW@589..593 "true" + SEMICOLON@593..594 ";" + WHITESPACE@594..596 "\n\n" + SELECT@596..651 + SELECT_CLAUSE@596..604 + SELECT_KW@596..602 "select" + WHITESPACE@602..603 " " + TARGET_LIST@603..604 + TARGET@603..604 + STAR@603..604 "*" + WHITESPACE@604..605 " " + FROM_CLAUSE@605..651 + FROM_KW@605..609 "from" + WHITESPACE@609..610 " " + JOIN_EXPR@610..651 + JOIN_EXPR@610..631 + FROM_ITEM@610..611 + NAME_REF@610..611 + IDENT@610..611 "t" + WHITESPACE@611..614 "\n " + JOIN@614..631 + JOIN_INNER@614..618 + JOIN_KW@614..618 "join" + WHITESPACE@618..619 " " + FROM_ITEM@619..620 + NAME_REF@619..620 + IDENT@619..620 "u" + WHITESPACE@620..621 " " + USING_CLAUSE@621..631 + USING_KW@621..626 "using" + WHITESPACE@626..627 " " + COLUMN_LIST@627..631 + L_PAREN@627..628 "(" + COLUMN@628..630 + NAME_REF@628..630 + IDENT@628..630 "id" + R_PAREN@630..631 ")" + WHITESPACE@631..634 "\n " + JOIN@634..651 + JOIN_INNER@634..638 + JOIN_KW@634..638 "join" + WHITESPACE@638..639 " " + FROM_ITEM@639..640 + NAME_REF@639..640 + IDENT@639..640 "c" + WHITESPACE@640..641 " " + USING_CLAUSE@641..651 + USING_KW@641..646 "using" + WHITESPACE@646..647 " " + COLUMN_LIST@647..651 + L_PAREN@647..648 "(" + COLUMN@648..650 + NAME_REF@648..650 + IDENT@648..650 "id" + R_PAREN@650..651 ")" + SEMICOLON@651..652 ";" + WHITESPACE@652..653 "\n" + +ERROR@30 "Join missing condition." +ERROR@60 "Join missing condition." +ERROR@91 "Join missing condition." +ERROR@121 "Join missing condition." +ERROR@203:213 "Join `using` clause is not allowed for natural joins." +ERROR@246:260 "Join condition is not allowed for natural joins." +ERROR@338:348 "Join `using` clause is not allowed for cross joins." +ERROR@379:393 "Join condition is not allowed for cross joins." +ERROR@424:431 "Join condition is not allowed for cross joins." diff --git a/crates/squawk_syntax/src/validation.rs b/crates/squawk_syntax/src/validation.rs index df64c5ef..343cd786 100644 --- a/crates/squawk_syntax/src/validation.rs +++ b/crates/squawk_syntax/src/validation.rs @@ -17,6 +17,7 @@ pub(crate) fn validate(root: &SyntaxNode, errors: &mut Vec) { ast::PrefixExpr(it) => validate_prefix_expr(it, errors), ast::ArrayExpr(it) => validate_array_expr(it, errors), ast::DropAggregate(it) => validate_drop_aggregate(it, errors), + ast::JoinExpr(it) => validate_join_expr(it, errors), ast::Literal(it) => validate_literal(it, errors), _ => (), } @@ -80,6 +81,72 @@ fn validate_literal(lit: ast::Literal, acc: &mut Vec) { } } +fn validate_join_expr(join_expr: ast::JoinExpr, acc: &mut Vec) { + let Some(join) = join_expr.join() else { + return; + }; + + let Some(join_type) = join.join_type() else { + return; + }; + + enum JoinClause { + Required, + NotAllowed, + } + use JoinClause::*; + + let join_clause = if join.natural_token().is_some() { + NotAllowed + } else { + match join_type { + ast::JoinType::JoinCross(_) => NotAllowed, + ast::JoinType::JoinFull(_) + | ast::JoinType::JoinInner(_) + | ast::JoinType::JoinLeft(_) + | ast::JoinType::JoinRight(_) => Required, + } + }; + + let join_name = if join.natural_token().is_some() { + "natural" + } else { + match join_type { + ast::JoinType::JoinCross(_) => "cross", + ast::JoinType::JoinFull(_) => "full", + ast::JoinType::JoinInner(_) => "inner", + ast::JoinType::JoinLeft(_) => "left", + ast::JoinType::JoinRight(_) => "right", + } + }; + + match join_clause { + Required => { + if join.on_clause().is_none() && join.using_clause().is_none() { + let end = join_expr.syntax().text_range().end(); + acc.push(SyntaxError::new( + "Join missing condition.", + TextRange::new(end, end), + )); + } + } + NotAllowed => { + if let Some(on_clause) = join.on_clause() { + acc.push(SyntaxError::new( + format!("Join condition is not allowed for {join_name} joins."), + on_clause.syntax().text_range(), + )); + } + if let Some(using_clause) = join.using_clause() { + acc.push(SyntaxError::new( + format!("Join `using` clause is not allowed for {join_name} joins."), + using_clause.syntax().text_range(), + )); + } + } + } +} + fn validate_drop_aggregate(drop_agg: ast::DropAggregate, acc: &mut Vec) { for agg in drop_agg.aggregates() { validate_aggregate_params(agg.param_list(), acc); diff --git a/crates/squawk_syntax/test_data/validation/join_clauses.sql b/crates/squawk_syntax/test_data/validation/join_clauses.sql new file mode 100644 index 00000000..357e575a --- /dev/null +++ b/crates/squawk_syntax/test_data/validation/join_clauses.sql @@ -0,0 +1,30 @@ +-- errs +select * from t join u; + +select * from t left join u; + +select * from t right join u; + +select * from t full join u; + + +-- err, can't use conditions with natural join +select * from t natural join u using (id); +select * from t natural join u on u.id = t.id; + +-- err, can't use conditions with cross joins +select * from t cross join u using (id); +select * from t cross join u on u.id = t.id; +select * from t cross join u on true; + +-- ok +select * from t natural join u; + +select * from t cross join u, b join c using(id); + +select * from t join u on u.id = t.id; +select * from t join u on true; + +select * from t + join u using (id) + join c using (id);