From 93ffda7f6da833a7a87864b9c881935f419e2591 Mon Sep 17 00:00:00 2001 From: Steve Dignam Date: Mon, 31 Aug 2026 21:32:13 -0400 Subject: [PATCH 1/2] parser: add validation for params --- crates/squawk_ide/src/code_actions/mod.rs | 12 +- ...param_in.rs => remove_routine_param_in.rs} | 74 +++- ... rewrite_routine_param_in_out_as_inout.rs} | 60 ++- ...t__aggregate_object_params_validation.snap | 78 ++++ ...st__alter_aggregate_params_validation.snap | 104 ++++-- ...t__create_aggregate_params_validation.snap | 62 +++- ...est__drop_aggregate_params_validation.snap | 350 ++++++++++-------- ...est__function_table_params_validation.snap | 188 ++++++++++ crates/squawk_syntax/src/validation.rs | 41 +- .../validation/aggregate_object_params.sql | 3 + .../validation/alter_aggregate_params.sql | 2 +- .../validation/create_aggregate_params.sql | 2 +- .../validation/drop_aggregate_params.sql | 2 + .../validation/function_table_params.sql | 5 + 14 files changed, 726 insertions(+), 257 deletions(-) rename crates/squawk_ide/src/code_actions/{remove_function_param_in.rs => remove_routine_param_in.rs} (59%) rename crates/squawk_ide/src/code_actions/{rewrite_function_param_in_out_as_inout.rs => rewrite_routine_param_in_out_as_inout.rs} (56%) create mode 100644 crates/squawk_syntax/src/snapshots/squawk_syntax__test__aggregate_object_params_validation.snap create mode 100644 crates/squawk_syntax/src/snapshots/squawk_syntax__test__function_table_params_validation.snap create mode 100644 crates/squawk_syntax/test_data/validation/aggregate_object_params.sql create mode 100644 crates/squawk_syntax/test_data/validation/function_table_params.sql diff --git a/crates/squawk_ide/src/code_actions/mod.rs b/crates/squawk_ide/src/code_actions/mod.rs index 667fa5c07..a403231b9 100644 --- a/crates/squawk_ide/src/code_actions/mod.rs +++ b/crates/squawk_ide/src/code_actions/mod.rs @@ -9,8 +9,8 @@ mod add_schema; mod convert_comment; mod quote_identifier; mod remove_else_clause; -mod remove_function_param_in; mod remove_redundant_alias; +mod remove_routine_param_in; mod rewrite_as_dollar_quoted_string; mod rewrite_as_regular_string; mod rewrite_at_local_as_timezone; @@ -24,7 +24,6 @@ mod rewrite_double_colon_to_cast; mod rewrite_extract_as_function_call; mod rewrite_from; mod rewrite_function_param_default_as_equals; -mod rewrite_function_param_in_out_as_inout; mod rewrite_in_as_expression; mod rewrite_integer_radix; mod rewrite_is_normalized_as_function_call; @@ -37,6 +36,7 @@ mod rewrite_overlaps_as_function_call; mod rewrite_overlay_as_function_call; mod rewrite_pattern_matching_as_operators; mod rewrite_position_as_function_call; +mod rewrite_routine_param_in_out_as_inout; mod rewrite_rows_from_as_unnest; mod rewrite_select_as_table; mod rewrite_select_as_values; @@ -60,8 +60,8 @@ use add_schema::add_schema; use convert_comment::convert_comment; use quote_identifier::quote_identifier; use remove_else_clause::remove_else_clause; -use remove_function_param_in::remove_function_param_in; use remove_redundant_alias::remove_redundant_alias; +use remove_routine_param_in::remove_routine_param_in; use rewrite_as_dollar_quoted_string::rewrite_as_dollar_quoted_string; use rewrite_as_regular_string::rewrite_as_regular_string; use rewrite_at_local_as_timezone::rewrite_at_local_as_timezone; @@ -75,7 +75,6 @@ use rewrite_double_colon_to_cast::rewrite_double_colon_to_cast; use rewrite_extract_as_function_call::rewrite_extract_as_function_call; use rewrite_from::rewrite_from; use rewrite_function_param_default_as_equals::rewrite_function_param_default_as_equals; -use rewrite_function_param_in_out_as_inout::rewrite_function_param_in_out_as_inout; use rewrite_in_as_expression::rewrite_in_as_expression; use rewrite_integer_radix::rewrite_integer_radix; use rewrite_is_normalized_as_function_call::rewrite_is_normalized_as_function_call; @@ -88,6 +87,7 @@ use rewrite_overlaps_as_function_call::rewrite_overlaps_as_function_call; use rewrite_overlay_as_function_call::rewrite_overlay_as_function_call; use rewrite_pattern_matching_as_operators::rewrite_pattern_matching_as_operators; use rewrite_position_as_function_call::rewrite_position_as_function_call; +use rewrite_routine_param_in_out_as_inout::rewrite_routine_param_in_out_as_inout; use rewrite_rows_from_as_unnest::rewrite_rows_from_as_unnest; use rewrite_select_as_table::rewrite_select_as_table; use rewrite_select_as_values::rewrite_select_as_values; @@ -125,8 +125,8 @@ pub fn code_actions(db: &dyn Db, position: InFile) -> Option, actions: &mut Vec, ) -> Option<()> { let token = token_from_offset(db, position)?; let param = token.parent_ancestors().find_map(ast::Param::cast)?; - param - .syntax() - .ancestors() - .find_map(ast::CreateFunction::cast)?; let ast::ParamMode::ParamIn(mode) = param.mode()? else { return None; }; @@ -46,13 +42,13 @@ mod test { use crate::code_actions::test_utils::{apply_code_action, code_action_not_applicable}; - use super::remove_function_param_in; + use super::remove_routine_param_in; #[test] fn removes_in() { assert_snapshot!( apply_code_action( - remove_function_param_in, + remove_routine_param_in, "create function f(i$0n value int) returns int language sql as $$ select value $$;", ), @"create function f(value int) returns int language sql as $$ select value $$;" @@ -63,7 +59,7 @@ mod test { fn preserves_comments() { assert_snapshot!( apply_code_action( - remove_function_param_in, + remove_routine_param_in, "create function f(i$0n /* before value */ value int) returns int language sql as $$ select value $$;", ), @"create function f(/* before value */ value int) returns int language sql as $$ select value $$;" @@ -74,17 +70,60 @@ mod test { fn applies_when_mode_follows_name() { assert_snapshot!( apply_code_action( - remove_function_param_in, + remove_routine_param_in, "create function f(value i$0n int) returns int language sql as $$ select value $$;", ), @"create function f(value int) returns int language sql as $$ select value $$;" ); } + #[test] + fn applies_to_create_aggregate_param() { + assert_snapshot!( + apply_code_action( + remove_routine_param_in, + "create aggregate a(i$0n value int) (sfunc = f, stype = int);", + ), + @"create aggregate a(value int) (sfunc = f, stype = int);" + ); + } + + #[test] + fn applies_to_aggregate_signature() { + assert_snapshot!( + apply_code_action(remove_routine_param_in, "drop aggregate a(i$0n int);"), + @"drop aggregate a(int);" + ); + } + + #[test] + fn applies_to_function_signature() { + assert_snapshot!( + apply_code_action(remove_routine_param_in, "drop function f(i$0n int);"), + @"drop function f(int);" + ); + } + + #[test] + fn applies_to_procedure_signature() { + assert_snapshot!( + apply_code_action(remove_routine_param_in, "drop procedure p(i$0n int);"), + @"drop procedure p(int);" + ); + } + + #[test] + fn applies_to_routine_signature() { + assert_snapshot!( + apply_code_action(remove_routine_param_in, "drop routine r(i$0n int);"), + @"drop routine r(int);" + ); + } + #[test] fn not_applicable_to_out() { assert!(code_action_not_applicable( - remove_function_param_in, + remove_routine_param_in, "create function f(o$0ut value int) returns int language sql as $$ select value $$;" )); } @@ -92,16 +131,19 @@ mod test { #[test] fn not_applicable_to_inout() { assert!(code_action_not_applicable( - remove_function_param_in, + remove_routine_param_in, "create function f(ino$0ut value int) returns int language sql as $$ select value $$;" )); } #[test] - fn not_applicable_to_procedure_param() { - assert!(code_action_not_applicable( - remove_function_param_in, - "create procedure p(i$0n value int) language sql as $$ select value $$;" - )); + fn applies_to_procedure_param() { + assert_snapshot!( + apply_code_action( + remove_routine_param_in, + "create procedure p(i$0n value int) language sql as $$ select value $$;", + ), + @"create procedure p(value int) language sql as $$ select value $$;" + ); } } diff --git a/crates/squawk_ide/src/code_actions/rewrite_function_param_in_out_as_inout.rs b/crates/squawk_ide/src/code_actions/rewrite_routine_param_in_out_as_inout.rs similarity index 56% rename from crates/squawk_ide/src/code_actions/rewrite_function_param_in_out_as_inout.rs rename to crates/squawk_ide/src/code_actions/rewrite_routine_param_in_out_as_inout.rs index e8de4d170..f24eac389 100644 --- a/crates/squawk_ide/src/code_actions/rewrite_function_param_in_out_as_inout.rs +++ b/crates/squawk_ide/src/code_actions/rewrite_routine_param_in_out_as_inout.rs @@ -7,17 +7,13 @@ use crate::{file::InFile, offsets::token_from_offset}; use super::{ActionKind, CodeAction}; -pub(super) fn rewrite_function_param_in_out_as_inout( +pub(super) fn rewrite_routine_param_in_out_as_inout( db: &dyn Db, position: InFile, actions: &mut Vec, ) -> Option<()> { let token = token_from_offset(db, position)?; let param = token.parent_ancestors().find_map(ast::Param::cast)?; - param - .syntax() - .ancestors() - .find_map(ast::CreateFunction::cast)?; let ast::ParamMode::ParamInOut(mode) = param.mode()? else { return None; }; @@ -42,13 +38,13 @@ mod test { use crate::code_actions::test_utils::{apply_code_action, code_action_not_applicable}; - use super::rewrite_function_param_in_out_as_inout; + use super::rewrite_routine_param_in_out_as_inout; #[test] fn rewrites_in_out_as_inout() { assert_snapshot!( apply_code_action( - rewrite_function_param_in_out_as_inout, + rewrite_routine_param_in_out_as_inout, "create function f(in $0out value int) returns int language sql as $$ select value $$;", ), @"create function f(inout value int) returns int language sql as $$ select value $$;" @@ -59,26 +55,62 @@ mod test { fn applies_when_mode_follows_name() { assert_snapshot!( apply_code_action( - rewrite_function_param_in_out_as_inout, + rewrite_routine_param_in_out_as_inout, "create function f(value in o$0ut int) returns int language sql as $$ select value $$;", ), @"create function f(value inout int) returns int language sql as $$ select value $$;" ); } + #[test] + fn applies_to_function_signature() { + assert_snapshot!( + apply_code_action( + rewrite_routine_param_in_out_as_inout, + "drop function f(in o$0ut int);", + ), + @"drop function f(inout int);" + ); + } + + #[test] + fn applies_to_procedure_signature() { + assert_snapshot!( + apply_code_action( + rewrite_routine_param_in_out_as_inout, + "drop procedure p(in o$0ut int);", + ), + @"drop procedure p(inout int);" + ); + } + + #[test] + fn applies_to_routine_signature() { + assert_snapshot!( + apply_code_action( + rewrite_routine_param_in_out_as_inout, + "drop routine r(in o$0ut int);", + ), + @"drop routine r(inout int);" + ); + } + #[test] fn not_applicable_to_inout() { assert!(code_action_not_applicable( - rewrite_function_param_in_out_as_inout, + rewrite_routine_param_in_out_as_inout, "create function f(ino$0ut value int) returns int language sql as $$ select value $$;" )); } #[test] - fn not_applicable_to_procedure_param() { - assert!(code_action_not_applicable( - rewrite_function_param_in_out_as_inout, - "create procedure p(in o$0ut value int) language sql as $$ select value $$;" - )); + fn applies_to_procedure_param() { + assert_snapshot!( + apply_code_action( + rewrite_routine_param_in_out_as_inout, + "create procedure p(in o$0ut value int) language sql as $$ select value $$;", + ), + @"create procedure p(inout value int) language sql as $$ select value $$;" + ); } } diff --git a/crates/squawk_syntax/src/snapshots/squawk_syntax__test__aggregate_object_params_validation.snap b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__aggregate_object_params_validation.snap new file mode 100644 index 000000000..625843864 --- /dev/null +++ b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__aggregate_object_params_validation.snap @@ -0,0 +1,78 @@ +--- +source: crates/squawk_syntax/src/test.rs +input_file: crates/squawk_syntax/test_data/validation/aggregate_object_params.sql +--- +SOURCE_FILE@0..105 + COMMENT_ON@0..47 + COMMENT_KW@0..7 "comment" + WHITESPACE@7..8 " " + ON_KW@8..10 "on" + WHITESPACE@10..11 " " + OBJECT_AGGREGATE@11..33 + AGGREGATE_KW@11..20 "aggregate" + WHITESPACE@20..21 " " + AGGREGATE@21..33 + PATH_REF@21..22 + PATH_SEGMENT_REF@21..22 + IDENT@21..22 "a" + PARAM_LIST@22..33 + L_PAREN@22..23 "(" + PARAM@23..32 + PARAM_IN_OUT@23..28 + INOUT_KW@23..28 "inout" + WHITESPACE@28..29 " " + PATH_TYPE@29..32 + PATH_REF@29..32 + PATH_SEGMENT_REF@29..32 + INT_KW@29..32 "int" + R_PAREN@32..33 ")" + WHITESPACE@33..34 " " + IS_KW@34..36 "is" + WHITESPACE@36..37 " " + LITERAL@37..46 + STRING@37..46 "'invalid'" + SEMICOLON@46..47 ";" + WHITESPACE@47..49 "\n\n" + SECURITY_LABEL@49..104 + SECURITY_KW@49..57 "security" + WHITESPACE@57..58 " " + LABEL_KW@58..63 "label" + WHITESPACE@63..64 " " + ON_KW@64..66 "on" + WHITESPACE@66..67 " " + OBJECT_AGGREGATE@67..90 + AGGREGATE_KW@67..76 "aggregate" + WHITESPACE@76..77 " " + AGGREGATE@77..90 + PATH_REF@77..78 + PATH_SEGMENT_REF@77..78 + IDENT@77..78 "a" + PARAM_LIST@78..90 + L_PAREN@78..79 "(" + PARAM@79..89 + PARAM_IN_OUT@79..85 + IN_KW@79..81 "in" + WHITESPACE@81..82 " " + OUT_KW@82..85 "out" + WHITESPACE@85..86 " " + PATH_TYPE@86..89 + PATH_REF@86..89 + PATH_SEGMENT_REF@86..89 + INT_KW@86..89 "int" + R_PAREN@89..90 ")" + WHITESPACE@90..91 " " + IS_KW@91..93 "is" + WHITESPACE@93..94 " " + LITERAL@94..103 + STRING@94..103 "'invalid'" + SEMICOLON@103..104 ";" + WHITESPACE@104..105 "\n" + +error[syntax-error]: In Out params are not allowed with aggregates. + ╭▸ +1 │ comment on aggregate a(inout int) is 'invalid'; + ╰╴ ━━━━━ +error[syntax-error]: In Out params are not allowed with aggregates. + ╭▸ +3 │ security label on aggregate a(in out int) is 'invalid'; + ╰╴ ━━━━━━ diff --git a/crates/squawk_syntax/src/snapshots/squawk_syntax__test__alter_aggregate_params_validation.snap b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__alter_aggregate_params_validation.snap index f647d40f5..a30ea406f 100644 --- a/crates/squawk_syntax/src/snapshots/squawk_syntax__test__alter_aggregate_params_validation.snap +++ b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__alter_aggregate_params_validation.snap @@ -2,10 +2,10 @@ source: crates/squawk_syntax/src/test.rs input_file: crates/squawk_syntax/test_data/validation/alter_aggregate_params.sql --- -SOURCE_FILE@0..101 +SOURCE_FILE@0..120 COMMENT@0..38 "-- can't have out par ..." WHITESPACE@38..39 "\n" - CREATE_AGGREGATE@39..100 + CREATE_AGGREGATE@39..119 CREATE_KW@39..45 "create" WHITESPACE@45..46 " " AGGREGATE_KW@46..55 "aggregate" @@ -14,7 +14,7 @@ SOURCE_FILE@0..101 PATH@56..57 PATH_SEGMENT@56..57 IDENT@56..57 "a" - PARAM_LIST@57..70 + PARAM_LIST@57..89 L_PAREN@57..58 "(" PARAM@58..62 PARAM_IN@58..60 @@ -34,41 +34,71 @@ SOURCE_FILE@0..101 PATH_REF@68..69 PATH_SEGMENT_REF@68..69 IDENT@68..69 "y" - R_PAREN@69..70 ")" - WHITESPACE@70..71 " " - ATTRIBUTE_LIST@71..99 - L_PAREN@71..72 "(" - WHITESPACE@72..75 "\n " - ATTRIBUTE_OPTION@75..84 - ATTRIBUTE_NAME@75..80 - IDENT@75..80 "sfunc" - WHITESPACE@80..81 " " - EQ@81..82 "=" - WHITESPACE@82..83 " " - ATTRIBUTE_VALUE@83..84 - PATH_TYPE@83..84 - PATH_REF@83..84 - PATH_SEGMENT_REF@83..84 - IDENT@83..84 "f" - COMMA@84..85 "," - WHITESPACE@85..88 "\n " - ATTRIBUTE_OPTION@88..97 - ATTRIBUTE_NAME@88..93 - IDENT@88..93 "stype" - WHITESPACE@93..94 " " - EQ@94..95 "=" - WHITESPACE@95..96 " " - ATTRIBUTE_VALUE@96..97 - PATH_TYPE@96..97 - PATH_REF@96..97 - PATH_SEGMENT_REF@96..97 - IDENT@96..97 "t" - WHITESPACE@97..98 "\n" - R_PAREN@98..99 ")" - SEMICOLON@99..100 ";" - WHITESPACE@100..101 "\n" + COMMA@69..70 "," + WHITESPACE@70..71 " " + PARAM@71..78 + PARAM_IN_OUT@71..76 + INOUT_KW@71..76 "inout" + WHITESPACE@76..77 " " + PATH_TYPE@77..78 + PATH_REF@77..78 + PATH_SEGMENT_REF@77..78 + IDENT@77..78 "z" + COMMA@78..79 "," + WHITESPACE@79..80 " " + PARAM@80..88 + PARAM_IN_OUT@80..86 + IN_KW@80..82 "in" + WHITESPACE@82..83 " " + OUT_KW@83..86 "out" + WHITESPACE@86..87 " " + PATH_TYPE@87..88 + PATH_REF@87..88 + PATH_SEGMENT_REF@87..88 + IDENT@87..88 "w" + R_PAREN@88..89 ")" + WHITESPACE@89..90 " " + ATTRIBUTE_LIST@90..118 + L_PAREN@90..91 "(" + WHITESPACE@91..94 "\n " + ATTRIBUTE_OPTION@94..103 + ATTRIBUTE_NAME@94..99 + IDENT@94..99 "sfunc" + WHITESPACE@99..100 " " + EQ@100..101 "=" + WHITESPACE@101..102 " " + ATTRIBUTE_VALUE@102..103 + PATH_TYPE@102..103 + PATH_REF@102..103 + PATH_SEGMENT_REF@102..103 + IDENT@102..103 "f" + COMMA@103..104 "," + WHITESPACE@104..107 "\n " + ATTRIBUTE_OPTION@107..116 + ATTRIBUTE_NAME@107..112 + IDENT@107..112 "stype" + WHITESPACE@112..113 " " + EQ@113..114 "=" + WHITESPACE@114..115 " " + ATTRIBUTE_VALUE@115..116 + PATH_TYPE@115..116 + PATH_REF@115..116 + PATH_SEGMENT_REF@115..116 + IDENT@115..116 "t" + WHITESPACE@116..117 "\n" + R_PAREN@117..118 ")" + SEMICOLON@118..119 ";" + WHITESPACE@119..120 "\n" error[syntax-error]: Out params are not allowed with aggregates. ╭▸ -2 │ create aggregate a(in x, out y) ( +2 │ create aggregate a(in x, out y, inout z, in out w) ( ╰╴ ━━━ +error[syntax-error]: In Out params are not allowed with aggregates. + ╭▸ +2 │ create aggregate a(in x, out y, inout z, in out w) ( + ╰╴ ━━━━━ +error[syntax-error]: In Out params are not allowed with aggregates. + ╭▸ +2 │ create aggregate a(in x, out y, inout z, in out w) ( + ╰╴ ━━━━━━ diff --git a/crates/squawk_syntax/src/snapshots/squawk_syntax__test__create_aggregate_params_validation.snap b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__create_aggregate_params_validation.snap index cf514c8ce..3e834fc83 100644 --- a/crates/squawk_syntax/src/snapshots/squawk_syntax__test__create_aggregate_params_validation.snap +++ b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__create_aggregate_params_validation.snap @@ -2,20 +2,20 @@ source: crates/squawk_syntax/src/test.rs input_file: crates/squawk_syntax/test_data/validation/create_aggregate_params.sql --- -SOURCE_FILE@0..91 +SOURCE_FILE@0..109 COMMENT@0..38 "-- can't have out par ..." WHITESPACE@38..39 "\n" - ALTER_AGGREGATE@39..89 + ALTER_AGGREGATE@39..107 ALTER_KW@39..44 "alter" WHITESPACE@44..45 " " AGGREGATE_KW@45..54 "aggregate" WHITESPACE@54..55 " " - AGGREGATE@55..70 + AGGREGATE@55..89 PATH_REF@55..56 PATH_SEGMENT_REF@55..56 IDENT@55..56 "a" WHITESPACE@56..57 " " - PARAM_LIST@57..70 + PARAM_LIST@57..89 L_PAREN@57..58 "(" PARAM@58..62 PARAM_IN@58..60 @@ -35,19 +35,49 @@ SOURCE_FILE@0..91 PATH_REF@68..69 PATH_SEGMENT_REF@68..69 IDENT@68..69 "u" - R_PAREN@69..70 ")" - WHITESPACE@70..76 " \n " - SET_SCHEMA@76..88 - SET_KW@76..79 "set" - WHITESPACE@79..80 " " - SCHEMA_KW@80..86 "schema" - WHITESPACE@86..87 " " - SCHEMA_REF@87..88 - IDENT@87..88 "s" - SEMICOLON@88..89 ";" - WHITESPACE@89..91 "\n\n" + COMMA@69..70 "," + WHITESPACE@70..71 " " + PARAM@71..78 + PARAM_IN_OUT@71..76 + INOUT_KW@71..76 "inout" + WHITESPACE@76..77 " " + PATH_TYPE@77..78 + PATH_REF@77..78 + PATH_SEGMENT_REF@77..78 + IDENT@77..78 "v" + COMMA@78..79 "," + WHITESPACE@79..80 " " + PARAM@80..88 + PARAM_IN_OUT@80..86 + IN_KW@80..82 "in" + WHITESPACE@82..83 " " + OUT_KW@83..86 "out" + WHITESPACE@86..87 " " + PATH_TYPE@87..88 + PATH_REF@87..88 + PATH_SEGMENT_REF@87..88 + IDENT@87..88 "w" + R_PAREN@88..89 ")" + WHITESPACE@89..94 "\n " + SET_SCHEMA@94..106 + SET_KW@94..97 "set" + WHITESPACE@97..98 " " + SCHEMA_KW@98..104 "schema" + WHITESPACE@104..105 " " + SCHEMA_REF@105..106 + IDENT@105..106 "s" + SEMICOLON@106..107 ";" + WHITESPACE@107..109 "\n\n" error[syntax-error]: Out params are not allowed with aggregates. ╭▸ -2 │ alter aggregate a (in t, out u) +2 │ alter aggregate a (in t, out u, inout v, in out w) ╰╴ ━━━ +error[syntax-error]: In Out params are not allowed with aggregates. + ╭▸ +2 │ alter aggregate a (in t, out u, inout v, in out w) + ╰╴ ━━━━━ +error[syntax-error]: In Out params are not allowed with aggregates. + ╭▸ +2 │ alter aggregate a (in t, out u, inout v, in out w) + ╰╴ ━━━━━━ diff --git a/crates/squawk_syntax/src/snapshots/squawk_syntax__test__drop_aggregate_params_validation.snap b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__drop_aggregate_params_validation.snap index 6b922fcbd..2a4c1183d 100644 --- a/crates/squawk_syntax/src/snapshots/squawk_syntax__test__drop_aggregate_params_validation.snap +++ b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__drop_aggregate_params_validation.snap @@ -2,11 +2,11 @@ source: crates/squawk_syntax/src/test.rs input_file: crates/squawk_syntax/test_data/validation/drop_aggregate_params.sql --- -SOURCE_FILE@0..389 +SOURCE_FILE@0..444 WHITESPACE@0..1 "\n" COMMENT@1..8 "-- full" WHITESPACE@8..9 "\n" - DROP_AGGREGATE@9..168 + DROP_AGGREGATE@9..223 DROP_KW@9..13 "drop" WHITESPACE@13..14 " " AGGREGATE_KW@14..23 "aggregate" @@ -40,7 +40,7 @@ SOURCE_FILE@0..389 R_PAREN@62..63 ")" COMMA@63..64 "," WHITESPACE@64..70 " \n " - AGGREGATE@70..146 + AGGREGATE@70..201 PATH_REF@70..77 PATH_REF@70..73 PATH_SEGMENT_REF@70..73 @@ -48,7 +48,7 @@ SOURCE_FILE@0..389 DOT@73..74 "." PATH_SEGMENT_REF@74..77 IDENT@74..77 "bar" - PARAM_LIST@77..146 + PARAM_LIST@77..201 L_PAREN@77..78 "(" WHITESPACE@78..87 "\n " PARAM@87..101 @@ -77,167 +77,203 @@ SOURCE_FILE@0..389 INTEGER_KW@119..126 "integer" COMMA@126..127 "," WHITESPACE@127..136 "\n " - PARAM@136..140 - PATH_TYPE@136..140 - PATH_REF@136..140 - PATH_SEGMENT_REF@136..140 - TEXT_KW@136..140 "text" - WHITESPACE@140..145 "\n " - R_PAREN@145..146 ")" - COMMA@146..147 "," - WHITESPACE@147..153 " \n " - AGGREGATE@153..157 - PATH_REF@153..154 - PATH_SEGMENT_REF@153..154 - IDENT@153..154 "c" - PARAM_LIST@154..157 - L_PAREN@154..155 "(" - STAR@155..156 "*" - R_PAREN@156..157 ")" - WHITESPACE@157..160 "\n " - CASCADE@160..167 - CASCADE_KW@160..167 "cascade" - SEMICOLON@167..168 ";" - WHITESPACE@168..170 "\n\n" - COMMENT@170..182 "-- aggregate" - WHITESPACE@182..183 "\n" - DROP_AGGREGATE@183..300 - DROP_KW@183..187 "drop" - WHITESPACE@187..188 " " - AGGREGATE_KW@188..197 "aggregate" - WHITESPACE@197..198 " " - AGGREGATE@198..290 - PATH_REF@198..199 - PATH_SEGMENT_REF@198..199 - IDENT@198..199 "a" - PARAM_LIST@199..290 - L_PAREN@199..200 "(" - WHITESPACE@200..203 "\n " - PARAM@203..210 - PATH_TYPE@203..210 - PATH_REF@203..210 - PATH_SEGMENT_REF@203..210 - INTEGER_KW@203..210 "integer" - COMMA@210..211 "," - WHITESPACE@211..214 "\n " - PARAM@214..218 - PATH_TYPE@214..218 - PATH_REF@214..218 - PATH_SEGMENT_REF@214..218 - TEXT_KW@214..218 "text" - COMMA@218..219 "," - WHITESPACE@219..222 "\n " - PARAM@222..229 - PATH_TYPE@222..229 - PATH_REF@222..229 - PATH_SEGMENT_REF@222..229 - NUMERIC_KW@222..229 "numeric" - WHITESPACE@229..232 "\n " - AGGREGATE_ORDER_BY@232..288 - ORDER_KW@232..237 "order" - WHITESPACE@237..238 " " - BY_KW@238..240 "by" - WHITESPACE@240..245 "\n " - PARAM@245..259 - PARAM_IN@245..247 - IN_KW@245..247 "in" - WHITESPACE@247..248 " " - PARAM_NAME@248..249 - IDENT@248..249 "a" - WHITESPACE@249..250 " " - TIMESTAMP_TYPE@250..259 - TIMESTAMP_KW@250..259 "timestamp" - COMMA@259..260 "," - WHITESPACE@260..265 "\n " - PARAM@265..278 - PARAM_OUT@265..268 - OUT_KW@265..268 "out" - WHITESPACE@268..269 " " - PARAM_NAME@269..270 - IDENT@269..270 "b" - WHITESPACE@270..271 " " - PATH_TYPE@271..278 - PATH_REF@271..278 - PATH_SEGMENT_REF@271..278 - NUMERIC_KW@271..278 "numeric" - COMMA@278..279 "," - WHITESPACE@279..284 "\n " - PARAM@284..288 - PATH_TYPE@284..288 - PATH_REF@284..288 - PATH_SEGMENT_REF@284..288 - TEXT_KW@284..288 "text" - WHITESPACE@288..289 "\n" - R_PAREN@289..290 ")" - WHITESPACE@290..291 " " - RESTRICT@291..299 - RESTRICT_KW@291..299 "restrict" - SEMICOLON@299..300 ";" - WHITESPACE@300..302 "\n\n" - DROP_AGGREGATE@302..387 - DROP_KW@302..306 "drop" - WHITESPACE@306..307 " " - AGGREGATE_KW@307..316 "aggregate" - WHITESPACE@316..317 " " - AGGREGATE@317..386 - PATH_REF@317..324 - PATH_REF@317..320 - PATH_SEGMENT_REF@317..320 - IDENT@317..320 "foo" - DOT@320..321 "." - PATH_SEGMENT_REF@321..324 - IDENT@321..324 "bar" - PARAM_LIST@324..386 - L_PAREN@324..325 "(" - WHITESPACE@325..328 "\n " - AGGREGATE_ORDER_BY@328..384 - ORDER_KW@328..333 "order" - WHITESPACE@333..334 " " - BY_KW@334..336 "by" - WHITESPACE@336..341 "\n " - PARAM@341..355 - PARAM_IN@341..343 - IN_KW@341..343 "in" - WHITESPACE@343..344 " " - PARAM_NAME@344..345 - IDENT@344..345 "a" - WHITESPACE@345..346 " " - TIMESTAMP_TYPE@346..355 - TIMESTAMP_KW@346..355 "timestamp" - COMMA@355..356 "," - WHITESPACE@356..361 "\n " - PARAM@361..374 - PARAM_OUT@361..364 - OUT_KW@361..364 "out" - WHITESPACE@364..365 " " - PARAM_NAME@365..366 - IDENT@365..366 "b" - WHITESPACE@366..367 " " - PATH_TYPE@367..374 - PATH_REF@367..374 - PATH_SEGMENT_REF@367..374 - NUMERIC_KW@367..374 "numeric" - COMMA@374..375 "," - WHITESPACE@375..380 "\n " - PARAM@380..384 - PATH_TYPE@380..384 - PATH_REF@380..384 - PATH_SEGMENT_REF@380..384 - TEXT_KW@380..384 "text" - WHITESPACE@384..385 "\n" - R_PAREN@385..386 ")" - SEMICOLON@386..387 ";" - WHITESPACE@387..389 "\n\n" + PARAM@136..153 + PARAM_IN_OUT@136..141 + INOUT_KW@136..141 "inout" + WHITESPACE@141..142 " " + PARAM_NAME@142..145 + IDENT@142..145 "baz" + WHITESPACE@145..146 " " + PATH_TYPE@146..153 + PATH_REF@146..153 + PATH_SEGMENT_REF@146..153 + INTEGER_KW@146..153 "integer" + COMMA@153..154 "," + WHITESPACE@154..163 "\n " + PARAM@163..181 + PARAM_IN_OUT@163..169 + IN_KW@163..165 "in" + WHITESPACE@165..166 " " + OUT_KW@166..169 "out" + WHITESPACE@169..170 " " + PARAM_NAME@170..173 + IDENT@170..173 "qux" + WHITESPACE@173..174 " " + PATH_TYPE@174..181 + PATH_REF@174..181 + PATH_SEGMENT_REF@174..181 + INTEGER_KW@174..181 "integer" + COMMA@181..182 "," + WHITESPACE@182..191 "\n " + PARAM@191..195 + PATH_TYPE@191..195 + PATH_REF@191..195 + PATH_SEGMENT_REF@191..195 + TEXT_KW@191..195 "text" + WHITESPACE@195..200 "\n " + R_PAREN@200..201 ")" + COMMA@201..202 "," + WHITESPACE@202..208 " \n " + AGGREGATE@208..212 + PATH_REF@208..209 + PATH_SEGMENT_REF@208..209 + IDENT@208..209 "c" + PARAM_LIST@209..212 + L_PAREN@209..210 "(" + STAR@210..211 "*" + R_PAREN@211..212 ")" + WHITESPACE@212..215 "\n " + CASCADE@215..222 + CASCADE_KW@215..222 "cascade" + SEMICOLON@222..223 ";" + WHITESPACE@223..225 "\n\n" + COMMENT@225..237 "-- aggregate" + WHITESPACE@237..238 "\n" + DROP_AGGREGATE@238..355 + DROP_KW@238..242 "drop" + WHITESPACE@242..243 " " + AGGREGATE_KW@243..252 "aggregate" + WHITESPACE@252..253 " " + AGGREGATE@253..345 + PATH_REF@253..254 + PATH_SEGMENT_REF@253..254 + IDENT@253..254 "a" + PARAM_LIST@254..345 + L_PAREN@254..255 "(" + WHITESPACE@255..258 "\n " + PARAM@258..265 + PATH_TYPE@258..265 + PATH_REF@258..265 + PATH_SEGMENT_REF@258..265 + INTEGER_KW@258..265 "integer" + COMMA@265..266 "," + WHITESPACE@266..269 "\n " + PARAM@269..273 + PATH_TYPE@269..273 + PATH_REF@269..273 + PATH_SEGMENT_REF@269..273 + TEXT_KW@269..273 "text" + COMMA@273..274 "," + WHITESPACE@274..277 "\n " + PARAM@277..284 + PATH_TYPE@277..284 + PATH_REF@277..284 + PATH_SEGMENT_REF@277..284 + NUMERIC_KW@277..284 "numeric" + WHITESPACE@284..287 "\n " + AGGREGATE_ORDER_BY@287..343 + ORDER_KW@287..292 "order" + WHITESPACE@292..293 " " + BY_KW@293..295 "by" + WHITESPACE@295..300 "\n " + PARAM@300..314 + PARAM_IN@300..302 + IN_KW@300..302 "in" + WHITESPACE@302..303 " " + PARAM_NAME@303..304 + IDENT@303..304 "a" + WHITESPACE@304..305 " " + TIMESTAMP_TYPE@305..314 + TIMESTAMP_KW@305..314 "timestamp" + COMMA@314..315 "," + WHITESPACE@315..320 "\n " + PARAM@320..333 + PARAM_OUT@320..323 + OUT_KW@320..323 "out" + WHITESPACE@323..324 " " + PARAM_NAME@324..325 + IDENT@324..325 "b" + WHITESPACE@325..326 " " + PATH_TYPE@326..333 + PATH_REF@326..333 + PATH_SEGMENT_REF@326..333 + NUMERIC_KW@326..333 "numeric" + COMMA@333..334 "," + WHITESPACE@334..339 "\n " + PARAM@339..343 + PATH_TYPE@339..343 + PATH_REF@339..343 + PATH_SEGMENT_REF@339..343 + TEXT_KW@339..343 "text" + WHITESPACE@343..344 "\n" + R_PAREN@344..345 ")" + WHITESPACE@345..346 " " + RESTRICT@346..354 + RESTRICT_KW@346..354 "restrict" + SEMICOLON@354..355 ";" + WHITESPACE@355..357 "\n\n" + DROP_AGGREGATE@357..442 + DROP_KW@357..361 "drop" + WHITESPACE@361..362 " " + AGGREGATE_KW@362..371 "aggregate" + WHITESPACE@371..372 " " + AGGREGATE@372..441 + PATH_REF@372..379 + PATH_REF@372..375 + PATH_SEGMENT_REF@372..375 + IDENT@372..375 "foo" + DOT@375..376 "." + PATH_SEGMENT_REF@376..379 + IDENT@376..379 "bar" + PARAM_LIST@379..441 + L_PAREN@379..380 "(" + WHITESPACE@380..383 "\n " + AGGREGATE_ORDER_BY@383..439 + ORDER_KW@383..388 "order" + WHITESPACE@388..389 " " + BY_KW@389..391 "by" + WHITESPACE@391..396 "\n " + PARAM@396..410 + PARAM_IN@396..398 + IN_KW@396..398 "in" + WHITESPACE@398..399 " " + PARAM_NAME@399..400 + IDENT@399..400 "a" + WHITESPACE@400..401 " " + TIMESTAMP_TYPE@401..410 + TIMESTAMP_KW@401..410 "timestamp" + COMMA@410..411 "," + WHITESPACE@411..416 "\n " + PARAM@416..429 + PARAM_OUT@416..419 + OUT_KW@416..419 "out" + WHITESPACE@419..420 " " + PARAM_NAME@420..421 + IDENT@420..421 "b" + WHITESPACE@421..422 " " + PATH_TYPE@422..429 + PATH_REF@422..429 + PATH_SEGMENT_REF@422..429 + NUMERIC_KW@422..429 "numeric" + COMMA@429..430 "," + WHITESPACE@430..435 "\n " + PARAM@435..439 + PATH_TYPE@435..439 + PATH_REF@435..439 + PATH_SEGMENT_REF@435..439 + TEXT_KW@435..439 "text" + WHITESPACE@439..440 "\n" + R_PAREN@440..441 ")" + SEMICOLON@441..442 ";" + WHITESPACE@442..444 "\n\n" error[syntax-error]: Out params are not allowed with aggregates. ╭▸ 9 │ out bar integer, ╰╴ ━━━ +error[syntax-error]: In Out params are not allowed with aggregates. + ╭▸ +10 │ inout baz integer, + ╰╴ ━━━━━ +error[syntax-error]: In Out params are not allowed with aggregates. + ╭▸ +11 │ in out qux integer, + ╰╴ ━━━━━━ error[syntax-error]: Out params are not allowed with aggregates. ╭▸ -22 │ out b numeric, +24 │ out b numeric, ╰╴ ━━━ error[syntax-error]: Out params are not allowed with aggregates. ╭▸ -29 │ out b numeric, +31 │ out b numeric, ╰╴ ━━━ diff --git a/crates/squawk_syntax/src/snapshots/squawk_syntax__test__function_table_params_validation.snap b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__function_table_params_validation.snap new file mode 100644 index 000000000..fa20b8e7c --- /dev/null +++ b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__function_table_params_validation.snap @@ -0,0 +1,188 @@ +--- +source: crates/squawk_syntax/src/test.rs +input_file: crates/squawk_syntax/test_data/validation/function_table_params.sql +--- +SOURCE_FILE@0..256 + CREATE_FUNCTION@0..82 + CREATE_KW@0..6 "create" + WHITESPACE@6..7 " " + FUNCTION_KW@7..15 "function" + WHITESPACE@15..16 " " + FUNCTION_NAME@16..17 + PATH@16..17 + PATH_SEGMENT@16..17 + IDENT@16..17 "f" + PARAM_LIST@17..28 + L_PAREN@17..18 "(" + PARAM@18..27 + PARAM_OUT@18..21 + OUT_KW@18..21 "out" + WHITESPACE@21..22 " " + PARAM_NAME@22..23 + IDENT@22..23 "x" + WHITESPACE@23..24 " " + PATH_TYPE@24..27 + PATH_REF@24..27 + PATH_SEGMENT_REF@24..27 + INT_KW@24..27 "int" + R_PAREN@27..28 ")" + WHITESPACE@28..29 " " + RET_TYPE@29..50 + RETURNS_KW@29..36 "returns" + WHITESPACE@36..37 " " + TABLE_KW@37..42 "table" + WHITESPACE@42..43 " " + RETURN_TABLE_ARG_LIST@43..50 + L_PAREN@43..44 "(" + RETURN_TABLE_COLUMN@44..49 + COLUMN_NAME@44..45 + IDENT@44..45 "y" + WHITESPACE@45..46 " " + PATH_TYPE@46..49 + PATH_REF@46..49 + PATH_SEGMENT_REF@46..49 + INT_KW@46..49 "int" + R_PAREN@49..50 ")" + WHITESPACE@50..51 " " + FUNC_OPTION_LIST@51..81 + LANGUAGE_FUNC_OPTION@51..63 + LANGUAGE_KW@51..59 "language" + WHITESPACE@59..60 " " + LANGUAGE_REF@60..63 + SQL_KW@60..63 "sql" + WHITESPACE@63..64 " " + AS_FUNC_OPTION@64..81 + AS_KW@64..66 "as" + WHITESPACE@66..67 " " + AS_DEFINITION@67..81 + LITERAL@67..81 + DOLLAR_QUOTED_STRING@67..81 "$$ select 1 $$" + SEMICOLON@81..82 ";" + WHITESPACE@82..84 "\n\n" + CREATE_FUNCTION@84..168 + CREATE_KW@84..90 "create" + WHITESPACE@90..91 " " + FUNCTION_KW@91..99 "function" + WHITESPACE@99..100 " " + FUNCTION_NAME@100..101 + PATH@100..101 + PATH_SEGMENT@100..101 + IDENT@100..101 "f" + PARAM_LIST@101..114 + L_PAREN@101..102 "(" + PARAM@102..113 + PARAM_IN_OUT@102..107 + INOUT_KW@102..107 "inout" + WHITESPACE@107..108 " " + PARAM_NAME@108..109 + IDENT@108..109 "x" + WHITESPACE@109..110 " " + PATH_TYPE@110..113 + PATH_REF@110..113 + PATH_SEGMENT_REF@110..113 + INT_KW@110..113 "int" + R_PAREN@113..114 ")" + WHITESPACE@114..115 " " + RET_TYPE@115..136 + RETURNS_KW@115..122 "returns" + WHITESPACE@122..123 " " + TABLE_KW@123..128 "table" + WHITESPACE@128..129 " " + RETURN_TABLE_ARG_LIST@129..136 + L_PAREN@129..130 "(" + RETURN_TABLE_COLUMN@130..135 + COLUMN_NAME@130..131 + IDENT@130..131 "y" + WHITESPACE@131..132 " " + PATH_TYPE@132..135 + PATH_REF@132..135 + PATH_SEGMENT_REF@132..135 + INT_KW@132..135 "int" + R_PAREN@135..136 ")" + WHITESPACE@136..137 " " + FUNC_OPTION_LIST@137..167 + LANGUAGE_FUNC_OPTION@137..149 + LANGUAGE_KW@137..145 "language" + WHITESPACE@145..146 " " + LANGUAGE_REF@146..149 + SQL_KW@146..149 "sql" + WHITESPACE@149..150 " " + AS_FUNC_OPTION@150..167 + AS_KW@150..152 "as" + WHITESPACE@152..153 " " + AS_DEFINITION@153..167 + LITERAL@153..167 + DOLLAR_QUOTED_STRING@153..167 "$$ select x $$" + SEMICOLON@167..168 ";" + WHITESPACE@168..170 "\n\n" + CREATE_FUNCTION@170..255 + CREATE_KW@170..176 "create" + WHITESPACE@176..177 " " + FUNCTION_KW@177..185 "function" + WHITESPACE@185..186 " " + FUNCTION_NAME@186..187 + PATH@186..187 + PATH_SEGMENT@186..187 + IDENT@186..187 "f" + PARAM_LIST@187..201 + L_PAREN@187..188 "(" + PARAM@188..200 + PARAM_IN_OUT@188..194 + IN_KW@188..190 "in" + WHITESPACE@190..191 " " + OUT_KW@191..194 "out" + WHITESPACE@194..195 " " + PARAM_NAME@195..196 + IDENT@195..196 "x" + WHITESPACE@196..197 " " + PATH_TYPE@197..200 + PATH_REF@197..200 + PATH_SEGMENT_REF@197..200 + INT_KW@197..200 "int" + R_PAREN@200..201 ")" + WHITESPACE@201..202 " " + RET_TYPE@202..223 + RETURNS_KW@202..209 "returns" + WHITESPACE@209..210 " " + TABLE_KW@210..215 "table" + WHITESPACE@215..216 " " + RETURN_TABLE_ARG_LIST@216..223 + L_PAREN@216..217 "(" + RETURN_TABLE_COLUMN@217..222 + COLUMN_NAME@217..218 + IDENT@217..218 "y" + WHITESPACE@218..219 " " + PATH_TYPE@219..222 + PATH_REF@219..222 + PATH_SEGMENT_REF@219..222 + INT_KW@219..222 "int" + R_PAREN@222..223 ")" + WHITESPACE@223..224 " " + FUNC_OPTION_LIST@224..254 + LANGUAGE_FUNC_OPTION@224..236 + LANGUAGE_KW@224..232 "language" + WHITESPACE@232..233 " " + LANGUAGE_REF@233..236 + SQL_KW@233..236 "sql" + WHITESPACE@236..237 " " + AS_FUNC_OPTION@237..254 + AS_KW@237..239 "as" + WHITESPACE@239..240 " " + AS_DEFINITION@240..254 + LITERAL@240..254 + DOLLAR_QUOTED_STRING@240..254 "$$ select x $$" + SEMICOLON@254..255 ";" + WHITESPACE@255..256 "\n" + +error[syntax-error]: OUT and INOUT arguments aren't allowed in TABLE functions + ╭▸ +1 │ create function f(out x int) returns table (y int) language sql as $$ select 1 $$; + ╰╴ ━━━ +error[syntax-error]: OUT and INOUT arguments aren't allowed in TABLE functions + ╭▸ +3 │ create function f(inout x int) returns table (y int) language sql as $$ select x $$; + ╰╴ ━━━━━ +error[syntax-error]: OUT and INOUT arguments aren't allowed in TABLE functions + ╭▸ +5 │ create function f(in out x int) returns table (y int) language sql as $$ select x $$; + ╰╴ ━━━━━━ diff --git a/crates/squawk_syntax/src/validation.rs b/crates/squawk_syntax/src/validation.rs index cadae76b1..fce109275 100644 --- a/crates/squawk_syntax/src/validation.rs +++ b/crates/squawk_syntax/src/validation.rs @@ -17,12 +17,12 @@ pub(crate) fn validate(root: &SyntaxNode, errors: &mut Vec) { for node in root.descendants() { match_ast! { match node { - ast::AlterAggregate(it) => validate_aggregate_params(it.aggregate().and_then(|x| x.param_list()), errors), + ast::Aggregate(it) => validate_aggregate_params(it.param_list(), errors), ast::AtomicBody(it) => validate_atomic_body(it, errors), ast::BinExpr(it) => validate_bin_expr(it, errors), ast::CastExpr(it) => validate_cast_expr(it, errors), ast::CreateAggregate(it) => validate_aggregate_params(it.param_list(), errors), - ast::CreateFunction(it) => validate_routine_body(it.option_list(), it.body(), errors), + ast::CreateFunction(it) => validate_create_function(it, errors), ast::CreateProcedure(it) => validate_routine_body(it.option_list(), it.body(), errors), ast::CreateTable(it) => validate_create_table(it, errors), ast::CreateViewLike(it) => validate_non_empty_column_list(it.column_list(), errors), @@ -34,7 +34,6 @@ pub(crate) fn validate(root: &SyntaxNode, errors: &mut Vec) { ast::WithTable(it) => validate_non_empty_column_list(it.column_list(), errors), 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), ast::NonStandardParam(it) => validate_non_standard_param(it, errors), @@ -807,12 +806,6 @@ fn validate_join_expr(join_expr: ast::JoinExpr, acc: &mut Vec) { } } -fn validate_drop_aggregate(drop_agg: ast::DropAggregate, acc: &mut Vec) { - for agg in drop_agg.aggregates() { - validate_aggregate_params(agg.param_list(), acc); - } -} - fn validate_array_expr(array_expr: ast::ArrayExpr, acc: &mut Vec) { if array_expr.array_token().is_none() { let parent_kind = array_expr.syntax().parent().map(|x| x.kind()); @@ -872,6 +865,36 @@ fn validate_custom_op(op: ast::CustomOp, acc: &mut Vec) { } } +fn validate_create_function(function: ast::CreateFunction, acc: &mut Vec) { + validate_routine_body(function.option_list(), function.body(), acc); + + let returns_table = function + .ret_type() + .is_some_and(|ret_type| ret_type.table_token().is_some()); + if !returns_table { + return; + } + + let Some(params) = function.param_list() else { + return; + }; + for param in params.all_params() { + let invalid_mode = match param.mode() { + Some(ast::ParamMode::ParamOut(mode)) => Some(mode.syntax().text_range()), + Some(ast::ParamMode::ParamInOut(mode)) => Some(mode.syntax().text_range()), + Some(ast::ParamMode::ParamIn(_)) | Some(ast::ParamMode::ParamVariadic(_)) | None => { + None + } + }; + if let Some(range) = invalid_mode { + acc.push(SyntaxError::new( + "OUT and INOUT arguments aren't allowed in TABLE functions", + range, + )); + } + } +} + fn validate_aggregate_params(aggregate_params: Option, acc: &mut Vec) { if let Some(params) = aggregate_params { for p in params.all_params() { diff --git a/crates/squawk_syntax/test_data/validation/aggregate_object_params.sql b/crates/squawk_syntax/test_data/validation/aggregate_object_params.sql new file mode 100644 index 000000000..cc0cc964f --- /dev/null +++ b/crates/squawk_syntax/test_data/validation/aggregate_object_params.sql @@ -0,0 +1,3 @@ +comment on aggregate a(inout int) is 'invalid'; + +security label on aggregate a(in out int) is 'invalid'; diff --git a/crates/squawk_syntax/test_data/validation/alter_aggregate_params.sql b/crates/squawk_syntax/test_data/validation/alter_aggregate_params.sql index 3ce44c795..f82bde48f 100644 --- a/crates/squawk_syntax/test_data/validation/alter_aggregate_params.sql +++ b/crates/squawk_syntax/test_data/validation/alter_aggregate_params.sql @@ -1,5 +1,5 @@ -- can't have out params in aggregates -create aggregate a(in x, out y) ( +create aggregate a(in x, out y, inout z, in out w) ( sfunc = f, stype = t ); diff --git a/crates/squawk_syntax/test_data/validation/create_aggregate_params.sql b/crates/squawk_syntax/test_data/validation/create_aggregate_params.sql index 9f07ea89d..3dddde013 100644 --- a/crates/squawk_syntax/test_data/validation/create_aggregate_params.sql +++ b/crates/squawk_syntax/test_data/validation/create_aggregate_params.sql @@ -1,4 +1,4 @@ -- can't have out params in aggregates -alter aggregate a (in t, out u) +alter aggregate a (in t, out u, inout v, in out w) set schema s; diff --git a/crates/squawk_syntax/test_data/validation/drop_aggregate_params.sql b/crates/squawk_syntax/test_data/validation/drop_aggregate_params.sql index 13c6e8b18..2e82890b9 100644 --- a/crates/squawk_syntax/test_data/validation/drop_aggregate_params.sql +++ b/crates/squawk_syntax/test_data/validation/drop_aggregate_params.sql @@ -7,6 +7,8 @@ drop aggregate foo.bar( in foo integer, out bar integer, + inout baz integer, + in out qux integer, text ), c(*) diff --git a/crates/squawk_syntax/test_data/validation/function_table_params.sql b/crates/squawk_syntax/test_data/validation/function_table_params.sql new file mode 100644 index 000000000..f5f7b7099 --- /dev/null +++ b/crates/squawk_syntax/test_data/validation/function_table_params.sql @@ -0,0 +1,5 @@ +create function f(out x int) returns table (y int) language sql as $$ select 1 $$; + +create function f(inout x int) returns table (y int) language sql as $$ select x $$; + +create function f(in out x int) returns table (y int) language sql as $$ select x $$; From 929699a46e1bd0ab76740bc8b6ec17fa1b7a2cd0 Mon Sep 17 00:00:00 2001 From: Steve Dignam Date: Mon, 31 Aug 2026 21:34:56 -0400 Subject: [PATCH 2/2] fix --- crates/squawk_fmt/tests/after/create_function.snap | 2 +- crates/squawk_fmt/tests/before/create_function.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/squawk_fmt/tests/after/create_function.snap b/crates/squawk_fmt/tests/after/create_function.snap index 68254582e..156b58e80 100644 --- a/crates/squawk_fmt/tests/after/create_function.snap +++ b/crates/squawk_fmt/tests/after/create_function.snap @@ -75,7 +75,7 @@ create function percent_type_table( -- comments in every position create /*a*/ or /*b*/ replace /*c*/ function /*d*/ app /*e*/./*f*/ commented( /*g*/ in /*h*/ value /*i*/ integer /*j*/ default /*k*/ 1 /*l*/, - /*m*/ out /*n*/ result /*o*/ text /*p*/ + /*m*/ in /*n*/ result /*o*/ text /*p*/ ) /*q*/ returns /*r*/ table /*s*/ ( /*t*/ id /*u*/ bigint /*v*/, /*w*/ label /*x*/ text /*y*/ diff --git a/crates/squawk_fmt/tests/before/create_function.sql b/crates/squawk_fmt/tests/before/create_function.sql index 70576cb3d..2d9bbd5d4 100644 --- a/crates/squawk_fmt/tests/before/create_function.sql +++ b/crates/squawk_fmt/tests/before/create_function.sql @@ -16,7 +16,7 @@ create function percent_type_table(unused integer) returns table (value accounts -- comments in every position create /*a*/ or /*b*/ replace /*c*/ function /*d*/ app /*e*/. /*f*/ commented -(/*g*/ in /*h*/ value /*i*/ integer /*j*/ default /*k*/ 1 /*l*/, /*m*/ out /*n*/ result /*o*/ text /*p*/) +(/*g*/ in /*h*/ value /*i*/ integer /*j*/ default /*k*/ 1 /*l*/, /*m*/ in /*n*/ result /*o*/ text /*p*/) /*q*/ returns /*r*/ table /*s*/ (/*t*/ id /*u*/ bigint /*v*/, /*w*/ label /*x*/ text /*y*/) /*z*/ language /*aa*/ sql /*ab*/ immutable