diff --git a/crates/squawk_parser/tests/data/ok/merge.sql b/crates/squawk_parser/tests/data/ok/merge.sql index da448883..f2d13d3f 100644 --- a/crates/squawk_parser/tests/data/ok/merge.sql +++ b/crates/squawk_parser/tests/data/ok/merge.sql @@ -150,8 +150,6 @@ merge into t b = 1, c = d, e = (select 1), - f = row(1, 2, default), - g = (1, 2, default), h = (default) when not matched by source then update set foo = bar; diff --git a/crates/squawk_parser/tests/data/ok/select.sql b/crates/squawk_parser/tests/data/ok/select.sql index 26381e9c..4ba845f9 100644 --- a/crates/squawk_parser/tests/data/ok/select.sql +++ b/crates/squawk_parser/tests/data/ok/select.sql @@ -464,7 +464,6 @@ select 1 collate; select foo.bar null; select foo.bar default; -select default; select c default; select null; select c null; diff --git a/crates/squawk_parser/tests/snapshots/tests__merge_ok.snap b/crates/squawk_parser/tests/snapshots/tests__merge_ok.snap index e021a975..f26bcf92 100644 --- a/crates/squawk_parser/tests/snapshots/tests__merge_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__merge_ok.snap @@ -1420,55 +1420,6 @@ SOURCE_FILE R_PAREN ")" COMMA "," WHITESPACE "\n " - SET_SINGLE_COLUMN - COLUMN - NAME_REF - IDENT "f" - WHITESPACE " " - EQ "=" - WHITESPACE " " - SET_EXPR - CALL_EXPR - NAME_REF - ROW_KW "row" - ARG_LIST - L_PAREN "(" - LITERAL - INT_NUMBER "1" - COMMA "," - WHITESPACE " " - LITERAL - INT_NUMBER "2" - COMMA "," - WHITESPACE " " - LITERAL - DEFAULT_KW "default" - R_PAREN ")" - COMMA "," - WHITESPACE "\n " - SET_SINGLE_COLUMN - COLUMN - NAME_REF - IDENT "g" - WHITESPACE " " - EQ "=" - WHITESPACE " " - SET_EXPR - TUPLE_EXPR - L_PAREN "(" - LITERAL - INT_NUMBER "1" - COMMA "," - WHITESPACE " " - LITERAL - INT_NUMBER "2" - COMMA "," - WHITESPACE " " - LITERAL - DEFAULT_KW "default" - R_PAREN ")" - COMMA "," - WHITESPACE "\n " SET_SINGLE_COLUMN COLUMN NAME_REF diff --git a/crates/squawk_parser/tests/snapshots/tests__select_ok.snap b/crates/squawk_parser/tests/snapshots/tests__select_ok.snap index b26d998f..f783cef5 100644 --- a/crates/squawk_parser/tests/snapshots/tests__select_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__select_ok.snap @@ -5863,16 +5863,6 @@ SOURCE_FILE DEFAULT_KW "default" SEMICOLON ";" WHITESPACE "\n\n" - SELECT - SELECT_CLAUSE - SELECT_KW "select" - WHITESPACE " " - TARGET_LIST - TARGET - LITERAL - DEFAULT_KW "default" - SEMICOLON ";" - WHITESPACE "\n" SELECT SELECT_CLAUSE SELECT_KW "select" diff --git a/crates/squawk_syntax/src/ast/generated/nodes.rs b/crates/squawk_syntax/src/ast/generated/nodes.rs index b414d1d6..d3b986de 100644 --- a/crates/squawk_syntax/src/ast/generated/nodes.rs +++ b/crates/squawk_syntax/src/ast/generated/nodes.rs @@ -3172,6 +3172,10 @@ impl Column { support::child(&self.syntax) } #[inline] + pub fn field_expr(&self) -> Option { + support::child(&self.syntax) + } + #[inline] pub fn index_expr(&self) -> Option { support::child(&self.syntax) } diff --git a/crates/squawk_syntax/src/postgresql.ungram b/crates/squawk_syntax/src/postgresql.ungram index 2cd387ad..47bd4cd6 100644 --- a/crates/squawk_syntax/src/postgresql.ungram +++ b/crates/squawk_syntax/src/postgresql.ungram @@ -675,6 +675,7 @@ Column = NotEnforced? Enforced? | 'period'? NameRef | IndexExpr + | FieldExpr ) ColumnConstraint = CheckConstraint diff --git a/crates/squawk_syntax/src/snapshots/squawk_syntax__test__default_ok_validation.snap b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__default_ok_validation.snap new file mode 100644 index 00000000..73f6cc0f --- /dev/null +++ b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__default_ok_validation.snap @@ -0,0 +1,388 @@ +--- +source: crates/squawk_syntax/src/test.rs +input_file: crates/squawk_syntax/test_data/validation/default_ok.sql +--- +SOURCE_FILE@0..492 + UPDATE@0..27 + UPDATE_KW@0..6 "UPDATE" + WHITESPACE@6..7 " " + RELATION_NAME@7..8 + PATH@7..8 + PATH_SEGMENT@7..8 + NAME_REF@7..8 + IDENT@7..8 "t" + WHITESPACE@8..9 " " + SET_CLAUSE@9..26 + SET_KW@9..12 "SET" + WHITESPACE@12..13 " " + SET_COLUMN_LIST@13..26 + SET_SINGLE_COLUMN@13..26 + COLUMN@13..16 + NAME_REF@13..16 + IDENT@13..16 "col" + WHITESPACE@16..17 " " + EQ@17..18 "=" + WHITESPACE@18..19 " " + SET_EXPR@19..26 + DEFAULT_KW@19..26 "DEFAULT" + SEMICOLON@26..27 ";" + WHITESPACE@27..29 "\n\n" + UPDATE@29..63 + UPDATE_KW@29..35 "UPDATE" + WHITESPACE@35..36 " " + RELATION_NAME@36..37 + PATH@36..37 + PATH_SEGMENT@36..37 + NAME_REF@36..37 + IDENT@36..37 "t" + WHITESPACE@37..38 " " + SET_CLAUSE@38..62 + SET_KW@38..41 "SET" + WHITESPACE@41..42 " " + SET_COLUMN_LIST@42..62 + SET_MULTIPLE_COLUMNS@42..62 + COLUMN_LIST@42..47 + L_PAREN@42..43 "(" + COLUMN@43..44 + NAME_REF@43..44 + IDENT@43..44 "a" + COMMA@44..45 "," + COLUMN@45..46 + NAME_REF@45..46 + IDENT@45..46 "b" + R_PAREN@46..47 ")" + WHITESPACE@47..48 " " + EQ@48..49 "=" + WHITESPACE@49..50 " " + SET_EXPR_LIST@50..62 + L_PAREN@50..51 "(" + SET_EXPR@51..58 + DEFAULT_KW@51..58 "DEFAULT" + COMMA@58..59 "," + WHITESPACE@59..60 " " + SET_EXPR@60..61 + LITERAL@60..61 + INT_NUMBER@60..61 "1" + R_PAREN@61..62 ")" + SEMICOLON@62..63 ";" + WHITESPACE@63..65 "\n\n" + INSERT@65..96 + INSERT_KW@65..71 "INSERT" + WHITESPACE@71..72 " " + INTO_KW@72..76 "into" + WHITESPACE@76..77 " " + PATH@77..78 + PATH_SEGMENT@77..78 + NAME_REF@77..78 + IDENT@77..78 "t" + WHITESPACE@78..79 " " + VALUES@79..95 + VALUES_KW@79..85 "VALUES" + WHITESPACE@85..86 " " + ROW_LIST@86..95 + ROW@86..95 + L_PAREN@86..87 "(" + LITERAL@87..94 + DEFAULT_KW@87..94 "DEFAULT" + R_PAREN@94..95 ")" + SEMICOLON@95..96 ";" + WHITESPACE@96..98 "\n\n" + MERGE@98..240 + MERGE_KW@98..103 "MERGE" + WHITESPACE@103..104 " " + INTO_KW@104..108 "INTO" + WHITESPACE@108..109 " " + RELATION_NAME@109..115 + PATH@109..115 + PATH_SEGMENT@109..115 + NAME_REF@109..115 + TARGET_KW@109..115 "target" + WHITESPACE@115..116 " " + ALIAS@116..117 + NAME@116..117 + IDENT@116..117 "t" + WHITESPACE@117..120 "\n " + USING_ON_CLAUSE@120..154 + USING_KW@120..125 "USING" + WHITESPACE@125..126 " " + FROM_ITEM@126..134 + NAME_REF@126..132 + SOURCE_KW@126..132 "source" + WHITESPACE@132..133 " " + ALIAS@133..134 + NAME@133..134 + IDENT@133..134 "s" + WHITESPACE@134..140 "\n " + ON_CLAUSE@140..154 + ON_KW@140..142 "ON" + WHITESPACE@142..143 " " + BIN_EXPR@143..154 + FIELD_EXPR@143..147 + NAME_REF@143..144 + IDENT@143..144 "t" + DOT@144..145 "." + NAME_REF@145..147 + IDENT@145..147 "id" + WHITESPACE@147..148 " " + EQ@148..149 "=" + WHITESPACE@149..150 " " + FIELD_EXPR@150..154 + NAME_REF@150..151 + IDENT@150..151 "s" + DOT@151..152 "." + NAME_REF@152..154 + IDENT@152..154 "id" + WHITESPACE@154..157 "\n " + MERGE_WHEN_NOT_MATCHED_TARGET@157..239 + WHEN_KW@157..161 "WHEN" + WHITESPACE@161..162 " " + NOT_KW@162..165 "NOT" + WHITESPACE@165..166 " " + MATCHED_KW@166..173 "MATCHED" + WHITESPACE@173..174 " " + THEN_KW@174..178 "THEN" + WHITESPACE@178..185 "\n " + MERGE_INSERT@185..239 + INSERT_KW@185..191 "INSERT" + WHITESPACE@191..192 " " + COLUMN_LIST@192..207 + L_PAREN@192..193 "(" + COLUMN@193..195 + NAME_REF@193..195 + IDENT@193..195 "id" + COMMA@195..196 "," + WHITESPACE@196..197 " " + COLUMN@197..201 + NAME_REF@197..201 + NAME_KW@197..201 "name" + COMMA@201..202 "," + WHITESPACE@202..203 " " + COLUMN@203..206 + NAME_REF@203..206 + IDENT@203..206 "qty" + R_PAREN@206..207 ")" + WHITESPACE@207..208 " " + VALUES@208..239 + VALUES_KW@208..214 "VALUES" + WHITESPACE@214..215 " " + ROW_LIST@215..239 + ROW@215..239 + L_PAREN@215..216 "(" + FIELD_EXPR@216..220 + NAME_REF@216..217 + IDENT@216..217 "s" + DOT@217..218 "." + NAME_REF@218..220 + IDENT@218..220 "id" + COMMA@220..221 "," + WHITESPACE@221..222 " " + LITERAL@222..229 + DEFAULT_KW@222..229 "DEFAULT" + COMMA@229..230 "," + WHITESPACE@230..231 " " + LITERAL@231..238 + DEFAULT_KW@231..238 "DEFAULT" + R_PAREN@238..239 ")" + SEMICOLON@239..240 ";" + WHITESPACE@240..242 "\n\n" + MERGE@242..351 + MERGE_KW@242..247 "MERGE" + WHITESPACE@247..248 " " + INTO_KW@248..252 "INTO" + WHITESPACE@252..253 " " + RELATION_NAME@253..259 + PATH@253..259 + PATH_SEGMENT@253..259 + NAME_REF@253..259 + TARGET_KW@253..259 "target" + WHITESPACE@259..260 " " + ALIAS@260..261 + NAME@260..261 + IDENT@260..261 "t" + WHITESPACE@261..264 "\n " + USING_ON_CLAUSE@264..298 + USING_KW@264..269 "USING" + WHITESPACE@269..270 " " + FROM_ITEM@270..278 + NAME_REF@270..276 + SOURCE_KW@270..276 "source" + WHITESPACE@276..277 " " + ALIAS@277..278 + NAME@277..278 + IDENT@277..278 "s" + WHITESPACE@278..284 "\n " + ON_CLAUSE@284..298 + ON_KW@284..286 "ON" + WHITESPACE@286..287 " " + BIN_EXPR@287..298 + FIELD_EXPR@287..291 + NAME_REF@287..288 + IDENT@287..288 "t" + DOT@288..289 "." + NAME_REF@289..291 + IDENT@289..291 "id" + WHITESPACE@291..292 " " + EQ@292..293 "=" + WHITESPACE@293..294 " " + FIELD_EXPR@294..298 + NAME_REF@294..295 + IDENT@294..295 "s" + DOT@295..296 "." + NAME_REF@296..298 + IDENT@296..298 "id" + WHITESPACE@298..301 "\n " + MERGE_WHEN_MATCHED@301..350 + WHEN_KW@301..305 "WHEN" + WHITESPACE@305..306 " " + MATCHED_KW@306..313 "MATCHED" + WHITESPACE@313..314 " " + THEN_KW@314..318 "THEN" + WHITESPACE@318..325 "\n " + MERGE_UPDATE@325..350 + UPDATE_KW@325..331 "UPDATE" + WHITESPACE@331..332 " " + SET_CLAUSE@332..350 + SET_KW@332..335 "SET" + WHITESPACE@335..336 " " + SET_COLUMN_LIST@336..350 + SET_SINGLE_COLUMN@336..350 + COLUMN@336..340 + NAME_REF@336..340 + IDENT@336..340 "qty2" + WHITESPACE@340..341 " " + EQ@341..342 "=" + WHITESPACE@342..343 " " + SET_EXPR@343..350 + DEFAULT_KW@343..350 "DEFAULT" + SEMICOLON@350..351 ";" + WHITESPACE@351..353 "\n\n" + UPDATE@353..382 + UPDATE_KW@353..359 "UPDATE" + WHITESPACE@359..360 " " + RELATION_NAME@360..361 + PATH@360..361 + PATH_SEGMENT@360..361 + NAME_REF@360..361 + IDENT@360..361 "t" + WHITESPACE@361..362 " " + SET_CLAUSE@362..381 + SET_KW@362..365 "SET" + WHITESPACE@365..366 " " + SET_COLUMN_LIST@366..381 + SET_SINGLE_COLUMN@366..381 + COLUMN@366..369 + NAME_REF@366..369 + IDENT@366..369 "col" + WHITESPACE@369..370 " " + EQ@370..371 "=" + WHITESPACE@371..372 " " + SET_EXPR@372..381 + PAREN_EXPR@372..381 + L_PAREN@372..373 "(" + LITERAL@373..380 + DEFAULT_KW@373..380 "DEFAULT" + R_PAREN@380..381 ")" + SEMICOLON@381..382 ";" + WHITESPACE@382..384 "\n\n" + UPDATE@384..417 + UPDATE_KW@384..390 "UPDATE" + WHITESPACE@390..391 " " + RELATION_NAME@391..392 + PATH@391..392 + PATH_SEGMENT@391..392 + NAME_REF@391..392 + IDENT@391..392 "t" + WHITESPACE@392..393 " " + SET_CLAUSE@393..416 + SET_KW@393..396 "SET" + WHITESPACE@396..397 " " + SET_COLUMN_LIST@397..416 + SET_SINGLE_COLUMN@397..416 + COLUMN@397..400 + NAME_REF@397..400 + IDENT@397..400 "col" + WHITESPACE@400..401 " " + EQ@401..402 "=" + WHITESPACE@402..403 " " + SET_EXPR@403..416 + PAREN_EXPR@403..416 + L_PAREN@403..404 "(" + PAREN_EXPR@404..415 + L_PAREN@404..405 "(" + PAREN_EXPR@405..414 + L_PAREN@405..406 "(" + LITERAL@406..413 + DEFAULT_KW@406..413 "DEFAULT" + R_PAREN@413..414 ")" + R_PAREN@414..415 ")" + R_PAREN@415..416 ")" + SEMICOLON@416..417 ";" + WHITESPACE@417..419 "\n\n" + UPDATE@419..456 + UPDATE_KW@419..425 "UPDATE" + WHITESPACE@425..426 " " + RELATION_NAME@426..427 + PATH@426..427 + PATH_SEGMENT@426..427 + NAME_REF@426..427 + IDENT@426..427 "t" + WHITESPACE@427..428 " " + SET_CLAUSE@428..455 + SET_KW@428..431 "SET" + WHITESPACE@431..432 " " + SET_COLUMN_LIST@432..455 + SET_MULTIPLE_COLUMNS@432..455 + COLUMN_LIST@432..438 + L_PAREN@432..433 "(" + COLUMN@433..434 + NAME_REF@433..434 + IDENT@433..434 "a" + COMMA@434..435 "," + WHITESPACE@435..436 " " + COLUMN@436..437 + NAME_REF@436..437 + IDENT@436..437 "b" + R_PAREN@437..438 ")" + WHITESPACE@438..439 " " + EQ@439..440 "=" + WHITESPACE@440..441 " " + SET_EXPR_LIST@441..455 + L_PAREN@441..442 "(" + SET_EXPR@442..451 + PAREN_EXPR@442..451 + L_PAREN@442..443 "(" + LITERAL@443..450 + DEFAULT_KW@443..450 "DEFAULT" + R_PAREN@450..451 ")" + COMMA@451..452 "," + WHITESPACE@452..453 " " + SET_EXPR@453..454 + LITERAL@453..454 + INT_NUMBER@453..454 "1" + R_PAREN@454..455 ")" + SEMICOLON@455..456 ";" + WHITESPACE@456..458 "\n\n" + INSERT@458..491 + INSERT_KW@458..464 "INSERT" + WHITESPACE@464..465 " " + INTO_KW@465..469 "INTO" + WHITESPACE@469..470 " " + PATH@470..471 + PATH_SEGMENT@470..471 + NAME_REF@470..471 + IDENT@470..471 "t" + WHITESPACE@471..472 " " + VALUES@472..490 + VALUES_KW@472..478 "VALUES" + WHITESPACE@478..479 " " + ROW_LIST@479..490 + ROW@479..490 + L_PAREN@479..480 "(" + PAREN_EXPR@480..489 + L_PAREN@480..481 "(" + LITERAL@481..488 + DEFAULT_KW@481..488 "DEFAULT" + R_PAREN@488..489 ")" + R_PAREN@489..490 ")" + SEMICOLON@490..491 ";" + WHITESPACE@491..492 "\n" diff --git a/crates/squawk_syntax/src/snapshots/squawk_syntax__test__default_validation.snap b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__default_validation.snap new file mode 100644 index 00000000..3ba19249 --- /dev/null +++ b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__default_validation.snap @@ -0,0 +1,157 @@ +--- +source: crates/squawk_syntax/src/test.rs +input_file: crates/squawk_syntax/test_data/validation/default.sql +--- +SOURCE_FILE@0..170 + INSERT@0..36 + INSERT_KW@0..6 "insert" + WHITESPACE@6..7 " " + INTO_KW@7..11 "into" + WHITESPACE@11..12 " " + PATH@12..13 + PATH_SEGMENT@12..13 + NAME_REF@12..13 + IDENT@12..13 "t" + WHITESPACE@13..14 " " + VALUES@14..35 + VALUES_KW@14..20 "VALUES" + WHITESPACE@20..21 " " + ROW_LIST@21..35 + ROW@21..35 + L_PAREN@21..22 "(" + CALL_EXPR@22..34 + NAME_REF@22..25 + IDENT@22..25 "foo" + ARG_LIST@25..34 + L_PAREN@25..26 "(" + LITERAL@26..33 + DEFAULT_KW@26..33 "DEFAULT" + R_PAREN@33..34 ")" + R_PAREN@34..35 ")" + SEMICOLON@35..36 ";" + WHITESPACE@36..38 "\n\n" + UPDATE@38..68 + UPDATE_KW@38..44 "UPDATE" + WHITESPACE@44..45 " " + RELATION_NAME@45..46 + PATH@45..46 + PATH_SEGMENT@45..46 + NAME_REF@45..46 + IDENT@45..46 "t" + WHITESPACE@46..47 " " + SET_CLAUSE@47..67 + SET_KW@47..50 "SET" + WHITESPACE@50..51 " " + SET_COLUMN_LIST@51..67 + SET_SINGLE_COLUMN@51..67 + COLUMN@51..57 + INDEX_EXPR@51..57 + NAME_REF@51..54 + IDENT@51..54 "arr" + L_BRACK@54..55 "[" + LITERAL@55..56 + INT_NUMBER@55..56 "1" + R_BRACK@56..57 "]" + WHITESPACE@57..58 " " + EQ@58..59 "=" + WHITESPACE@59..60 " " + SET_EXPR@60..67 + DEFAULT_KW@60..67 "DEFAULT" + SEMICOLON@67..68 ";" + WHITESPACE@68..70 "\n\n" + UPDATE@70..104 + UPDATE_KW@70..76 "UPDATE" + WHITESPACE@76..77 " " + RELATION_NAME@77..78 + PATH@77..78 + PATH_SEGMENT@77..78 + NAME_REF@77..78 + IDENT@77..78 "t" + WHITESPACE@78..79 " " + SET_CLAUSE@79..103 + SET_KW@79..82 "SET" + WHITESPACE@82..83 " " + SET_COLUMN_LIST@83..103 + SET_SINGLE_COLUMN@83..103 + COLUMN@83..93 + FIELD_EXPR@83..93 + NAME_REF@83..87 + IDENT@83..87 "comp" + DOT@87..88 "." + NAME_REF@88..93 + IDENT@88..93 "field" + WHITESPACE@93..94 " " + EQ@94..95 "=" + WHITESPACE@95..96 " " + SET_EXPR@96..103 + DEFAULT_KW@96..103 "DEFAULT" + SEMICOLON@103..104 ";" + WHITESPACE@104..106 "\n\n" + VALUES@106..123 + VALUES_KW@106..112 "values" + WHITESPACE@112..113 " " + ROW_LIST@113..122 + ROW@113..122 + L_PAREN@113..114 "(" + LITERAL@114..121 + DEFAULT_KW@114..121 "default" + R_PAREN@121..122 ")" + SEMICOLON@122..123 ";" + WHITESPACE@123..125 "\n\n" + SELECT@125..140 + SELECT_CLAUSE@125..139 + SELECT_KW@125..131 "select" + WHITESPACE@131..132 " " + TARGET_LIST@132..139 + TARGET@132..139 + LITERAL@132..139 + DEFAULT_KW@132..139 "default" + SEMICOLON@139..140 ";" + WHITESPACE@140..142 "\n\n" + SELECT@142..169 + SELECT_CLAUSE@142..150 + SELECT_KW@142..148 "select" + WHITESPACE@148..149 " " + TARGET_LIST@149..150 + TARGET@149..150 + LITERAL@149..150 + INT_NUMBER@149..150 "1" + WHITESPACE@150..151 " " + WHERE_CLAUSE@151..168 + WHERE_KW@151..156 "where" + WHITESPACE@156..157 " " + BIN_EXPR@157..168 + LITERAL@157..164 + DEFAULT_KW@157..164 "default" + WHITESPACE@164..165 " " + EQ@165..166 "=" + WHITESPACE@166..167 " " + LITERAL@167..168 + INT_NUMBER@167..168 "1" + SEMICOLON@168..169 ";" + WHITESPACE@169..170 "\n" + +error[syntax-error]: DEFAULT is not allowed in this context + ╭▸ +1 │ insert into t VALUES (foo(DEFAULT)); + ╰╴ ━━━━━━━ +error[syntax-error]: DEFAULT may only assign to a simple column name + ╭▸ +3 │ UPDATE t SET arr[1] = DEFAULT; + ╰╴ ━━━━━━ +error[syntax-error]: DEFAULT may only assign to a simple column name + ╭▸ +5 │ UPDATE t SET comp.field = DEFAULT; + ╰╴ ━━━━━━━━━━ +error[syntax-error]: DEFAULT is not allowed in this context + ╭▸ +7 │ values (default); + ╰╴ ━━━━━━━ +error[syntax-error]: DEFAULT is not allowed in this context + ╭▸ +9 │ select default; + ╰╴ ━━━━━━━ +error[syntax-error]: DEFAULT is not allowed in this context + ╭▸ +11 │ select 1 where default = 1; + ╰╴ ━━━━━━━ diff --git a/crates/squawk_syntax/src/validation.rs b/crates/squawk_syntax/src/validation.rs index 6aa43ba4..affb9416 100644 --- a/crates/squawk_syntax/src/validation.rs +++ b/crates/squawk_syntax/src/validation.rs @@ -6,7 +6,7 @@ use std::ops::Range; -use crate::ast::AstNode; +use crate::ast::{AstNode, LitKind}; use crate::unescape::{escape_unicode_esc_str, uescape_char}; use crate::{SyntaxNode, SyntaxToken, ast, match_ast, syntax_error::SyntaxError}; use rowan::{TextRange, TextSize}; @@ -29,6 +29,7 @@ pub(crate) fn validate(root: &SyntaxNode, errors: &mut Vec) { ast::RuleStmtList(it) => validate_rule_stmt_list(it, errors), ast::Select(it) => validate_select(it, errors), ast::SelectInto(it) => validate_select_into(it, errors), + ast::SetSingleColumn(it) => validate_set_single_column(it, errors), ast::SourceFile(it) => validate_source_file(it, errors), _ => (), } @@ -245,6 +246,62 @@ fn validate_literal(lit: ast::Literal, acc: &mut Vec) { validate_unicode_esc_string(&lit, acc); validate_prefixed_strings(&lit, acc); + validate_default_literal(&lit, acc); +} + +fn validate_default_literal(lit: &ast::Literal, acc: &mut Vec) { + if !matches!(lit.kind(), Some(LitKind::Default(_))) { + return; + } + let node = lit.syntax(); + if is_valid_default_literal_position(node) { + return; + } + acc.push(SyntaxError::new( + "DEFAULT is not allowed in this context", + node.text_range(), + )); +} + +fn is_valid_default_literal_position(literal: &SyntaxNode) -> bool { + for ancestor in literal.ancestors().skip(1) { + match ancestor.kind() { + // unwrap parens + PAREN_EXPR => continue, + SET_EXPR => return true, + ROW => return is_row_in_insert_values(&ancestor), + _ => return false, + } + } + false +} + +fn is_row_in_insert_values(row: &SyntaxNode) -> bool { + // row_list + row.parent() + // values + .and_then(|n| n.parent()) + // insert / merge_insert + .and_then(|n| n.parent()) + .is_some_and(|p| matches!(p.kind(), INSERT | MERGE_INSERT)) +} + +fn validate_set_single_column(it: ast::SetSingleColumn, acc: &mut Vec) { + let Some(set_expr) = it.set_expr() else { + return; + }; + if set_expr.default_token().is_none() { + return; + } + let Some(column) = it.column() else { + return; + }; + if column.index_expr().is_some() || column.field_expr().is_some() { + acc.push(SyntaxError::new( + "DEFAULT may only assign to a simple column name", + column.syntax().text_range(), + )); + } } #[derive(Clone, Copy)] diff --git a/crates/squawk_syntax/test_data/validation/default.sql b/crates/squawk_syntax/test_data/validation/default.sql new file mode 100644 index 00000000..4f399b53 --- /dev/null +++ b/crates/squawk_syntax/test_data/validation/default.sql @@ -0,0 +1,11 @@ +insert into t VALUES (foo(DEFAULT)); + +UPDATE t SET arr[1] = DEFAULT; + +UPDATE t SET comp.field = DEFAULT; + +values (default); + +select default; + +select 1 where default = 1; diff --git a/crates/squawk_syntax/test_data/validation/default_ok.sql b/crates/squawk_syntax/test_data/validation/default_ok.sql new file mode 100644 index 00000000..9fd10e9f --- /dev/null +++ b/crates/squawk_syntax/test_data/validation/default_ok.sql @@ -0,0 +1,25 @@ +UPDATE t SET col = DEFAULT; + +UPDATE t SET (a,b) = (DEFAULT, 1); + +INSERT into t VALUES (DEFAULT); + +MERGE INTO target t + USING source s + ON t.id = s.id + WHEN NOT MATCHED THEN + INSERT (id, name, qty) VALUES (s.id, DEFAULT, DEFAULT); + +MERGE INTO target t + USING source s + ON t.id = s.id + WHEN MATCHED THEN + UPDATE SET qty2 = DEFAULT; + +UPDATE t SET col = (DEFAULT); + +UPDATE t SET col = (((DEFAULT))); + +UPDATE t SET (a, b) = ((DEFAULT), 1); + +INSERT INTO t VALUES ((DEFAULT));