diff --git a/crates/squawk_parser/src/grammar.rs b/crates/squawk_parser/src/grammar.rs index c6808c32..4603a7ad 100644 --- a/crates/squawk_parser/src/grammar.rs +++ b/crates/squawk_parser/src/grammar.rs @@ -877,11 +877,7 @@ fn exists_fn(p: &mut Parser<'_>) -> CompletedMarker { let m = p.start(); p.bump(EXISTS_KW); p.expect(L_PAREN); - if p.at_ts(SELECT_FIRST) { - select(p, None, &SelectRestrictions::default(), false); - } else { - p.error("expected select"); - } + query(p); p.expect(R_PAREN); let m = m.complete(p, EXISTS_FN).precede(p); opt_agg_clauses(p); @@ -10024,7 +10020,7 @@ fn create_role(p: &mut Parser<'_>) -> CompletedMarker { fn select_insert_delete_update_or_notify(p: &mut Parser<'_>, semi_allowed: bool) { // statement - // Any SELECT, INSERT, UPDATE, DELETE, MERGE, or VALUES statement. + // Any SELECT, INSERT, UPDATE, DELETE, NOTIFY, or VALUES statement. let statement = stmt( p, &StmtRestrictions { @@ -10034,7 +10030,8 @@ fn select_insert_delete_update_or_notify(p: &mut Parser<'_>, semi_allowed: bool) ); if let Some(statement) = statement { match statement.kind() { - SELECT | VALUES | INSERT | UPDATE | DELETE | NOTIFY => (), + SELECT | COMPOUND_SELECT | PAREN_SELECT | TABLE | VALUES | INSERT | UPDATE | DELETE + | NOTIFY => (), kind => { p.error(format!( "expected SELECT, INSERT, UPDATE, DELETE, NOTIFY, or VALUES statement, got {kind:?}" @@ -11148,7 +11145,7 @@ fn explain(p: &mut Parser<'_>) -> CompletedMarker { } fn opt_explain_option_list(p: &mut Parser<'_>) -> Option { - if !p.at(L_PAREN) || (p.at(L_PAREN) && p.nth_at_ts(1, SELECT_FIRST)) { + if !p.at(L_PAREN) || p.nth_at_ts(1, PAREN_SELECT_FIRST) { return None; } let m = p.start(); diff --git a/crates/squawk_parser/tests/data/ok/create_rule.sql b/crates/squawk_parser/tests/data/ok/create_rule.sql index a05c8bf6..fadad310 100644 --- a/crates/squawk_parser/tests/data/ok/create_rule.sql +++ b/crates/squawk_parser/tests/data/ok/create_rule.sql @@ -16,6 +16,16 @@ create or replace rule r as on select notify f; ); +-- select_stmt_variants +create rule r as on insert to t do instead ((select 1)); +create rule r as on insert to t do also ((select 1)); +create rule r as on insert to t do instead (notify foo; (select 1);); +create rule r as on insert to t do instead ((select 1) union (select 2);); +create rule r as on insert to t do instead select 1 union select 2; +create rule r as on insert to t do instead (select 1 union select 2;); +create rule r as on insert to t do instead table t; +create rule r as on insert to t do instead (table t;); + -- doc_1 CREATE RULE "_RETURN" AS ON SELECT TO t1 diff --git a/crates/squawk_parser/tests/data/ok/explain.sql b/crates/squawk_parser/tests/data/ok/explain.sql index e0719639..305ac9e2 100644 --- a/crates/squawk_parser/tests/data/ok/explain.sql +++ b/crates/squawk_parser/tests/data/ok/explain.sql @@ -74,9 +74,12 @@ EXPLAIN (GENERIC_PLAN) -- parens_select explain analyze (((((select 1))))); +explain ((select 1)); +explain ((select 1) union (select 2)); -- parens_values explain analyze (((((values (1)))))); +explain ((values (1))); -- boolean off EXPLAIN (COSTS OFF) SELECT * FROM foo WHERE i = 4; diff --git a/crates/squawk_parser/tests/data/ok/select_funcs.sql b/crates/squawk_parser/tests/data/ok/select_funcs.sql index 6fd03406..34c9ef96 100644 --- a/crates/squawk_parser/tests/data/ok/select_funcs.sql +++ b/crates/squawk_parser/tests/data/ok/select_funcs.sql @@ -144,6 +144,10 @@ select json_scalar(1); -- exists select exists(select 1 from t where a = b); +select exists((select 1)); +select exists(((select 1))); +select exists((select 1) union (select 2)); +select exists((table t)); select exists(with t as (select 1) select * from t); diff --git a/crates/squawk_parser/tests/snapshots/tests__create_rule_ok.snap b/crates/squawk_parser/tests/snapshots/tests__create_rule_ok.snap index 0e51b70b..5acc8459 100644 --- a/crates/squawk_parser/tests/snapshots/tests__create_rule_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__create_rule_ok.snap @@ -211,6 +211,384 @@ SOURCE_FILE R_PAREN ")" SEMICOLON ";" WHITESPACE "\n\n" + COMMENT "-- select_stmt_variants" + WHITESPACE "\n" + CREATE_RULE + CREATE_KW "create" + WHITESPACE " " + RULE_KW "rule" + WHITESPACE " " + NAME + IDENT "r" + WHITESPACE " " + AS_KW "as" + WHITESPACE " " + RULE_ON + ON_KW "on" + WHITESPACE " " + INSERT_KW "insert" + WHITESPACE " " + TO_KW "to" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME_REF + IDENT "t" + WHITESPACE " " + RULE_DO + DO_KW "do" + WHITESPACE " " + INSTEAD_KW "instead" + WHITESPACE " " + RULE_STMT_LIST + L_PAREN "(" + PAREN_SELECT + L_PAREN "(" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + LITERAL + INT_NUMBER "1" + R_PAREN ")" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + CREATE_RULE + CREATE_KW "create" + WHITESPACE " " + RULE_KW "rule" + WHITESPACE " " + NAME + IDENT "r" + WHITESPACE " " + AS_KW "as" + WHITESPACE " " + RULE_ON + ON_KW "on" + WHITESPACE " " + INSERT_KW "insert" + WHITESPACE " " + TO_KW "to" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME_REF + IDENT "t" + WHITESPACE " " + RULE_DO + DO_KW "do" + WHITESPACE " " + ALSO_KW "also" + WHITESPACE " " + RULE_STMT_LIST + L_PAREN "(" + PAREN_SELECT + L_PAREN "(" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + LITERAL + INT_NUMBER "1" + R_PAREN ")" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + CREATE_RULE + CREATE_KW "create" + WHITESPACE " " + RULE_KW "rule" + WHITESPACE " " + NAME + IDENT "r" + WHITESPACE " " + AS_KW "as" + WHITESPACE " " + RULE_ON + ON_KW "on" + WHITESPACE " " + INSERT_KW "insert" + WHITESPACE " " + TO_KW "to" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME_REF + IDENT "t" + WHITESPACE " " + RULE_DO + DO_KW "do" + WHITESPACE " " + INSTEAD_KW "instead" + WHITESPACE " " + RULE_STMT_LIST + L_PAREN "(" + NOTIFY + NOTIFY_KW "notify" + WHITESPACE " " + NAME_REF + IDENT "foo" + SEMICOLON ";" + WHITESPACE " " + PAREN_SELECT + L_PAREN "(" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + LITERAL + INT_NUMBER "1" + R_PAREN ")" + SEMICOLON ";" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + CREATE_RULE + CREATE_KW "create" + WHITESPACE " " + RULE_KW "rule" + WHITESPACE " " + NAME + IDENT "r" + WHITESPACE " " + AS_KW "as" + WHITESPACE " " + RULE_ON + ON_KW "on" + WHITESPACE " " + INSERT_KW "insert" + WHITESPACE " " + TO_KW "to" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME_REF + IDENT "t" + WHITESPACE " " + RULE_DO + DO_KW "do" + WHITESPACE " " + INSTEAD_KW "instead" + WHITESPACE " " + RULE_STMT_LIST + L_PAREN "(" + COMPOUND_SELECT + PAREN_SELECT + L_PAREN "(" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + LITERAL + INT_NUMBER "1" + R_PAREN ")" + WHITESPACE " " + UNION_KW "union" + WHITESPACE " " + PAREN_SELECT + L_PAREN "(" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + LITERAL + INT_NUMBER "2" + R_PAREN ")" + SEMICOLON ";" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + CREATE_RULE + CREATE_KW "create" + WHITESPACE " " + RULE_KW "rule" + WHITESPACE " " + NAME + IDENT "r" + WHITESPACE " " + AS_KW "as" + WHITESPACE " " + RULE_ON + ON_KW "on" + WHITESPACE " " + INSERT_KW "insert" + WHITESPACE " " + TO_KW "to" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME_REF + IDENT "t" + WHITESPACE " " + RULE_DO + DO_KW "do" + WHITESPACE " " + INSTEAD_KW "instead" + WHITESPACE " " + COMPOUND_SELECT + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + LITERAL + INT_NUMBER "1" + WHITESPACE " " + UNION_KW "union" + WHITESPACE " " + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + LITERAL + INT_NUMBER "2" + SEMICOLON ";" + WHITESPACE "\n" + CREATE_RULE + CREATE_KW "create" + WHITESPACE " " + RULE_KW "rule" + WHITESPACE " " + NAME + IDENT "r" + WHITESPACE " " + AS_KW "as" + WHITESPACE " " + RULE_ON + ON_KW "on" + WHITESPACE " " + INSERT_KW "insert" + WHITESPACE " " + TO_KW "to" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME_REF + IDENT "t" + WHITESPACE " " + RULE_DO + DO_KW "do" + WHITESPACE " " + INSTEAD_KW "instead" + WHITESPACE " " + RULE_STMT_LIST + L_PAREN "(" + COMPOUND_SELECT + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + LITERAL + INT_NUMBER "1" + WHITESPACE " " + UNION_KW "union" + WHITESPACE " " + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + LITERAL + INT_NUMBER "2" + SEMICOLON ";" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + CREATE_RULE + CREATE_KW "create" + WHITESPACE " " + RULE_KW "rule" + WHITESPACE " " + NAME + IDENT "r" + WHITESPACE " " + AS_KW "as" + WHITESPACE " " + RULE_ON + ON_KW "on" + WHITESPACE " " + INSERT_KW "insert" + WHITESPACE " " + TO_KW "to" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME_REF + IDENT "t" + WHITESPACE " " + RULE_DO + DO_KW "do" + WHITESPACE " " + INSTEAD_KW "instead" + WHITESPACE " " + TABLE + TABLE_KW "table" + WHITESPACE " " + RELATION_NAME + PATH + PATH_SEGMENT + NAME_REF + IDENT "t" + SEMICOLON ";" + WHITESPACE "\n" + CREATE_RULE + CREATE_KW "create" + WHITESPACE " " + RULE_KW "rule" + WHITESPACE " " + NAME + IDENT "r" + WHITESPACE " " + AS_KW "as" + WHITESPACE " " + RULE_ON + ON_KW "on" + WHITESPACE " " + INSERT_KW "insert" + WHITESPACE " " + TO_KW "to" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME_REF + IDENT "t" + WHITESPACE " " + RULE_DO + DO_KW "do" + WHITESPACE " " + INSTEAD_KW "instead" + WHITESPACE " " + RULE_STMT_LIST + L_PAREN "(" + TABLE + TABLE_KW "table" + WHITESPACE " " + RELATION_NAME + PATH + PATH_SEGMENT + NAME_REF + IDENT "t" + SEMICOLON ";" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n\n" COMMENT "-- doc_1" WHITESPACE "\n" CREATE_RULE diff --git a/crates/squawk_parser/tests/snapshots/tests__explain_ok.snap b/crates/squawk_parser/tests/snapshots/tests__explain_ok.snap index 03b4a2e6..e1e2c5fb 100644 --- a/crates/squawk_parser/tests/snapshots/tests__explain_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__explain_ok.snap @@ -761,6 +761,59 @@ SOURCE_FILE R_PAREN ")" R_PAREN ")" SEMICOLON ";" + WHITESPACE "\n" + EXPLAIN + EXPLAIN_KW "explain" + WHITESPACE " " + PAREN_SELECT + L_PAREN "(" + PAREN_SELECT + L_PAREN "(" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + LITERAL + INT_NUMBER "1" + R_PAREN ")" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + EXPLAIN + EXPLAIN_KW "explain" + WHITESPACE " " + PAREN_SELECT + L_PAREN "(" + COMPOUND_SELECT + PAREN_SELECT + L_PAREN "(" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + LITERAL + INT_NUMBER "1" + R_PAREN ")" + WHITESPACE " " + UNION_KW "union" + WHITESPACE " " + PAREN_SELECT + L_PAREN "(" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + LITERAL + INT_NUMBER "2" + R_PAREN ")" + R_PAREN ")" + SEMICOLON ";" WHITESPACE "\n\n" COMMENT "-- parens_values" WHITESPACE "\n" @@ -794,6 +847,26 @@ SOURCE_FILE R_PAREN ")" R_PAREN ")" SEMICOLON ";" + WHITESPACE "\n" + EXPLAIN + EXPLAIN_KW "explain" + WHITESPACE " " + PAREN_SELECT + L_PAREN "(" + PAREN_SELECT + L_PAREN "(" + VALUES + VALUES_KW "values" + WHITESPACE " " + ROW_LIST + ROW + L_PAREN "(" + LITERAL + INT_NUMBER "1" + R_PAREN ")" + R_PAREN ")" + R_PAREN ")" + SEMICOLON ";" WHITESPACE "\n\n" COMMENT "-- boolean off" WHITESPACE "\n" diff --git a/crates/squawk_parser/tests/snapshots/tests__select_funcs_ok.snap b/crates/squawk_parser/tests/snapshots/tests__select_funcs_ok.snap index dfaf7a6b..c1c3f5eb 100644 --- a/crates/squawk_parser/tests/snapshots/tests__select_funcs_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__select_funcs_ok.snap @@ -2480,6 +2480,120 @@ SOURCE_FILE IDENT "b" R_PAREN ")" SEMICOLON ";" + WHITESPACE "\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + CALL_EXPR + EXISTS_FN + EXISTS_KW "exists" + L_PAREN "(" + PAREN_SELECT + L_PAREN "(" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + LITERAL + INT_NUMBER "1" + R_PAREN ")" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + CALL_EXPR + EXISTS_FN + EXISTS_KW "exists" + L_PAREN "(" + PAREN_SELECT + L_PAREN "(" + PAREN_SELECT + L_PAREN "(" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + LITERAL + INT_NUMBER "1" + R_PAREN ")" + R_PAREN ")" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + CALL_EXPR + EXISTS_FN + EXISTS_KW "exists" + L_PAREN "(" + COMPOUND_SELECT + PAREN_SELECT + L_PAREN "(" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + LITERAL + INT_NUMBER "1" + R_PAREN ")" + WHITESPACE " " + UNION_KW "union" + WHITESPACE " " + PAREN_SELECT + L_PAREN "(" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + LITERAL + INT_NUMBER "2" + R_PAREN ")" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + CALL_EXPR + EXISTS_FN + EXISTS_KW "exists" + L_PAREN "(" + PAREN_SELECT + L_PAREN "(" + TABLE + TABLE_KW "table" + WHITESPACE " " + RELATION_NAME + PATH + PATH_SEGMENT + NAME_REF + IDENT "t" + R_PAREN ")" + R_PAREN ")" + SEMICOLON ";" WHITESPACE "\n\n" SELECT SELECT_CLAUSE