From a3acdfe0eb11efe70268fe0c04a1e38e5c907f6d Mon Sep 17 00:00:00 2001 From: Steve Dignam Date: Mon, 22 Jun 2026 23:03:41 -0400 Subject: [PATCH] parser: fix various panics with malformed SQL --- crates/squawk_parser/src/grammar.rs | 59 ++++---- .../tests/data/err/alter_extension.sql | 3 + .../tests/data/err/alter_index.sql | 2 + .../tests/data/err/create_domain.sql | 2 + .../tests/data/err/create_table.sql | 3 + .../tests/data/err/join_natural.sql | 4 + .../tests/data/err/json_object.sql | 4 + .../snapshots/tests__alter_extension_err.snap | 50 +++++++ .../snapshots/tests__alter_index_err.snap | 38 ++++++ .../snapshots/tests__create_domain_err.snap | 48 +++++++ .../snapshots/tests__create_table_err.snap | 61 ++++++++- .../snapshots/tests__join_natural_err.snap | 126 ++++++++++++++++++ .../snapshots/tests__json_object_err.snap | 93 +++++++++++++ 13 files changed, 456 insertions(+), 37 deletions(-) create mode 100644 crates/squawk_parser/tests/data/err/alter_extension.sql create mode 100644 crates/squawk_parser/tests/data/err/alter_index.sql create mode 100644 crates/squawk_parser/tests/data/err/create_domain.sql create mode 100644 crates/squawk_parser/tests/data/err/join_natural.sql create mode 100644 crates/squawk_parser/tests/data/err/json_object.sql create mode 100644 crates/squawk_parser/tests/snapshots/tests__alter_extension_err.snap create mode 100644 crates/squawk_parser/tests/snapshots/tests__alter_index_err.snap create mode 100644 crates/squawk_parser/tests/snapshots/tests__create_domain_err.snap create mode 100644 crates/squawk_parser/tests/snapshots/tests__join_natural_err.snap create mode 100644 crates/squawk_parser/tests/snapshots/tests__json_object_err.snap diff --git a/crates/squawk_parser/src/grammar.rs b/crates/squawk_parser/src/grammar.rs index d2b9b5420..b827cb054 100644 --- a/crates/squawk_parser/src/grammar.rs +++ b/crates/squawk_parser/src/grammar.rs @@ -550,7 +550,7 @@ fn json_object_fn_arg_list(p: &mut Parser<'_>) { p.bump(COMMA); continue; } else { - p.error("expected a comma"); + p.err_and_bump("expected a comma"); } } opt_json_null_clause(p); @@ -2064,7 +2064,7 @@ fn opt_interval_trailing(p: &mut Parser<'_>) { if opt_numeric_literal(p).is_none() { p.error("expected number") } - p.bump(R_PAREN); + p.expect(R_PAREN); } _ => (), } @@ -3031,7 +3031,6 @@ const JOIN_TYPE_FIRST: TokenSet = // RIGHT [ OUTER ] JOIN // FULL [ OUTER ] JOIN fn join_type(p: &mut Parser<'_>) -> Option { - assert!(p.at_ts(JOIN_TYPE_FIRST)); let m = p.start(); let kind = match p.current() { CROSS_KW => { @@ -4098,9 +4097,8 @@ fn opt_virtual_or_stored(p: &mut Parser<'_>) { fn opt_no_inherit(p: &mut Parser<'_>) { let m = p.start(); if p.eat(NO_KW) { - if p.eat(INHERIT_KW) { - m.complete(p, NO_INHERIT); - } + p.expect(INHERIT_KW); + m.complete(p, NO_INHERIT); } else { m.abandon(p); } @@ -5895,7 +5893,7 @@ fn stmt(p: &mut Parser, r: &StmtRestrictions) -> Option { PARSER_KW => Some(alter_text_search_parser(p)), TEMPLATE_KW => Some(alter_text_search_template(p)), _ => { - p.error("expected TEMPLATE, CONFIGURATION, DICTIONARY, PARSER, or TEMPLATE"); + p.err_and_bump("expected TEMPLATE, CONFIGURATION, DICTIONARY, PARSER, or TEMPLATE"); None } }, @@ -5991,7 +5989,7 @@ fn stmt(p: &mut Parser, r: &StmtRestrictions) -> Option { PARSER_KW => Some(create_text_search_parser(p)), TEMPLATE_KW => Some(create_text_search_template(p)), _ => { - p.error("expected TEMPLATE, CONFIGURATION, DICTIONARY, PARSER, or TEMPLATE"); + p.err_and_bump("expected TEMPLATE, CONFIGURATION, DICTIONARY, PARSER, or TEMPLATE"); None } }, @@ -6019,7 +6017,7 @@ fn stmt(p: &mut Parser, r: &StmtRestrictions) -> Option { DATA_KW => Some(drop_foreign_data(p)), TABLE_KW => Some(drop_foreign_table(p)), _ => { - p.error("expected DATA or TABLE"); + p.err_and_bump("expected DATA or TABLE"); None } }, @@ -6054,7 +6052,7 @@ fn stmt(p: &mut Parser, r: &StmtRestrictions) -> Option { PARSER_KW => Some(drop_text_search_parser(p)), TEMPLATE_KW => Some(drop_text_search_template(p)), _ => { - p.error("expected TEMPLATE, CONFIGURATION, DICTIONARY, PARSER, or TEMPLATE"); + p.err_and_bump("expected TEMPLATE, CONFIGURATION, DICTIONARY, PARSER, or TEMPLATE"); None } }, @@ -6300,7 +6298,11 @@ fn alter_rule(p: &mut Parser<'_>) -> CompletedMarker { p.bump(RULE_KW); name_ref(p); on_table(p); - rename_to(p); + if p.at(RENAME_KW) { + rename_to(p); + } else { + p.error("expected RENAME"); + } p.eat(SEMICOLON); m.complete(p, ALTER_RULE) } @@ -6911,7 +6913,7 @@ fn alter_index(p: &mut Parser<'_>) -> CompletedMarker { } else { DEPENDS_ON_EXTENSION }; - p.bump(DEPENDS_KW); + p.expect(DEPENDS_KW); p.expect(ON_KW); p.expect(EXTENSION_KW); path_name_ref(p); @@ -7105,17 +7107,12 @@ fn alter_foreign_table(p: &mut Parser<'_>) -> CompletedMarker { // ALTER FOREIGN DATA WRAPPER name OWNER TO { new_owner | CURRENT_ROLE | CURRENT_USER | SESSION_USER } // ALTER FOREIGN DATA WRAPPER name RENAME TO new_name fn alter_foreign_data_wrapper(p: &mut Parser<'_>) -> CompletedMarker { - assert!( - p.at(ALTER_KW) - && p.nth_at(1, FOREIGN_KW) - && p.nth_at(2, DATA_KW) - && p.nth_at(3, WRAPPER_KW) - ); + assert!(p.at(ALTER_KW) && p.nth_at(1, FOREIGN_KW) && p.nth_at(2, DATA_KW)); let m = p.start(); p.bump(ALTER_KW); p.bump(FOREIGN_KW); p.bump(DATA_KW); - p.bump(WRAPPER_KW); + p.expect(WRAPPER_KW); name_ref(p); let found_option = match p.current() { OWNER_KW => { @@ -7303,7 +7300,7 @@ fn alter_extension(p: &mut Parser<'_>) -> CompletedMarker { } LANGUAGE_KW | PROCEDURAL_KW => { p.eat(PROCEDURAL_KW); - p.bump(LANGUAGE_KW); + p.expect(LANGUAGE_KW); name_ref(p); } TEXT_KW => { @@ -10714,14 +10711,12 @@ fn function_sig_list(p: &mut Parser<'_>) { // DROP FOREIGN DATA WRAPPER [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ] fn drop_foreign_data(p: &mut Parser<'_>) -> CompletedMarker { - assert!( - p.at(DROP_KW) && p.nth_at(1, FOREIGN_KW) && p.nth_at(2, DATA_KW) && p.nth_at(3, WRAPPER_KW) - ); + assert!(p.at(DROP_KW) && p.nth_at(1, FOREIGN_KW) && p.nth_at(2, DATA_KW)); let m = p.start(); p.bump(DROP_KW); p.bump(FOREIGN_KW); p.bump(DATA_KW); - p.bump(WRAPPER_KW); + p.expect(WRAPPER_KW); opt_if_exists(p); name_ref_list(p); opt_cascade_or_restrict(p); @@ -10745,11 +10740,11 @@ fn drop_foreign_table(p: &mut Parser<'_>) -> CompletedMarker { // DROP ACCESS METHOD [ IF EXISTS ] name [ CASCADE | RESTRICT ] fn drop_access_method(p: &mut Parser<'_>) -> CompletedMarker { - assert!(p.at(DROP_KW) && p.nth_at(1, ACCESS_KW) && p.nth_at(2, METHOD_KW)); + assert!(p.at(DROP_KW) && p.nth_at(1, ACCESS_KW)); let m = p.start(); p.bump(DROP_KW); p.bump(ACCESS_KW); - p.bump(METHOD_KW); + p.expect(METHOD_KW); opt_if_exists(p); name_ref(p); opt_cascade_or_restrict(p); @@ -10848,11 +10843,11 @@ fn drop_domain(p: &mut Parser<'_>) -> CompletedMarker { // DROP EVENT TRIGGER [ IF EXISTS ] name [ CASCADE | RESTRICT ] fn drop_event_trigger(p: &mut Parser<'_>) -> CompletedMarker { - assert!(p.at(DROP_KW) && p.nth_at(1, EVENT_KW) && p.nth_at(2, TRIGGER_KW)); + assert!(p.at(DROP_KW) && p.nth_at(1, EVENT_KW)); let m = p.start(); p.bump(DROP_KW); p.bump(EVENT_KW); - p.bump(TRIGGER_KW); + p.expect(TRIGGER_KW); opt_if_exists(p); name_ref(p); opt_cascade_or_restrict(p); @@ -10875,11 +10870,11 @@ fn drop_extension(p: &mut Parser<'_>) -> CompletedMarker { // DROP MATERIALIZED VIEW [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ] fn drop_materialized_view(p: &mut Parser<'_>) -> CompletedMarker { - assert!(p.at(DROP_KW) && p.nth_at(1, MATERIALIZED_KW) && p.nth_at(2, VIEW_KW)); + assert!(p.at(DROP_KW) && p.nth_at(1, MATERIALIZED_KW)); let m = p.start(); p.bump(DROP_KW); p.bump(MATERIALIZED_KW); - p.bump(VIEW_KW); + p.expect(VIEW_KW); opt_if_exists(p); path_name_ref_list(p); opt_cascade_or_restrict(p); @@ -14150,7 +14145,7 @@ fn create_index(p: &mut Parser<'_>) -> CompletedMarker { let m = p.start(); p.bump(CREATE_KW); p.eat(UNIQUE_KW); - p.bump(INDEX_KW); + p.expect(INDEX_KW); p.eat(CONCURRENTLY_KW); // [ [ IF NOT EXISTS ] name ] if opt_if_not_exists(p).is_some() { @@ -15290,7 +15285,7 @@ fn opt_alter_table_action(p: &mut Parser<'_>) -> Option { CLUSTER_KW => { let m = p.start(); p.bump(CLUSTER_KW); - p.bump(ON_KW); + p.expect(ON_KW); name_ref(p); m.complete(p, CLUSTER_ON) } diff --git a/crates/squawk_parser/tests/data/err/alter_extension.sql b/crates/squawk_parser/tests/data/err/alter_extension.sql new file mode 100644 index 000000000..3427b0c75 --- /dev/null +++ b/crates/squawk_parser/tests/data/err/alter_extension.sql @@ -0,0 +1,3 @@ +-- PROCEDURAL with no LANGUAGE +alter extension e add procedural; +alter extension e drop procedural; diff --git a/crates/squawk_parser/tests/data/err/alter_index.sql b/crates/squawk_parser/tests/data/err/alter_index.sql new file mode 100644 index 000000000..7d1969b69 --- /dev/null +++ b/crates/squawk_parser/tests/data/err/alter_index.sql @@ -0,0 +1,2 @@ +-- missing trailing depends +alter index idx no; diff --git a/crates/squawk_parser/tests/data/err/create_domain.sql b/crates/squawk_parser/tests/data/err/create_domain.sql new file mode 100644 index 000000000..eeb1c9b4e --- /dev/null +++ b/crates/squawk_parser/tests/data/err/create_domain.sql @@ -0,0 +1,2 @@ +-- missing trailing inherit +create domain d as int check (v > 0) no; diff --git a/crates/squawk_parser/tests/data/err/create_table.sql b/crates/squawk_parser/tests/data/err/create_table.sql index 07546bbb7..5844e5970 100644 --- a/crates/squawk_parser/tests/data/err/create_table.sql +++ b/crates/squawk_parser/tests/data/err/create_table.sql @@ -12,6 +12,9 @@ create table t (a text,); -- missing columns / constraints create table t (,,,,,); +-- missing inherit +create table t ( c int check (c > 10) no; + -- a column list with SET DEFAULT is only supported for ON DELETE actions create table t ( a int, diff --git a/crates/squawk_parser/tests/data/err/join_natural.sql b/crates/squawk_parser/tests/data/err/join_natural.sql new file mode 100644 index 000000000..e22767486 --- /dev/null +++ b/crates/squawk_parser/tests/data/err/join_natural.sql @@ -0,0 +1,4 @@ +-- NATURAL with no following join type +merge into t using u natural; +select * from a natural; +select * from a join natural; diff --git a/crates/squawk_parser/tests/data/err/json_object.sql b/crates/squawk_parser/tests/data/err/json_object.sql new file mode 100644 index 000000000..e9a41682d --- /dev/null +++ b/crates/squawk_parser/tests/data/err/json_object.sql @@ -0,0 +1,4 @@ +-- avoid parser getting stuck +select json_object(]); +select json_object(;); +select json_object(1, ]); diff --git a/crates/squawk_parser/tests/snapshots/tests__alter_extension_err.snap b/crates/squawk_parser/tests/snapshots/tests__alter_extension_err.snap new file mode 100644 index 000000000..38c90a75f --- /dev/null +++ b/crates/squawk_parser/tests/snapshots/tests__alter_extension_err.snap @@ -0,0 +1,50 @@ +--- +source: crates/squawk_parser/tests/tests.rs +input_file: crates/squawk_parser/tests/data/err/alter_extension.sql +--- +SOURCE_FILE + COMMENT "-- PROCEDURAL with no LANGUAGE" + WHITESPACE "\n" + ALTER_EXTENSION + ALTER_KW "alter" + WHITESPACE " " + EXTENSION_KW "extension" + WHITESPACE " " + NAME_REF + IDENT "e" + WHITESPACE " " + ADD_KW "add" + WHITESPACE " " + PROCEDURAL_KW "procedural" + SEMICOLON ";" + WHITESPACE "\n" + ALTER_EXTENSION + ALTER_KW "alter" + WHITESPACE " " + EXTENSION_KW "extension" + WHITESPACE " " + NAME_REF + IDENT "e" + WHITESPACE " " + DROP_KW "drop" + WHITESPACE " " + PROCEDURAL_KW "procedural" + SEMICOLON ";" + WHITESPACE "\n" +--- +error[syntax-error]: expected LANGUAGE_KW + ╭▸ +2 │ alter extension e add procedural; + ╰╴ ━ +error[syntax-error]: expected name + ╭▸ +2 │ alter extension e add procedural; + ╰╴ ━ +error[syntax-error]: expected LANGUAGE_KW + ╭▸ +3 │ alter extension e drop procedural; + ╰╴ ━ +error[syntax-error]: expected name + ╭▸ +3 │ alter extension e drop procedural; + ╰╴ ━ diff --git a/crates/squawk_parser/tests/snapshots/tests__alter_index_err.snap b/crates/squawk_parser/tests/snapshots/tests__alter_index_err.snap new file mode 100644 index 000000000..0aff249b2 --- /dev/null +++ b/crates/squawk_parser/tests/snapshots/tests__alter_index_err.snap @@ -0,0 +1,38 @@ +--- +source: crates/squawk_parser/tests/tests.rs +input_file: crates/squawk_parser/tests/data/err/alter_index.sql +--- +SOURCE_FILE + COMMENT "-- missing trailing depends" + WHITESPACE "\n" + ALTER_INDEX + ALTER_KW "alter" + WHITESPACE " " + INDEX_KW "index" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME_REF + IDENT "idx" + WHITESPACE " " + NO_DEPENDS_ON_EXTENSION + NO_KW "no" + SEMICOLON ";" + WHITESPACE "\n" +--- +error[syntax-error]: expected DEPENDS_KW + ╭▸ +2 │ alter index idx no; + ╰╴ ━ +error[syntax-error]: expected ON_KW + ╭▸ +2 │ alter index idx no; + ╰╴ ━ +error[syntax-error]: expected EXTENSION_KW + ╭▸ +2 │ alter index idx no; + ╰╴ ━ +error[syntax-error]: expected path name + ╭▸ +2 │ alter index idx no; + ╰╴ ━ diff --git a/crates/squawk_parser/tests/snapshots/tests__create_domain_err.snap b/crates/squawk_parser/tests/snapshots/tests__create_domain_err.snap new file mode 100644 index 000000000..3faacfa90 --- /dev/null +++ b/crates/squawk_parser/tests/snapshots/tests__create_domain_err.snap @@ -0,0 +1,48 @@ +--- +source: crates/squawk_parser/tests/tests.rs +input_file: crates/squawk_parser/tests/data/err/create_domain.sql +--- +SOURCE_FILE + COMMENT "-- missing trailing inherit" + WHITESPACE "\n" + CREATE_DOMAIN + CREATE_KW "create" + WHITESPACE " " + DOMAIN_KW "domain" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME + IDENT "d" + WHITESPACE " " + AS_KW "as" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + INT_KW "int" + WHITESPACE " " + CHECK_CONSTRAINT + CHECK_KW "check" + WHITESPACE " " + L_PAREN "(" + BIN_EXPR + NAME_REF + IDENT "v" + WHITESPACE " " + R_ANGLE ">" + WHITESPACE " " + LITERAL + INT_NUMBER "0" + R_PAREN ")" + WHITESPACE " " + NO_INHERIT + NO_KW "no" + SEMICOLON ";" + WHITESPACE "\n" +--- +error[syntax-error]: expected INHERIT_KW + ╭▸ +2 │ create domain d as int check (v > 0) no; + ╰╴ ━ diff --git a/crates/squawk_parser/tests/snapshots/tests__create_table_err.snap b/crates/squawk_parser/tests/snapshots/tests__create_table_err.snap index 2036f7b73..434e67f38 100644 --- a/crates/squawk_parser/tests/snapshots/tests__create_table_err.snap +++ b/crates/squawk_parser/tests/snapshots/tests__create_table_err.snap @@ -101,6 +101,49 @@ SOURCE_FILE R_PAREN ")" SEMICOLON ";" WHITESPACE "\n\n" + CREATE_TABLE + COMMENT "-- missing inherit" + WHITESPACE "\n" + CREATE_KW "create" + WHITESPACE " " + TABLE_KW "table" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME + IDENT "t" + WHITESPACE " " + TABLE_ARG_LIST + L_PAREN "(" + WHITESPACE " " + COLUMN + NAME + IDENT "c" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + INT_KW "int" + WHITESPACE " " + CHECK_CONSTRAINT + CHECK_KW "check" + WHITESPACE " " + L_PAREN "(" + BIN_EXPR + NAME_REF + IDENT "c" + WHITESPACE " " + R_ANGLE ">" + WHITESPACE " " + LITERAL + INT_NUMBER "10" + R_PAREN ")" + WHITESPACE " " + NO_INHERIT + NO_KW "no" + SEMICOLON ";" + WHITESPACE "\n\n" CREATE_TABLE COMMENT "-- a column list with SET DEFAULT is only supported for ON DELETE actions" WHITESPACE "\n" @@ -702,23 +745,31 @@ error[syntax-error]: unexpected comma ╭▸ 13 │ create table t (,,,,,); ╰╴ ━ +error[syntax-error]: expected INHERIT_KW + ╭▸ +16 │ create table t ( c int check (c > 10) no; + ╰╴ ━ +error[syntax-error]: expected R_PAREN + ╭▸ +16 │ create table t ( c int check (c > 10) no; + ╰╴ ━ error[syntax-error]: expected COMMA ╭▸ -51 │ for values from ('2024-01-01' 1) to ('2024-04-01' 5); +54 │ for values from ('2024-01-01' 1) to ('2024-04-01' 5); ╰╴ ━ error[syntax-error]: expected COMMA ╭▸ -51 │ for values from ('2024-01-01' 1) to ('2024-04-01' 5); +54 │ for values from ('2024-01-01' 1) to ('2024-04-01' 5); ╰╴ ━ error[syntax-error]: expected COMMA ╭▸ -55 │ for values in (1 2 3); +58 │ for values in (1 2 3); ╰╴ ━ error[syntax-error]: expected COMMA ╭▸ -55 │ for values in (1 2 3); +58 │ for values in (1 2 3); ╰╴ ━ error[syntax-error]: expected COMMA ╭▸ -61 │ exclude using btree ( a with buzz.> b with <) +64 │ exclude using btree ( a with buzz.> b with <) ╰╴ ━ diff --git a/crates/squawk_parser/tests/snapshots/tests__join_natural_err.snap b/crates/squawk_parser/tests/snapshots/tests__join_natural_err.snap new file mode 100644 index 000000000..a5d551022 --- /dev/null +++ b/crates/squawk_parser/tests/snapshots/tests__join_natural_err.snap @@ -0,0 +1,126 @@ +--- +source: crates/squawk_parser/tests/tests.rs +input_file: crates/squawk_parser/tests/data/err/join_natural.sql +--- +SOURCE_FILE + COMMENT "-- NATURAL with no following join type" + WHITESPACE "\n" + MERGE + MERGE_KW "merge" + WHITESPACE " " + INTO_KW "into" + WHITESPACE " " + RELATION_NAME + PATH + PATH_SEGMENT + NAME_REF + IDENT "t" + WHITESPACE " " + USING_ON_CLAUSE + USING_KW "using" + WHITESPACE " " + JOIN_EXPR + FROM_ITEM + NAME_REF + IDENT "u" + WHITESPACE " " + JOIN + NATURAL_KW "natural" + MERGE_WHEN_NOT_MATCHED_TARGET + MERGE_DO_NOTHING + SEMICOLON ";" + WHITESPACE "\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + STAR "*" + WHITESPACE " " + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + JOIN_EXPR + FROM_ITEM + NAME_REF + IDENT "a" + WHITESPACE " " + JOIN + NATURAL_KW "natural" + SEMICOLON ";" + WHITESPACE "\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + STAR "*" + WHITESPACE " " + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + JOIN_EXPR + JOIN_EXPR + FROM_ITEM + NAME_REF + IDENT "a" + WHITESPACE " " + JOIN + JOIN_INNER + JOIN_KW "join" + WHITESPACE " " + JOIN + NATURAL_KW "natural" + SEMICOLON ";" + WHITESPACE "\n" +--- +error[syntax-error]: expected join type + ╭▸ +2 │ merge into t using u natural; + ╰╴ ━ +error[syntax-error]: expected from_item + ╭▸ +2 │ merge into t using u natural; + ╰╴ ━ +error[syntax-error]: expected on clause + ╭▸ +2 │ merge into t using u natural; + ╰╴ ━ +error[syntax-error]: expected WHEN_KW + ╭▸ +2 │ merge into t using u natural; + ╰╴ ━ +error[syntax-error]: expected MATCHED, or NOT MATCHED + ╭▸ +2 │ merge into t using u natural; + ╰╴ ━ +error[syntax-error]: expected THEN_KW + ╭▸ +2 │ merge into t using u natural; + ╰╴ ━ +error[syntax-error]: expected INSERT, UPDATE, DELETE, or DO NOTHING + ╭▸ +2 │ merge into t using u natural; + ╰╴ ━ +error[syntax-error]: expected join type + ╭▸ +3 │ select * from a natural; + ╰╴ ━ +error[syntax-error]: expected from_item + ╭▸ +3 │ select * from a natural; + ╰╴ ━ +error[syntax-error]: expected from_item + ╭▸ +4 │ select * from a join natural; + ╰╴ ━ +error[syntax-error]: expected join type + ╭▸ +4 │ select * from a join natural; + ╰╴ ━ +error[syntax-error]: expected from_item + ╭▸ +4 │ select * from a join natural; + ╰╴ ━ diff --git a/crates/squawk_parser/tests/snapshots/tests__json_object_err.snap b/crates/squawk_parser/tests/snapshots/tests__json_object_err.snap new file mode 100644 index 000000000..d6f5916de --- /dev/null +++ b/crates/squawk_parser/tests/snapshots/tests__json_object_err.snap @@ -0,0 +1,93 @@ +--- +source: crates/squawk_parser/tests/tests.rs +input_file: crates/squawk_parser/tests/data/err/json_object.sql +--- +SOURCE_FILE + COMMENT "-- avoid parser getting stuck" + WHITESPACE "\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + CALL_EXPR + JSON_OBJECT_FN + JSON_OBJECT_KW "json_object" + L_PAREN "(" + ERROR + R_BRACK "]" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + CALL_EXPR + JSON_OBJECT_FN + JSON_OBJECT_KW "json_object" + L_PAREN "(" + ERROR + SEMICOLON ";" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + CALL_EXPR + JSON_OBJECT_FN + JSON_OBJECT_KW "json_object" + L_PAREN "(" + LITERAL + INT_NUMBER "1" + COMMA "," + WHITESPACE " " + ERROR + R_BRACK "]" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" +--- +error[syntax-error]: expected an expression, found R_BRACK + ╭▸ +2 │ select json_object(]); + ╰╴ ━ +error[syntax-error]: expected expression + ╭▸ +2 │ select json_object(]); + ╰╴ ━ +error[syntax-error]: expected a comma + ╭▸ +2 │ select json_object(]); + ╰╴ ━ +error[syntax-error]: expected an expression, found SEMICOLON + ╭▸ +3 │ select json_object(;); + ╰╴ ━ +error[syntax-error]: expected expression + ╭▸ +3 │ select json_object(;); + ╰╴ ━ +error[syntax-error]: expected a comma + ╭▸ +3 │ select json_object(;); + ╰╴ ━ +error[syntax-error]: expected an expression, found R_BRACK + ╭▸ +4 │ select json_object(1, ]); + ╰╴ ━ +error[syntax-error]: expected expression + ╭▸ +4 │ select json_object(1, ]); + ╰╴ ━ +error[syntax-error]: expected a comma + ╭▸ +4 │ select json_object(1, ]); + ╰╴ ━