diff --git a/PLAN.md b/PLAN.md index 474b0ff9..bafe35c9 100644 --- a/PLAN.md +++ b/PLAN.md @@ -791,6 +791,22 @@ type: string -- id of the customer that rents the CPU ``` +#### Star + +```sql +select * from (select case +-- ^$ hover + when random() > 0.5 then + true + else + false + end) +``` + +``` +("case": boolean) +``` + ### Semantic Syntax Highlighting https://code.visualstudio.com/api/language-extensions/semantic-highlight-guide diff --git a/crates/squawk_parser/src/generated/syntax_kind.rs b/crates/squawk_parser/src/generated/syntax_kind.rs index f7b75dd8..3eb49e10 100644 --- a/crates/squawk_parser/src/generated/syntax_kind.rs +++ b/crates/squawk_parser/src/generated/syntax_kind.rs @@ -831,6 +831,7 @@ pub enum SyntaxKind { LANGUAGE_FUNC_OPTION, LEAKPROOF_FUNC_OPTION, LIKE_CLAUSE, + LIKE_OPTION, LIMIT_CLAUSE, LISTEN, LITERAL, diff --git a/crates/squawk_parser/src/grammar.rs b/crates/squawk_parser/src/grammar.rs index d8bb46c3..f5d3af65 100644 --- a/crates/squawk_parser/src/grammar.rs +++ b/crates/squawk_parser/src/grammar.rs @@ -3796,7 +3796,7 @@ fn on_delete_action(p: &mut Parser<'_>) { m.complete(p, ON_DELETE_ACTION); } -const LIKE_OPTION: TokenSet = TokenSet::new(&[ +const LIKE_OPTION_FIRST: TokenSet = TokenSet::new(&[ COMMENTS_KW, COMPRESSION_KW, CONSTRAINTS_KW, @@ -3811,17 +3811,18 @@ const LIKE_OPTION: TokenSet = TokenSet::new(&[ // where like_option is: // { INCLUDING | EXCLUDING } { COMMENTS | COMPRESSION | CONSTRAINTS | DEFAULTS | GENERATED | IDENTITY | INDEXES | STATISTICS | STORAGE | ALL } -fn like_option(p: &mut Parser<'_>) -> bool { +fn opt_like_option(p: &mut Parser<'_>) -> Option { if p.at(INCLUDING_KW) || p.at(EXCLUDING_KW) { + let m = p.start(); p.bump_any(); - if p.at_ts(LIKE_OPTION) { + if p.at_ts(LIKE_OPTION_FIRST) { p.bump_any(); } else { - p.error("expected like option"); + p.err_and_bump(&format!("expected like option, got {:?}", p.current())); } - true + Some(m.complete(p, LIKE_OPTION)) } else { - false + None } } @@ -4235,7 +4236,7 @@ fn like_clause(p: &mut Parser<'_>) -> CompletedMarker { p.bump(LIKE_KW); path_name_ref(p); while !p.at(EOF) { - if !like_option(p) { + if opt_like_option(p).is_none() { break; } } @@ -8522,6 +8523,8 @@ fn create_foreign_table(p: &mut Parser<'_>) -> CompletedMarker { while !p.at(EOF) && !p.at(R_PAREN) { if p.at_ts(TABLE_CONSTRAINT_FIRST) { table_constraint(p); + } else if p.at(LIKE_KW) { + like_clause(p); } else { name(p); type_name(p); diff --git a/crates/squawk_parser/tests/data/ok/create_foreign_table_pg18.sql b/crates/squawk_parser/tests/data/ok/create_foreign_table_pg18.sql new file mode 100644 index 00000000..540d6582 --- /dev/null +++ b/crates/squawk_parser/tests/data/ok/create_foreign_table_pg18.sql @@ -0,0 +1,15 @@ +-- like clause is >=pg18 +create foreign table u ( + like t +) server s; + +-- like clause is >=pg18 +create foreign table remote_users ( + like local_users + including defaults + including constraints + excluding generated + excluding statistics + excluding all +) server remote_server; + diff --git a/crates/squawk_parser/tests/data/ok/create_table_pg17.sql b/crates/squawk_parser/tests/data/ok/create_table_pg17.sql index 76350371..d6a847db 100644 --- a/crates/squawk_parser/tests/data/ok/create_table_pg17.sql +++ b/crates/squawk_parser/tests/data/ok/create_table_pg17.sql @@ -23,6 +23,16 @@ create table t ( unique (a without overlaps) ); + +-- primary key without overlaps +create table t ( + a text, + b text, + c text, + constraint pk + primary key (a, b, c without overlaps) +); + -- temporal_primary_key CREATE TABLE addresses ( id int8 generated BY DEFAULT AS IDENTITY, diff --git a/crates/squawk_parser/tests/snapshots/tests__create_foreign_table_pg18_ok.snap b/crates/squawk_parser/tests/snapshots/tests__create_foreign_table_pg18_ok.snap new file mode 100644 index 00000000..3459ff3d --- /dev/null +++ b/crates/squawk_parser/tests/snapshots/tests__create_foreign_table_pg18_ok.snap @@ -0,0 +1,94 @@ +--- +source: crates/squawk_parser/tests/tests.rs +input_file: crates/squawk_parser/tests/data/ok/create_foreign_table_pg18.sql +--- +SOURCE_FILE + COMMENT "-- like clause is >=pg18" + WHITESPACE "\n" + CREATE_FOREIGN_TABLE + CREATE_KW "create" + WHITESPACE " " + FOREIGN_KW "foreign" + WHITESPACE " " + TABLE_KW "table" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME + IDENT "u" + WHITESPACE " " + L_PAREN "(" + WHITESPACE "\n " + LIKE_CLAUSE + LIKE_KW "like" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME_REF + IDENT "t" + WHITESPACE "\n" + R_PAREN ")" + WHITESPACE " " + SERVER_KW "server" + WHITESPACE " " + NAME_REF + IDENT "s" + SEMICOLON ";" + WHITESPACE "\n\n" + COMMENT "-- like clause is >=pg18" + WHITESPACE "\n" + CREATE_FOREIGN_TABLE + CREATE_KW "create" + WHITESPACE " " + FOREIGN_KW "foreign" + WHITESPACE " " + TABLE_KW "table" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME + IDENT "remote_users" + WHITESPACE " " + L_PAREN "(" + WHITESPACE "\n " + LIKE_CLAUSE + LIKE_KW "like" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME_REF + IDENT "local_users" + WHITESPACE "\n " + LIKE_OPTION + INCLUDING_KW "including" + WHITESPACE " " + DEFAULTS_KW "defaults" + WHITESPACE "\n " + LIKE_OPTION + INCLUDING_KW "including" + WHITESPACE " " + CONSTRAINTS_KW "constraints" + WHITESPACE "\n " + LIKE_OPTION + EXCLUDING_KW "excluding" + WHITESPACE " " + GENERATED_KW "generated" + WHITESPACE "\n " + LIKE_OPTION + EXCLUDING_KW "excluding" + WHITESPACE " " + STATISTICS_KW "statistics" + WHITESPACE "\n " + LIKE_OPTION + EXCLUDING_KW "excluding" + WHITESPACE " " + ALL_KW "all" + WHITESPACE "\n" + R_PAREN ")" + WHITESPACE " " + SERVER_KW "server" + WHITESPACE " " + NAME_REF + IDENT "remote_server" + SEMICOLON ";" + WHITESPACE "\n\n" diff --git a/crates/squawk_parser/tests/snapshots/tests__create_table_ok.snap b/crates/squawk_parser/tests/snapshots/tests__create_table_ok.snap index 7a6178ba..b471a1a2 100644 --- a/crates/squawk_parser/tests/snapshots/tests__create_table_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__create_table_ok.snap @@ -175,9 +175,10 @@ SOURCE_FILE NAME_REF IDENT "b" WHITESPACE " " - INCLUDING_KW "including" - WHITESPACE " " - COMMENTS_KW "comments" + LIKE_OPTION + INCLUDING_KW "including" + WHITESPACE " " + COMMENTS_KW "comments" WHITESPACE "\n" R_PAREN ")" SEMICOLON ";" @@ -221,41 +222,50 @@ SOURCE_FILE NAME_REF IDENT "b" WHITESPACE " " - INCLUDING_KW "including" - WHITESPACE " " - COMMENTS_KW "comments" - WHITESPACE " " - INCLUDING_KW "including" - WHITESPACE " " - CONSTRAINTS_KW "constraints" - WHITESPACE " " - EXCLUDING_KW "excluding" - WHITESPACE " " - DEFAULTS_KW "defaults" - WHITESPACE " " - EXCLUDING_KW "excluding" - WHITESPACE " " - GENERATED_KW "generated" - WHITESPACE " " - EXCLUDING_KW "excluding" - WHITESPACE " " - IDENTITY_KW "identity" + LIKE_OPTION + INCLUDING_KW "including" + WHITESPACE " " + COMMENTS_KW "comments" WHITESPACE " " - EXCLUDING_KW "excluding" + LIKE_OPTION + INCLUDING_KW "including" + WHITESPACE " " + CONSTRAINTS_KW "constraints" WHITESPACE " " - INDEXES_KW "indexes" + LIKE_OPTION + EXCLUDING_KW "excluding" + WHITESPACE " " + DEFAULTS_KW "defaults" WHITESPACE " " - EXCLUDING_KW "excluding" + LIKE_OPTION + EXCLUDING_KW "excluding" + WHITESPACE " " + GENERATED_KW "generated" WHITESPACE " " - STATISTICS_KW "statistics" + LIKE_OPTION + EXCLUDING_KW "excluding" + WHITESPACE " " + IDENTITY_KW "identity" WHITESPACE " " - INCLUDING_KW "including" + LIKE_OPTION + EXCLUDING_KW "excluding" + WHITESPACE " " + INDEXES_KW "indexes" WHITESPACE " " - STORAGE_KW "storage" + LIKE_OPTION + EXCLUDING_KW "excluding" + WHITESPACE " " + STATISTICS_KW "statistics" WHITESPACE " " - EXCLUDING_KW "excluding" + LIKE_OPTION + INCLUDING_KW "including" + WHITESPACE " " + STORAGE_KW "storage" WHITESPACE " " - ALL_KW "all" + LIKE_OPTION + EXCLUDING_KW "excluding" + WHITESPACE " " + ALL_KW "all" WHITESPACE "\n" R_PAREN ")" SEMICOLON ";" diff --git a/crates/squawk_parser/tests/snapshots/tests__create_table_pg17_ok.snap b/crates/squawk_parser/tests/snapshots/tests__create_table_pg17_ok.snap index 1c7d6ee4..c766d818 100644 --- a/crates/squawk_parser/tests/snapshots/tests__create_table_pg17_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__create_table_pg17_ok.snap @@ -286,6 +286,88 @@ SOURCE_FILE WHITESPACE "\n" R_PAREN ")" SEMICOLON ";" + WHITESPACE "\n\n\n" + CREATE_TABLE + COMMENT "-- primary key without overlaps" + WHITESPACE "\n" + CREATE_KW "create" + WHITESPACE " " + TABLE_KW "table" + WHITESPACE " " + PATH + PATH_SEGMENT + NAME + IDENT "t" + WHITESPACE " " + TABLE_ARG_LIST + L_PAREN "(" + WHITESPACE "\n " + COLUMN + NAME + IDENT "a" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + TEXT_KW "text" + COMMA "," + WHITESPACE "\n " + COLUMN + NAME + IDENT "b" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + TEXT_KW "text" + COMMA "," + WHITESPACE "\n " + COLUMN + NAME + IDENT "c" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + TEXT_KW "text" + COMMA "," + WHITESPACE "\n " + PRIMARY_KEY_CONSTRAINT + CONSTRAINT_KW "constraint" + WHITESPACE " " + NAME + IDENT "pk" + WHITESPACE " \n " + PRIMARY_KW "primary" + WHITESPACE " " + KEY_KW "key" + WHITESPACE " " + COLUMN_LIST + L_PAREN "(" + COLUMN + NAME_REF + IDENT "a" + COMMA "," + WHITESPACE " " + COLUMN + NAME_REF + IDENT "b" + COMMA "," + WHITESPACE " " + COLUMN + NAME_REF + IDENT "c" + WHITESPACE " " + WITHOUT_KW "without" + WHITESPACE " " + OVERLAPS_KW "overlaps" + R_PAREN ")" + WHITESPACE "\n" + R_PAREN ")" + SEMICOLON ";" WHITESPACE "\n\n" CREATE_TABLE COMMENT "-- temporal_primary_key" diff --git a/crates/squawk_syntax/src/ast/generated/nodes.rs b/crates/squawk_syntax/src/ast/generated/nodes.rs index dd6fbcc1..c69bfa4f 100644 --- a/crates/squawk_syntax/src/ast/generated/nodes.rs +++ b/crates/squawk_syntax/src/ast/generated/nodes.rs @@ -6184,12 +6184,59 @@ pub struct LikeClause { pub(crate) syntax: SyntaxNode, } impl LikeClause { + #[inline] + pub fn like_options(&self) -> AstChildren { + support::children(&self.syntax) + } #[inline] pub fn like_token(&self) -> Option { support::token(&self.syntax, SyntaxKind::LIKE_KW) } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct LikeOption { + pub(crate) syntax: SyntaxNode, +} +impl LikeOption { + #[inline] + pub fn comments_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::COMMENTS_KW) + } + #[inline] + pub fn compression_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::COMPRESSION_KW) + } + #[inline] + pub fn constraints_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::CONSTRAINTS_KW) + } + #[inline] + pub fn defaults_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::DEFAULTS_KW) + } + #[inline] + pub fn excluding_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::EXCLUDING_KW) + } + #[inline] + pub fn generated_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::GENERATED_KW) + } + #[inline] + pub fn identity_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::IDENTITY_KW) + } + #[inline] + pub fn including_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::INCLUDING_KW) + } + #[inline] + pub fn indexes_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::INDEXES_KW) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct LimitClause { pub(crate) syntax: SyntaxNode, @@ -15526,6 +15573,24 @@ impl AstNode for LikeClause { &self.syntax } } +impl AstNode for LikeOption { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::LIKE_OPTION + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} impl AstNode for LimitClause { #[inline] fn can_cast(kind: SyntaxKind) -> bool { diff --git a/crates/squawk_syntax/src/postgresql.ungram b/crates/squawk_syntax/src/postgresql.ungram index b110a9d0..c57eaab2 100644 --- a/crates/squawk_syntax/src/postgresql.ungram +++ b/crates/squawk_syntax/src/postgresql.ungram @@ -495,8 +495,18 @@ Truncate = ('restart' 'identity' | 'continue' 'identity')? ('cascade' | 'restrict')? +LikeOption = + ('including' | 'excluding') + ('comments' + | 'compression' + | 'constraints' + | 'defaults' + | 'generated' + | 'identity' + | 'indexes') + LikeClause = - 'like' + 'like' LikeOption* WhereClause = 'where' Expr diff --git a/crates/xtask/src/codegen.rs b/crates/xtask/src/codegen.rs index 61b2c58a..1f7ca352 100644 --- a/crates/xtask/src/codegen.rs +++ b/crates/xtask/src/codegen.rs @@ -242,13 +242,14 @@ fn generate_syntax_kinds(grammar: KindsSrc) -> Result { .map(|(_token, name)| format_ident!("{}", name)) .collect::>(); - let x = |&name| match name { - "Self" => format_ident!("SELF_TYPE_KW"), - name => format_ident!("{}_KW", name.to_case(Case::UpperSnake)), - }; - let all_keywords_values = grammar.keywords.to_vec(); - let all_keywords = all_keywords_values.iter().map(x).collect::>(); + let all_keywords = all_keywords_values + .iter() + .map(|&name| match name { + "Self" => format_ident!("SELF_TYPE_KW"), + name => format_ident!("{}_KW", name.to_case(Case::UpperSnake)), + }) + .collect::>(); let literals = grammar .literals diff --git a/postgres/kwlist.h b/postgres/kwlist.h index 577b3b45..f0decd44 100644 --- a/postgres/kwlist.h +++ b/postgres/kwlist.h @@ -1,7 +1,7 @@ // synced from: -// commit: cb1456423d3925f9c70a488b58f03f186561f00f -// committed at: 2025-05-22T21:14:54Z -// file: https://github.com/postgres/postgres/blob/cb1456423d3925f9c70a488b58f03f186561f00f/src/include/parser/kwlist.h +// commit: b0fb2c6aa5a485e28210e13ae5536c1231b1261f +// committed at: 2025-09-27T21:17:51Z +// file: https://github.com/postgres/postgres/blob/b0fb2c6aa5a485e28210e13ae5536c1231b1261f/src/include/parser/kwlist.h // // update via: // cargo xtask sync-kwlist