From 764e9f00ce64e1242c02c7974a6d512043412f70 Mon Sep 17 00:00:00 2001 From: Steve Dignam Date: Sat, 3 May 2025 18:13:35 -0400 Subject: [PATCH 1/6] v2: add syntax --- Cargo.toml | 5 + crates/squawk_syntax/Cargo.toml | 20 + crates/squawk_syntax/src/ast.rs | 126 + crates/squawk_syntax/src/ast/node_ext.rs | 52 + crates/squawk_syntax/src/ast/nodes.rs | 4559 +++++++++++++++++ crates/squawk_syntax/src/ast/support.rs | 45 + crates/squawk_syntax/src/ast/traits.rs | 34 + crates/squawk_syntax/src/lib.rs | 384 ++ crates/squawk_syntax/src/parsing.rs | 105 + ...st__alter_aggregate_params_validation.snap | 64 + ..._syntax__test__array_exprs_validation.snap | 51 + ...t__create_aggregate_params_validation.snap | 47 + ...ax__test__custom_operators_validation.snap | 112 + ...est__drop_aggregate_params_validation.snap | 239 + crates/squawk_syntax/src/syntax_error.rs | 71 + crates/squawk_syntax/src/syntax_node.rs | 93 + crates/squawk_syntax/src/test.rs | 51 + crates/squawk_syntax/src/token_text.rs | 128 + crates/squawk_syntax/src/validation.rs | 106 + .../validation/alter_aggregate_params.sql | 5 + .../test_data/validation/array_exprs.sql | 3 + .../validation/create_aggregate_params.sql | 4 + .../test_data/validation/custom_operators.sql | 8 + .../validation/drop_aggregate_params.sql | 32 + 24 files changed, 6344 insertions(+) create mode 100644 crates/squawk_syntax/Cargo.toml create mode 100644 crates/squawk_syntax/src/ast.rs create mode 100644 crates/squawk_syntax/src/ast/node_ext.rs create mode 100644 crates/squawk_syntax/src/ast/nodes.rs create mode 100644 crates/squawk_syntax/src/ast/support.rs create mode 100644 crates/squawk_syntax/src/ast/traits.rs create mode 100644 crates/squawk_syntax/src/lib.rs create mode 100644 crates/squawk_syntax/src/parsing.rs create mode 100644 crates/squawk_syntax/src/snapshots/squawk_syntax__test__alter_aggregate_params_validation.snap create mode 100644 crates/squawk_syntax/src/snapshots/squawk_syntax__test__array_exprs_validation.snap create mode 100644 crates/squawk_syntax/src/snapshots/squawk_syntax__test__create_aggregate_params_validation.snap create mode 100644 crates/squawk_syntax/src/snapshots/squawk_syntax__test__custom_operators_validation.snap create mode 100644 crates/squawk_syntax/src/snapshots/squawk_syntax__test__drop_aggregate_params_validation.snap create mode 100644 crates/squawk_syntax/src/syntax_error.rs create mode 100644 crates/squawk_syntax/src/syntax_node.rs create mode 100644 crates/squawk_syntax/src/test.rs create mode 100644 crates/squawk_syntax/src/token_text.rs create mode 100644 crates/squawk_syntax/src/validation.rs create mode 100644 crates/squawk_syntax/test_data/validation/alter_aggregate_params.sql create mode 100644 crates/squawk_syntax/test_data/validation/array_exprs.sql create mode 100644 crates/squawk_syntax/test_data/validation/create_aggregate_params.sql create mode 100644 crates/squawk_syntax/test_data/validation/custom_operators.sql create mode 100644 crates/squawk_syntax/test_data/validation/drop_aggregate_params.sql diff --git a/Cargo.toml b/Cargo.toml index 82c6aa96..8f03ee50 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,8 @@ dir-test = "0.4" drop_bomb = "0.1.5" camino = "1.1.9" pg_query = "6.1.0" +rowan = "0.15.15" +smol_str = "0.3.2" # local squawk-parser = { version = "0.0.0", path = "./crates/parser" } @@ -53,3 +55,6 @@ debug = 0 [profile.dev.package] insta.opt-level = 3 similar.opt-level = 3 +# These speed up local tests. +rowan.opt-level = 3 +text-size.opt-level = 3 diff --git a/crates/squawk_syntax/Cargo.toml b/crates/squawk_syntax/Cargo.toml new file mode 100644 index 00000000..e5f210c2 --- /dev/null +++ b/crates/squawk_syntax/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "squawk_syntax" +version = "0.0.0" +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true + +[dependencies] +parser.workspace = true +rowan.workspace = true +smol_str.workspace = true + +[dev-dependencies] +insta.workspace = true +dir-test.workspace = true +camino.workspace = true + +[lints] +workspace = true diff --git a/crates/squawk_syntax/src/ast.rs b/crates/squawk_syntax/src/ast.rs new file mode 100644 index 00000000..4343298a --- /dev/null +++ b/crates/squawk_syntax/src/ast.rs @@ -0,0 +1,126 @@ +// via https://github.com/rust-lang/rust-analyzer/blob/d8887c0758bbd2d5f752d5bd405d4491e90e7ed6/crates/syntax/src/ast.rs +// +// Permission is hereby granted, free of charge, to any +// person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the +// Software without restriction, including without +// limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +mod nodes; +mod support; +mod traits; + +mod node_ext; + +use std::marker::PhantomData; + +use crate::syntax_node::{SyntaxNode, SyntaxNodeChildren, SyntaxToken}; +use parser::SyntaxKind; + +pub use self::{ + nodes::*, + // generated::{nodes::*, tokens::*}, + // node_ext::{ + // AttrKind, FieldKind, Macro, NameLike, NameOrNameRef, PathSegmentKind, SelfParamKind, + // SlicePatComponents, StructKind, TraitOrAlias, TypeBoundKind, TypeOrConstParam, + // VisibilityKind, + // }, + // operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp}, + // token_ext::{CommentKind, CommentPlacement, CommentShape, IsString, QuoteOffsets, Radix}, + traits::{ + // AttrDocCommentIter, DocCommentIter, + HasArgList, // HasAttrs, HasDocComments, HasGenericArgs, + HasIfExists, + HasIfNotExists, // HasTypeBounds, + // HasVisibility, + // HasGenericParams, HasLoopBody, + HasModuleItem, + HasName, + }, +}; + +/// The main trait to go from untyped `SyntaxNode` to a typed ast. The +/// conversion itself has zero runtime cost: ast and syntax nodes have exactly +/// the same representation: a pointer to the tree root and a pointer to the +/// node itself. +pub trait AstNode { + fn can_cast(kind: SyntaxKind) -> bool + where + Self: Sized; + + fn cast(syntax: SyntaxNode) -> Option + where + Self: Sized; + + fn syntax(&self) -> &SyntaxNode; + fn clone_for_update(&self) -> Self + where + Self: Sized, + { + Self::cast(self.syntax().clone_for_update()).unwrap() + } + fn clone_subtree(&self) -> Self + where + Self: Sized, + { + Self::cast(self.syntax().clone_subtree()).unwrap() + } +} + +/// Like `AstNode`, but wraps tokens rather than interior nodes. +pub trait AstToken { + fn can_cast(token: SyntaxKind) -> bool + where + Self: Sized; + + fn cast(syntax: SyntaxToken) -> Option + where + Self: Sized; + + fn syntax(&self) -> &SyntaxToken; + + fn text(&self) -> &str { + self.syntax().text() + } +} + +/// An iterator over `SyntaxNode` children of a particular AST type. +#[derive(Debug, Clone)] +pub struct AstChildren { + inner: SyntaxNodeChildren, + ph: PhantomData, +} + +impl AstChildren { + fn new(parent: &SyntaxNode) -> Self { + AstChildren { + inner: parent.children(), + ph: PhantomData, + } + } +} + +impl Iterator for AstChildren { + type Item = N; + fn next(&mut self) -> Option { + self.inner.find_map(N::cast) + } +} diff --git a/crates/squawk_syntax/src/ast/node_ext.rs b/crates/squawk_syntax/src/ast/node_ext.rs new file mode 100644 index 00000000..43d17b28 --- /dev/null +++ b/crates/squawk_syntax/src/ast/node_ext.rs @@ -0,0 +1,52 @@ +// via https://github.com/rust-lang/rust-analyzer/blob/d8887c0758bbd2d5f752d5bd405d4491e90e7ed6/crates/syntax/src/ast/node_ext.rs +// +// Permission is hereby granted, free of charge, to any +// person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the +// Software without restriction, including without +// limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +use std::borrow::Cow; + +use rowan::{GreenNodeData, GreenTokenData, NodeOrToken}; + +use crate::{SyntaxNode, TokenText}; + +// impl ast::Name { +// pub fn text(&self) -> TokenText<'_> { +// text_of_first_token(self.syntax()) +// } +// } + +pub(crate) fn text_of_first_token(node: &SyntaxNode) -> TokenText<'_> { + fn first_token(green_ref: &GreenNodeData) -> &GreenTokenData { + green_ref + .children() + .next() + .and_then(NodeOrToken::into_token) + .unwrap() + } + + match node.green() { + Cow::Borrowed(green_ref) => TokenText::borrowed(first_token(green_ref).text()), + Cow::Owned(green) => TokenText::owned(first_token(&green).to_owned()), + } +} diff --git a/crates/squawk_syntax/src/ast/nodes.rs b/crates/squawk_syntax/src/ast/nodes.rs new file mode 100644 index 00000000..e81c2fcf --- /dev/null +++ b/crates/squawk_syntax/src/ast/nodes.rs @@ -0,0 +1,4559 @@ +// based on rust-analyzer's AST but SQL instead of Rust +// https://github.com/rust-lang/rust-analyzer/tree/d8887c0758bbd2d5f752d5bd405d4491e90e7ed6/crates/syntax/src/ast +use crate::ast::{AstNode, AstToken}; +use crate::syntax_node::SyntaxToken; +use crate::{ast, syntax_node::SyntaxNode}; +use crate::{SyntaxKind, TokenText}; + +use super::node_ext::text_of_first_token; +use super::{support, AstChildren}; + +impl ArgList { + pub fn args(&self) -> AstChildren { + support::children(&self.syntax) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SourceFile { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasModuleItem for SourceFile {} +impl AstNode for SourceFile { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::SOURCE_FILE + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Name { + pub(crate) syntax: SyntaxNode, +} +impl Name { + #[inline] + pub fn ident_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::IDENT) + } + pub fn text(&self) -> TokenText<'_> { + text_of_first_token(self.syntax()) + } +} +impl AstNode for Name { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + matches!(kind, SyntaxKind::NAME) + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + SyntaxKind::NAME => Name { syntax }, + _ => return None, + }; + Some(res) + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ArgList { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for ArgList { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::ARG_LIST + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CallExpr { + pub(crate) syntax: SyntaxNode, +} + +impl ast::HasArgList for CallExpr {} + +impl CallExpr { + #[inline] + pub fn expr(&self) -> Option { + support::child(&self.syntax) + } + // TODO: why do we have arg list and param list? + #[inline] + pub fn param_list(&self) -> Option { + support::child(&self.syntax) + } +} + +impl AstNode for CallExpr { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::CALL_EXPR + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CastExpr { + pub(crate) syntax: SyntaxNode, +} + +impl CastExpr { + #[inline] + pub fn expr(&self) -> Option { + support::child(&self.syntax) + } + + #[inline] + pub fn ty(&self) -> Option { + support::child(&self.syntax) + } +} + +impl AstNode for CastExpr { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::CAST_EXPR + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ArrayExpr { + pub(crate) syntax: SyntaxNode, +} + +impl ArrayExpr { + #[inline] + pub fn array_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::ARRAY_KW) + } +} + +impl AstNode for ArrayExpr { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::ARRAY_EXPR + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Expr { + CallExpr(CallExpr), + CastExpr(CastExpr), + ArrayExpr(ArrayExpr), + Literal(Literal), + NameRef(NameRef), +} + +impl AstNode for Expr { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + matches!( + kind, + SyntaxKind::CALL_EXPR + | SyntaxKind::CAST_EXPR + | SyntaxKind::LITERAL + | SyntaxKind::NAME_REF + | SyntaxKind::ARRAY_EXPR + ) + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + SyntaxKind::CALL_EXPR => Expr::CallExpr(CallExpr { syntax }), + SyntaxKind::CAST_EXPR => Expr::CastExpr(CastExpr { syntax }), + SyntaxKind::LITERAL => Expr::Literal(Literal { syntax }), + SyntaxKind::NAME_REF => Expr::NameRef(NameRef { syntax }), + SyntaxKind::ARRAY_EXPR => Expr::ArrayExpr(ArrayExpr { syntax }), + _ => return None, + }; + Some(res) + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + match self { + Expr::CallExpr(it) => &it.syntax, + Expr::CastExpr(it) => &it.syntax, + Expr::Literal(it) => &it.syntax, + Expr::NameRef(it) => &it.syntax, + Expr::ArrayExpr(it) => &it.syntax, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DefaultConstraint { + pub(crate) syntax: SyntaxNode, +} + +impl DefaultConstraint { + #[inline] + pub fn expr(&self) -> Option { + support::child(&self.syntax) + } +} + +impl AstNode for DefaultConstraint { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::DEFAULT_CONSTRAINT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct GeneratedConstraint { + pub(crate) syntax: SyntaxNode, +} + +impl GeneratedConstraint { + #[inline] + pub fn expr(&self) -> Option { + support::child(&self.syntax) + } +} + +impl AstNode for GeneratedConstraint { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::GENERATED_CONSTRAINT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ReferencesConstraint { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for ReferencesConstraint { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::REFERENCES_CONSTRAINT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct UsingIndex { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for UsingIndex { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::USING_INDEX + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct PrimaryKeyConstraint { + pub(crate) syntax: SyntaxNode, +} + +impl PrimaryKeyConstraint { + #[inline] + pub fn using_index(&self) -> Option { + support::child(&self.syntax) + } +} + +impl AstNode for PrimaryKeyConstraint { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::PRIMARY_KEY_CONSTRAINT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ForeignKeyConstraint { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for ForeignKeyConstraint { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::FOREIGN_KEY_CONSTRAINT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ExcludeConstraint { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for ExcludeConstraint { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::EXCLUDE_CONSTRAINT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct UniqueConstraint { + pub(crate) syntax: SyntaxNode, +} + +impl UniqueConstraint { + #[inline] + pub fn using_index(&self) -> Option { + support::child(&self.syntax) + } +} + +impl AstNode for UniqueConstraint { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::UNIQUE_CONSTRAINT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CheckConstraint { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for CheckConstraint { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::CHECK_CONSTRAINT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct NullConstraint { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for NullConstraint { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::NULL_CONSTRAINT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct NotNullConstraint { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for NotNullConstraint { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::NOT_NULL_CONSTRAINT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct NotValid { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for NotValid { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::NOT_VALID + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Constraint { + DefaultConstraint(DefaultConstraint), + GeneratedConstraint(GeneratedConstraint), + ReferencesConstraint(ReferencesConstraint), + PrimaryKeyConstraint(PrimaryKeyConstraint), + ForeignKeyConstraint(ForeignKeyConstraint), + ExcludeConstraint(ExcludeConstraint), + UniqueConstraint(UniqueConstraint), + CheckConstraint(CheckConstraint), + NullConstraint(NullConstraint), + NotNullConstraint(NotNullConstraint), +} + +impl Constraint { + #[inline] + pub fn name(&self) -> Option { + support::child(self.syntax()) + } +} + +impl AstNode for Constraint { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + matches!( + kind, + SyntaxKind::DEFAULT_CONSTRAINT + | SyntaxKind::GENERATED_CONSTRAINT + | SyntaxKind::REFERENCES_CONSTRAINT + | SyntaxKind::PRIMARY_KEY_CONSTRAINT + | SyntaxKind::FOREIGN_KEY_CONSTRAINT + | SyntaxKind::EXCLUDE_CONSTRAINT + | SyntaxKind::UNIQUE_CONSTRAINT + | SyntaxKind::CHECK_CONSTRAINT + | SyntaxKind::NULL_CONSTRAINT + | SyntaxKind::NOT_NULL_CONSTRAINT + ) + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + SyntaxKind::DEFAULT_CONSTRAINT => { + Constraint::DefaultConstraint(DefaultConstraint { syntax }) + } + SyntaxKind::GENERATED_CONSTRAINT => { + Constraint::GeneratedConstraint(GeneratedConstraint { syntax }) + } + SyntaxKind::REFERENCES_CONSTRAINT => { + Constraint::ReferencesConstraint(ReferencesConstraint { syntax }) + } + SyntaxKind::PRIMARY_KEY_CONSTRAINT => { + Constraint::PrimaryKeyConstraint(PrimaryKeyConstraint { syntax }) + } + SyntaxKind::FOREIGN_KEY_CONSTRAINT => { + Constraint::ForeignKeyConstraint(ForeignKeyConstraint { syntax }) + } + SyntaxKind::EXCLUDE_CONSTRAINT => { + Constraint::ExcludeConstraint(ExcludeConstraint { syntax }) + } + SyntaxKind::UNIQUE_CONSTRAINT => { + Constraint::UniqueConstraint(UniqueConstraint { syntax }) + } + SyntaxKind::CHECK_CONSTRAINT => Constraint::CheckConstraint(CheckConstraint { syntax }), + SyntaxKind::NULL_CONSTRAINT => Constraint::NullConstraint(NullConstraint { syntax }), + SyntaxKind::NOT_NULL_CONSTRAINT => { + Constraint::NotNullConstraint(NotNullConstraint { syntax }) + } + + _ => return None, + }; + Some(res) + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + match self { + Constraint::DefaultConstraint(t) => &t.syntax, + Constraint::GeneratedConstraint(t) => &t.syntax, + Constraint::ReferencesConstraint(t) => &t.syntax, + Constraint::PrimaryKeyConstraint(t) => &t.syntax, + Constraint::ForeignKeyConstraint(t) => &t.syntax, + Constraint::ExcludeConstraint(t) => &t.syntax, + Constraint::UniqueConstraint(t) => &t.syntax, + Constraint::CheckConstraint(t) => &t.syntax, + Constraint::NullConstraint(t) => &t.syntax, + Constraint::NotNullConstraint(t) => &t.syntax, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ValidateConstraint { + pub(crate) syntax: SyntaxNode, +} + +impl ValidateConstraint { + #[inline] + pub fn name_ref(&self) -> Option { + support::child(&self.syntax) + } +} + +impl AstNode for ValidateConstraint { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::VALIDATE_CONSTRAINT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AddColumn { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasIfNotExists for AddColumn {} +impl AddColumn { + #[inline] + pub fn ty(&self) -> Option { + support::child(&self.syntax) + } + + #[inline] + pub fn constraints(&self) -> AstChildren { + support::children(&self.syntax) + } +} + +impl AstNode for AddColumn { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::ADD_COLUMN + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ReplicaIdentity { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for ReplicaIdentity { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::REPLICA_IDENTITY + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct OfType { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for OfType { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::OF_TYPE + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct NotOf { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for NotOf { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::NOT_OF + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ForceRls { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for ForceRls { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::FORCE_RLS + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Inherit { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for Inherit { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::INHERIT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct NoInherit { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for NoInherit { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::NO_INHERIT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct EnableTrigger { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for EnableTrigger { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::ENABLE_TRIGGER + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct EnableReplicaTrigger { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for EnableReplicaTrigger { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::ENABLE_REPLICA_TRIGGER + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct EnableReplicaRule { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for EnableReplicaRule { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::ENABLE_REPLICA_RULE + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct EnableAlwaysTrigger { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for EnableAlwaysTrigger { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::ENABLE_ALWAYS_TRIGGER + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct EnableAlwaysRule { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for EnableAlwaysRule { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::ENABLE_ALWAYS_RULE + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct EnableRule { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for EnableRule { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::ENABLE_RULE + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct EnableRls { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for EnableRls { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::ENABLE_RLS + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DisableTrigger { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for DisableTrigger { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::DISABLE_TRIGGER + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DisableRls { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for DisableRls { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::DISABLE_RLS + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DisableRule { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for DisableRule { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::DISABLE_RULE + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DisableCluster { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for DisableCluster { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::DISABLE_CLUSTER + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct OwnerTo { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for OwnerTo { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::OWNER_TO + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DetachPartition { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for DetachPartition { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::DETACH_PARTITION + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DropConstraint { + pub(crate) syntax: SyntaxNode, +} + +impl ast::HasIfExists for DropConstraint {} + +impl DropConstraint { + #[inline] + pub fn name_ref(&self) -> Option { + support::child(&self.syntax) + } +} +impl AstNode for DropConstraint { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::DROP_CONSTRAINT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DropColumn { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasIfExists for DropColumn {} +impl AstNode for DropColumn { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::DROP_COLUMN + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AddConstraint { + pub(crate) syntax: SyntaxNode, +} +impl AddConstraint { + #[inline] + pub fn constraint(&self) -> Option { + support::child(&self.syntax) + } + + #[inline] + pub fn not_valid(&self) -> Option { + support::child(self.syntax()) + } +} + +impl AstNode for AddConstraint { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::ADD_CONSTRAINT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AttachPartition { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for AttachPartition { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::ATTACH_PARTITION + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SetSchema { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for SetSchema { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::SET_SCHEMA + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SetTablespace { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for SetTablespace { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::SET_TABLESPACE + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SetWithoutCluster { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for SetWithoutCluster { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::SET_WITHOUT_CLUSTER + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SetWithoutOids { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for SetWithoutOids { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::SET_WITHOUT_OIDS + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SetAccessMethod { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for SetAccessMethod { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::SET_ACCESS_METHOD + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SetLogged { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for SetLogged { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::SET_LOGGED + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SetUnlogged { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for SetUnlogged { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::SET_UNLOGGED + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SetStorageParams { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for SetStorageParams { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::SET_STORAGE_PARAMS + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct RenameTable { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for RenameTable { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::RENAME_TABLE + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct RenameConstraint { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for RenameConstraint { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::RENAME_CONSTRAINT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct RenameColumn { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for RenameColumn { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::RENAME_COLUMN + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AlterConstraint { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for AlterConstraint { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::ALTER_CONSTRAINT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DropDefault { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for DropDefault { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::DROP_DEFAULT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DropExpression { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for DropExpression { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::DROP_EXPRESSION + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DropIdentity { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for DropIdentity { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::DROP_IDENTITY + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DropNotNull { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for DropNotNull { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::DROP_NOT_NULL + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Restart { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for Restart { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::RESTART + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AddGenerated { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for AddGenerated { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::ADD_GENERATED + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ResetOptions { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for ResetOptions { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::RESET_OPTIONS + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SetType { + pub(crate) syntax: SyntaxNode, +} + +impl SetType { + #[inline] + pub fn ty(&self) -> Option { + support::child(&self.syntax) + } +} + +impl AstNode for SetType { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::SET_TYPE + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SetGeneratedOptions { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for SetGeneratedOptions { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::SET_GENERATED_OPTIONS + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SetGenerated { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for SetGenerated { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::SET_GENERATED + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SetSequenceOption { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for SetSequenceOption { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::SET_SEQUENCE_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 + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SetDefault { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for SetDefault { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::SET_DEFAULT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SetExpression { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for SetExpression { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::SET_EXPRESSION + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SetStatistics { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for SetStatistics { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::SET_STATISTICS + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SetOptions { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for SetOptions { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::SET_OPTIONS + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SetStorage { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for SetStorage { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::SET_STORAGE + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SetCompression { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for SetCompression { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::SET_COMPRESSION + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SetNotNull { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for SetNotNull { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::SET_NOT_NULL + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum AlterColumnOption { + DropDefault(DropDefault), + DropExpression(DropExpression), + DropIdentity(DropIdentity), + DropNotNull(DropNotNull), + Restart(Restart), + AddGenerated(AddGenerated), + ResetOptions(ResetOptions), + SetType(SetType), + SetGeneratedOptions(SetGeneratedOptions), + SetGenerated(SetGenerated), + SetSequenceOption(SetSequenceOption), + SetDefault(SetDefault), + SetExpression(SetExpression), + SetStatistics(SetStatistics), + SetOptions(SetOptions), + SetStorage(SetStorage), + SetCompression(SetCompression), + SetNotNull(SetNotNull), +} + +impl AstNode for AlterColumnOption { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + matches!( + kind, + SyntaxKind::DROP_DEFAULT + | SyntaxKind::DROP_EXPRESSION + | SyntaxKind::DROP_IDENTITY + | SyntaxKind::DROP_NOT_NULL + | SyntaxKind::RESTART + | SyntaxKind::ADD_GENERATED + | SyntaxKind::RESET_OPTIONS + | SyntaxKind::SET_TYPE + | SyntaxKind::SET_GENERATED_OPTIONS + | SyntaxKind::SET_GENERATED + | SyntaxKind::SET_DEFAULT + | SyntaxKind::SET_EXPRESSION + | SyntaxKind::SET_STATISTICS + | SyntaxKind::SET_OPTIONS + | SyntaxKind::SET_STORAGE + | SyntaxKind::COMPRESSION_KW + | SyntaxKind::SET_NOT_NULL + ) + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + SyntaxKind::DROP_DEFAULT => AlterColumnOption::DropDefault(DropDefault { syntax }), + SyntaxKind::DROP_EXPRESSION => { + AlterColumnOption::DropExpression(DropExpression { syntax }) + } + SyntaxKind::DROP_IDENTITY => AlterColumnOption::DropIdentity(DropIdentity { syntax }), + SyntaxKind::DROP_NOT_NULL => AlterColumnOption::DropNotNull(DropNotNull { syntax }), + SyntaxKind::RESTART => AlterColumnOption::Restart(Restart { syntax }), + SyntaxKind::ADD_GENERATED => AlterColumnOption::AddGenerated(AddGenerated { syntax }), + SyntaxKind::RESET_OPTIONS => AlterColumnOption::ResetOptions(ResetOptions { syntax }), + SyntaxKind::SET_TYPE => AlterColumnOption::SetType(SetType { syntax }), + SyntaxKind::SET_GENERATED_OPTIONS => { + AlterColumnOption::SetGeneratedOptions(SetGeneratedOptions { syntax }) + } + SyntaxKind::SET_GENERATED => AlterColumnOption::SetGenerated(SetGenerated { syntax }), + SyntaxKind::SET_SEQUENCE_OPTION => { + AlterColumnOption::SetSequenceOption(SetSequenceOption { syntax }) + } + SyntaxKind::SET_DEFAULT => AlterColumnOption::SetDefault(SetDefault { syntax }), + SyntaxKind::SET_EXPRESSION => { + AlterColumnOption::SetExpression(SetExpression { syntax }) + } + SyntaxKind::SET_STATISTICS => { + AlterColumnOption::SetStatistics(SetStatistics { syntax }) + } + SyntaxKind::SET_OPTIONS => AlterColumnOption::SetOptions(SetOptions { syntax }), + SyntaxKind::SET_STORAGE => AlterColumnOption::SetStorage(SetStorage { syntax }), + SyntaxKind::SET_COMPRESSION => { + AlterColumnOption::SetCompression(SetCompression { syntax }) + } + SyntaxKind::SET_NOT_NULL => AlterColumnOption::SetNotNull(SetNotNull { syntax }), + + _ => return None, + }; + Some(res) + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + match self { + AlterColumnOption::DropDefault(it) => &it.syntax, + AlterColumnOption::DropExpression(it) => &it.syntax, + AlterColumnOption::DropIdentity(it) => &it.syntax, + AlterColumnOption::DropNotNull(it) => &it.syntax, + AlterColumnOption::Restart(it) => &it.syntax, + AlterColumnOption::AddGenerated(it) => &it.syntax, + AlterColumnOption::ResetOptions(it) => &it.syntax, + AlterColumnOption::SetType(it) => &it.syntax, + AlterColumnOption::SetGeneratedOptions(it) => &it.syntax, + AlterColumnOption::SetGenerated(it) => &it.syntax, + AlterColumnOption::SetSequenceOption(it) => &it.syntax, + AlterColumnOption::SetDefault(it) => &it.syntax, + AlterColumnOption::SetExpression(it) => &it.syntax, + AlterColumnOption::SetStatistics(it) => &it.syntax, + AlterColumnOption::SetOptions(it) => &it.syntax, + AlterColumnOption::SetStorage(it) => &it.syntax, + AlterColumnOption::SetCompression(it) => &it.syntax, + AlterColumnOption::SetNotNull(it) => &it.syntax, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CreateDomain { + pub(crate) syntax: SyntaxNode, +} + +impl CreateDomain { + #[inline] + pub fn constraints(&self) -> AstChildren { + support::children(&self.syntax) + } +} + +impl AstNode for CreateDomain { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::CREATE_DOMAIN_STMT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AlterColumn { + pub(crate) syntax: SyntaxNode, +} +impl AlterColumn { + #[inline] + pub fn option(&self) -> Option { + support::child(&self.syntax) + } +} +impl AstNode for AlterColumn { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::ALTER_COLUMN + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct NoForceRls { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for NoForceRls { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::NO_FORCE_RLS + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +pub enum AlterTableAction { + ValidateConstraint(ValidateConstraint), + ReplicaIdentity(ReplicaIdentity), + OfType(OfType), + NotOf(NotOf), + ForceRls(ForceRls), + NoForceRls(NoForceRls), + Inherit(Inherit), + NoInherit(NoInherit), + EnableTrigger(EnableTrigger), + EnableReplicaTrigger(EnableReplicaTrigger), + EnableReplicaRule(EnableReplicaRule), + EnableAlwaysTrigger(EnableAlwaysTrigger), + EnableAlwaysRule(EnableAlwaysRule), + EnableRule(EnableRule), + EnableRls(EnableRls), + DisableTrigger(DisableTrigger), + DisableRls(DisableRls), + DisableRule(DisableRule), + DisableCluster(DisableCluster), + OwnerTo(OwnerTo), + DetachPartition(DetachPartition), + DropConstraint(DropConstraint), + DropColumn(DropColumn), + AddConstraint(AddConstraint), + AddColumn(AddColumn), + AttachPartition(AttachPartition), + SetSchema(SetSchema), + SetTablespace(SetTablespace), + SetWithoutCluster(SetWithoutCluster), + SetWithoutOids(SetWithoutOids), + SetAccessMethod(SetAccessMethod), + SetLogged(SetLogged), + SetUnlogged(SetUnlogged), + SetStorageParams(SetStorageParams), + RenameTable(RenameTable), + RenameConstraint(RenameConstraint), + RenameColumn(RenameColumn), + AlterConstraint(AlterConstraint), + AlterColumn(AlterColumn), +} + +impl AstNode for AlterTableAction { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + matches!( + kind, + SyntaxKind::VALIDATE_CONSTRAINT + | SyntaxKind::FORCE_RLS + | SyntaxKind::REPLICA_IDENTITY + | SyntaxKind::OF_TYPE + | SyntaxKind::NOT_OF + | SyntaxKind::INHERIT + | SyntaxKind::NO_INHERIT + | SyntaxKind::ENABLE_TRIGGER + | SyntaxKind::ENABLE_REPLICA_TRIGGER + | SyntaxKind::ENABLE_REPLICA_RULE + | SyntaxKind::ENABLE_ALWAYS_TRIGGER + | SyntaxKind::ENABLE_ALWAYS_RULE + | SyntaxKind::ENABLE_RULE + | SyntaxKind::ENABLE_RLS + | SyntaxKind::DISABLE_TRIGGER + | SyntaxKind::DISABLE_RLS + | SyntaxKind::DISABLE_RULE + | SyntaxKind::DISABLE_CLUSTER + | SyntaxKind::OWNER_TO + | SyntaxKind::DETACH_PARTITION + | SyntaxKind::DROP_CONSTRAINT + | SyntaxKind::DROP_COLUMN + | SyntaxKind::ADD_CONSTRAINT + | SyntaxKind::ADD_COLUMN + | SyntaxKind::ATTACH_PARTITION + | SyntaxKind::SET_SCHEMA + | SyntaxKind::SET_TABLESPACE + | SyntaxKind::SET_WITHOUT_CLUSTER + | SyntaxKind::SET_WITHOUT_OIDS + | SyntaxKind::SET_ACCESS_METHOD + | SyntaxKind::SET_LOGGED + | SyntaxKind::SET_UNLOGGED + | SyntaxKind::SET_STORAGE_PARAMS + | SyntaxKind::RENAME_TABLE + | SyntaxKind::RENAME_CONSTRAINT + | SyntaxKind::RENAME_COLUMN + | SyntaxKind::ALTER_CONSTRAINT + | SyntaxKind::ALTER_COLUMN + ) + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + SyntaxKind::VALIDATE_CONSTRAINT => { + AlterTableAction::ValidateConstraint(ValidateConstraint { syntax }) + } + SyntaxKind::ADD_COLUMN => AlterTableAction::AddColumn(AddColumn { syntax }), + SyntaxKind::REPLICA_IDENTITY => { + AlterTableAction::ReplicaIdentity(ReplicaIdentity { syntax }) + } + SyntaxKind::OF_TYPE => AlterTableAction::OfType(OfType { syntax }), + SyntaxKind::NOT_OF => AlterTableAction::NotOf(NotOf { syntax }), + SyntaxKind::FORCE_RLS => AlterTableAction::ForceRls(ForceRls { syntax }), + + SyntaxKind::INHERIT => AlterTableAction::Inherit(Inherit { syntax }), + SyntaxKind::NO_INHERIT => AlterTableAction::NoInherit(NoInherit { syntax }), + SyntaxKind::ENABLE_TRIGGER => AlterTableAction::EnableTrigger(EnableTrigger { syntax }), + SyntaxKind::ENABLE_REPLICA_TRIGGER => { + AlterTableAction::EnableReplicaTrigger(EnableReplicaTrigger { syntax }) + } + SyntaxKind::ENABLE_REPLICA_RULE => { + AlterTableAction::EnableReplicaRule(EnableReplicaRule { syntax }) + } + SyntaxKind::ENABLE_ALWAYS_TRIGGER => { + AlterTableAction::EnableAlwaysTrigger(EnableAlwaysTrigger { syntax }) + } + SyntaxKind::ENABLE_ALWAYS_RULE => { + AlterTableAction::EnableAlwaysRule(EnableAlwaysRule { syntax }) + } + SyntaxKind::ENABLE_RULE => AlterTableAction::EnableRule(EnableRule { syntax }), + SyntaxKind::ENABLE_RLS => AlterTableAction::EnableRls(EnableRls { syntax }), + SyntaxKind::DISABLE_TRIGGER => { + AlterTableAction::DisableTrigger(DisableTrigger { syntax }) + } + SyntaxKind::DISABLE_RLS => AlterTableAction::DisableRls(DisableRls { syntax }), + SyntaxKind::DISABLE_RULE => AlterTableAction::DisableRule(DisableRule { syntax }), + SyntaxKind::DISABLE_CLUSTER => { + AlterTableAction::DisableCluster(DisableCluster { syntax }) + } + SyntaxKind::OWNER_TO => AlterTableAction::OwnerTo(OwnerTo { syntax }), + SyntaxKind::DETACH_PARTITION => { + AlterTableAction::DetachPartition(DetachPartition { syntax }) + } + SyntaxKind::DROP_CONSTRAINT => { + AlterTableAction::DropConstraint(DropConstraint { syntax }) + } + SyntaxKind::DROP_COLUMN => AlterTableAction::DropColumn(DropColumn { syntax }), + SyntaxKind::ADD_CONSTRAINT => AlterTableAction::AddConstraint(AddConstraint { syntax }), + SyntaxKind::ATTACH_PARTITION => { + AlterTableAction::AttachPartition(AttachPartition { syntax }) + } + SyntaxKind::SET_SCHEMA => AlterTableAction::SetSchema(SetSchema { syntax }), + SyntaxKind::SET_TABLESPACE => AlterTableAction::SetTablespace(SetTablespace { syntax }), + SyntaxKind::SET_WITHOUT_CLUSTER => { + AlterTableAction::SetWithoutCluster(SetWithoutCluster { syntax }) + } + SyntaxKind::SET_WITHOUT_OIDS => { + AlterTableAction::SetWithoutOids(SetWithoutOids { syntax }) + } + SyntaxKind::SET_ACCESS_METHOD => { + AlterTableAction::SetAccessMethod(SetAccessMethod { syntax }) + } + SyntaxKind::SET_LOGGED => AlterTableAction::SetLogged(SetLogged { syntax }), + SyntaxKind::SET_UNLOGGED => AlterTableAction::SetUnlogged(SetUnlogged { syntax }), + SyntaxKind::SET_STORAGE_PARAMS => { + AlterTableAction::SetStorageParams(SetStorageParams { syntax }) + } + SyntaxKind::RENAME_TABLE => AlterTableAction::RenameTable(RenameTable { syntax }), + SyntaxKind::RENAME_CONSTRAINT => { + AlterTableAction::RenameConstraint(RenameConstraint { syntax }) + } + SyntaxKind::RENAME_COLUMN => AlterTableAction::RenameColumn(RenameColumn { syntax }), + SyntaxKind::ALTER_CONSTRAINT => { + AlterTableAction::AlterConstraint(AlterConstraint { syntax }) + } + SyntaxKind::ALTER_COLUMN => AlterTableAction::AlterColumn(AlterColumn { syntax }), + _ => return None, + }; + Some(res) + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + match self { + AlterTableAction::ValidateConstraint(it) => &it.syntax, + AlterTableAction::AddColumn(it) => &it.syntax, + AlterTableAction::ReplicaIdentity(it) => &it.syntax, + AlterTableAction::OfType(it) => &it.syntax, + AlterTableAction::NotOf(it) => &it.syntax, + AlterTableAction::ForceRls(it) => &it.syntax, + AlterTableAction::Inherit(it) => &it.syntax, + AlterTableAction::NoInherit(it) => &it.syntax, + AlterTableAction::EnableTrigger(it) => &it.syntax, + AlterTableAction::EnableReplicaTrigger(it) => &it.syntax, + AlterTableAction::EnableReplicaRule(it) => &it.syntax, + AlterTableAction::EnableAlwaysTrigger(it) => &it.syntax, + AlterTableAction::EnableAlwaysRule(it) => &it.syntax, + AlterTableAction::EnableRule(it) => &it.syntax, + AlterTableAction::EnableRls(it) => &it.syntax, + AlterTableAction::NoForceRls(it) => &it.syntax, + AlterTableAction::DisableTrigger(it) => &it.syntax, + AlterTableAction::DisableRls(it) => &it.syntax, + AlterTableAction::DisableRule(it) => &it.syntax, + AlterTableAction::DisableCluster(it) => &it.syntax, + AlterTableAction::OwnerTo(it) => &it.syntax, + AlterTableAction::DetachPartition(it) => &it.syntax, + AlterTableAction::DropConstraint(it) => &it.syntax, + AlterTableAction::DropColumn(it) => &it.syntax, + AlterTableAction::AddConstraint(it) => &it.syntax, + AlterTableAction::AttachPartition(it) => &it.syntax, + AlterTableAction::SetSchema(it) => &it.syntax, + AlterTableAction::SetTablespace(it) => &it.syntax, + AlterTableAction::SetWithoutCluster(it) => &it.syntax, + AlterTableAction::SetWithoutOids(it) => &it.syntax, + AlterTableAction::SetAccessMethod(it) => &it.syntax, + AlterTableAction::SetLogged(it) => &it.syntax, + AlterTableAction::SetUnlogged(it) => &it.syntax, + AlterTableAction::SetStorageParams(it) => &it.syntax, + AlterTableAction::RenameTable(it) => &it.syntax, + AlterTableAction::RenameConstraint(it) => &it.syntax, + AlterTableAction::RenameColumn(it) => &it.syntax, + AlterTableAction::AlterConstraint(it) => &it.syntax, + AlterTableAction::AlterColumn(it) => &it.syntax, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct RenameTo { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for RenameTo { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::RENAME_TO + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum AlterDomainAction { + SetDefault(SetDefault), + DropDefault(DropDefault), + SetNotNull(SetNotNull), + DropNotNull(DropNotNull), + AddConstraint(AddConstraint), + DropConstraint(DropConstraint), + RenameConstraint(RenameConstraint), + ValidateConstraint(ValidateConstraint), + OwnerTo(OwnerTo), + RenameTo(RenameTo), + SetSchema(SetSchema), +} + +impl AstNode for AlterDomainAction { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + matches!( + kind, + SyntaxKind::SET_DEFAULT + | SyntaxKind::DROP_DEFAULT + | SyntaxKind::SET_NOT_NULL + | SyntaxKind::DROP_NOT_NULL + | SyntaxKind::ADD_CONSTRAINT + | SyntaxKind::DROP_CONSTRAINT + | SyntaxKind::RENAME_CONSTRAINT + | SyntaxKind::VALIDATE_CONSTRAINT + | SyntaxKind::OWNER_TO + | SyntaxKind::RENAME_TO + | SyntaxKind::SET_SCHEMA + ) + } + + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + SyntaxKind::SET_DEFAULT => AlterDomainAction::SetDefault(SetDefault { syntax }), + SyntaxKind::DROP_DEFAULT => AlterDomainAction::DropDefault(DropDefault { syntax }), + SyntaxKind::SET_NOT_NULL => AlterDomainAction::SetNotNull(SetNotNull { syntax }), + SyntaxKind::DROP_NOT_NULL => AlterDomainAction::DropNotNull(DropNotNull { syntax }), + SyntaxKind::ADD_CONSTRAINT => { + AlterDomainAction::AddConstraint(AddConstraint { syntax }) + } + SyntaxKind::DROP_CONSTRAINT => { + AlterDomainAction::DropConstraint(DropConstraint { syntax }) + } + SyntaxKind::RENAME_CONSTRAINT => { + AlterDomainAction::RenameConstraint(RenameConstraint { syntax }) + } + SyntaxKind::VALIDATE_CONSTRAINT => { + AlterDomainAction::ValidateConstraint(ValidateConstraint { syntax }) + } + SyntaxKind::OWNER_TO => AlterDomainAction::OwnerTo(OwnerTo { syntax }), + SyntaxKind::RENAME_TO => AlterDomainAction::RenameTo(RenameTo { syntax }), + SyntaxKind::SET_SCHEMA => AlterDomainAction::SetSchema(SetSchema { syntax }), + _ => return None, + }; + Some(res) + } + + #[inline] + fn syntax(&self) -> &SyntaxNode { + match self { + AlterDomainAction::SetDefault(it) => &it.syntax, + AlterDomainAction::DropDefault(it) => &it.syntax, + AlterDomainAction::SetNotNull(it) => &it.syntax, + AlterDomainAction::DropNotNull(it) => &it.syntax, + AlterDomainAction::AddConstraint(it) => &it.syntax, + AlterDomainAction::DropConstraint(it) => &it.syntax, + AlterDomainAction::RenameConstraint(it) => &it.syntax, + AlterDomainAction::ValidateConstraint(it) => &it.syntax, + AlterDomainAction::OwnerTo(it) => &it.syntax, + AlterDomainAction::RenameTo(it) => &it.syntax, + AlterDomainAction::SetSchema(it) => &it.syntax, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AlterDomain { + pub(crate) syntax: SyntaxNode, +} + +impl AlterDomain { + #[inline] + pub fn actions(&self) -> AstChildren { + support::children(&self.syntax) + } +} + +impl AstNode for AlterDomain { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::ALTER_DOMAIN_STMT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AlterTable { + pub(crate) syntax: SyntaxNode, +} + +impl AlterTable { + #[inline] + pub fn path(&self) -> Option { + support::child(&self.syntax) + } +} + +impl AlterTable { + #[inline] + pub fn actions(&self) -> AstChildren { + support::children(&self.syntax) + } +} + +impl AstNode for AlterTable { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::ALTER_TABLE + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DropDatabase { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for DropDatabase { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::DROP_DATABASE_STMT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Column { + pub(crate) syntax: SyntaxNode, +} + +impl Column { + #[inline] + pub fn ty(&self) -> Option { + support::child(&self.syntax) + } +} + +impl AstNode for Column { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::COLUMN + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct LikeClause { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for LikeClause { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::LIKE_CLAUSE + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum TableArg { + Column(Column), + LikeClause(LikeClause), + Constraint(Constraint), +} + +impl AstNode for TableArg { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + matches!(kind, SyntaxKind::COLUMN | SyntaxKind::LIKE_CLAUSE) + | ast::Constraint::can_cast(kind) + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + SyntaxKind::COLUMN => TableArg::Column(Column { syntax }), + SyntaxKind::LIKE_CLAUSE => TableArg::LikeClause(LikeClause { syntax }), + _ => { + if let Some(result) = ast::Constraint::cast(syntax) { + return Some(TableArg::Constraint(result)); + } + return None; + } + }; + Some(res) + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + match self { + TableArg::Column(it) => &it.syntax, + TableArg::LikeClause(it) => &it.syntax, + TableArg::Constraint(it) => it.syntax(), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct TableArgs { + pub(crate) syntax: SyntaxNode, +} + +impl TableArgs { + #[inline] + pub fn args(&self) -> AstChildren { + support::children(&self.syntax) + } +} + +impl AstNode for TableArgs { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::TABLE_ARGS + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CreateTable { + pub(crate) syntax: SyntaxNode, +} + +impl ast::HasIfNotExists for CreateTable {} + +impl CreateTable { + #[inline] + pub fn path(&self) -> Option { + support::child(&self.syntax) + } +} + +impl CreateTable { + #[inline] + pub fn table_args(&self) -> Option { + support::child(&self.syntax) + } +} + +impl AstNode for CreateTable { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::CREATE_TABLE + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Begin { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for Begin { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::BEGIN_STMT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Commit { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for Commit { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::COMMIT_STMT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct IfExists { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for IfExists { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::IF_EXISTS + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct IfNotExists { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for IfNotExists { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::IF_NOT_EXISTS + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CreateIndex { + pub(crate) syntax: SyntaxNode, +} + +impl ast::HasIfNotExists for CreateIndex {} + +impl CreateIndex { + #[inline] + pub fn name(&self) -> Option { + support::child(&self.syntax) + } + + #[inline] + pub fn path(&self) -> Option { + support::child(&self.syntax) + } + + #[inline] + pub fn concurrently_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::CONCURRENTLY_KW) + } +} + +impl AstNode for CreateIndex { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::CREATE_INDEX_STMT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DropType { + pub(crate) syntax: SyntaxNode, +} + +impl ast::HasIfExists for DropType {} + +impl AstNode for DropType { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::DROP_TYPE_STMT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DropIndex { + pub(crate) syntax: SyntaxNode, +} + +impl ast::HasIfExists for DropIndex {} + +impl DropIndex { + #[inline] + pub fn concurrently_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::CONCURRENTLY_KW) + } +} + +impl AstNode for DropIndex { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::DROP_INDEX_STMT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DropTable { + pub(crate) syntax: SyntaxNode, +} + +impl ast::HasIfExists for DropTable {} + +impl AstNode for DropTable { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::DROP_TABLE + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AlterAggregate { + pub(crate) syntax: SyntaxNode, +} + +impl AlterAggregate { + #[inline] + pub fn path(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn param_list(&self) -> Option { + support::child(&self.syntax) + } +} + +impl AstNode for AlterAggregate { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::ALTER_AGGREGATE_STMT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct PrefixExpr { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for PrefixExpr { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::PREFIX_EXPR + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CustomOp { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for CustomOp { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::CUSTOM_OP + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DropAggregate { + pub(crate) syntax: SyntaxNode, +} + +impl DropAggregate { + #[inline] + pub fn aggregates(&self) -> AstChildren { + support::children(&self.syntax) + } +} + +impl AstNode for DropAggregate { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::DROP_AGGREGATE_STMT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CreateAggregate { + pub(crate) syntax: SyntaxNode, +} + +impl CreateAggregate { + #[inline] + pub fn path(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn param_list(&self) -> Option { + support::child(&self.syntax) + } +} + +impl AstNode for CreateAggregate { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::CREATE_AGGREGATE_STMT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Item { + AlterTable(AlterTable), + AlterDomain(AlterDomain), + AlterAggregate(AlterAggregate), + CreateAggregate(CreateAggregate), + Begin(Begin), + Commit(Commit), + CreateFunc(CreateFunc), + CreateIndex(CreateIndex), + CreateTable(CreateTable), + CreateDomain(CreateDomain), + DropDatabase(DropDatabase), + DropAggregate(DropAggregate), + DropTable(DropTable), + DropIndex(DropIndex), + DropType(DropType), + Select(Select), +} +// impl ast::HasAttrs for Item {} +// impl ast::HasDocComments for Item {} + +impl AstNode for Item { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + matches!( + kind, + SyntaxKind::SELECT + | SyntaxKind::CREATE_FUNCTION_STMT + | SyntaxKind::ALTER_TABLE + | SyntaxKind::DROP_DATABASE_STMT + | SyntaxKind::CREATE_TABLE + | SyntaxKind::BEGIN_STMT + | SyntaxKind::COMMIT_STMT + | SyntaxKind::CREATE_INDEX_STMT + | SyntaxKind::DROP_TABLE + | SyntaxKind::DROP_INDEX_STMT + | SyntaxKind::DROP_TYPE_STMT + | SyntaxKind::DROP_AGGREGATE_STMT + | SyntaxKind::CREATE_DOMAIN_STMT + | SyntaxKind::ALTER_DOMAIN_STMT + | SyntaxKind::ALTER_AGGREGATE_STMT + | SyntaxKind::CREATE_AGGREGATE_STMT + ) + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + SyntaxKind::SELECT => Item::Select(Select { syntax }), + SyntaxKind::CREATE_FUNCTION_STMT => Item::CreateFunc(CreateFunc { syntax }), + SyntaxKind::ALTER_TABLE => Item::AlterTable(AlterTable { syntax }), + SyntaxKind::DROP_DATABASE_STMT => Item::DropDatabase(DropDatabase { syntax }), + SyntaxKind::CREATE_TABLE => Item::CreateTable(CreateTable { syntax }), + SyntaxKind::BEGIN_STMT => Item::Begin(Begin { syntax }), + SyntaxKind::COMMIT_STMT => Item::Commit(Commit { syntax }), + SyntaxKind::CREATE_INDEX_STMT => Item::CreateIndex(CreateIndex { syntax }), + SyntaxKind::DROP_TABLE => Item::DropTable(DropTable { syntax }), + SyntaxKind::DROP_INDEX_STMT => Item::DropIndex(DropIndex { syntax }), + SyntaxKind::DROP_TYPE_STMT => Item::DropType(DropType { syntax }), + SyntaxKind::CREATE_DOMAIN_STMT => Item::CreateDomain(CreateDomain { syntax }), + SyntaxKind::ALTER_DOMAIN_STMT => Item::AlterDomain(AlterDomain { syntax }), + SyntaxKind::ALTER_AGGREGATE_STMT => Item::AlterAggregate(AlterAggregate { syntax }), + SyntaxKind::CREATE_AGGREGATE_STMT => Item::CreateAggregate(CreateAggregate { syntax }), + SyntaxKind::DROP_AGGREGATE_STMT => Item::DropAggregate(DropAggregate { syntax }), + _ => return None, + }; + Some(res) + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + match self { + Item::Select(it) => &it.syntax, + Item::CreateFunc(it) => &it.syntax, + Item::AlterTable(it) => &it.syntax, + Item::DropDatabase(it) => &it.syntax, + Item::CreateTable(it) => &it.syntax, + Item::Begin(it) => &it.syntax, + Item::Commit(it) => &it.syntax, + Item::CreateIndex(it) => &it.syntax, + Item::DropTable(it) => &it.syntax, + Item::DropIndex(it) => &it.syntax, + Item::DropType(it) => &it.syntax, + Item::CreateDomain(it) => &it.syntax, + Item::AlterDomain(it) => &it.syntax, + Item::AlterAggregate(it) => &it.syntax, + Item::CreateAggregate(it) => &it.syntax, + Item::DropAggregate(it) => &it.syntax, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Select { + pub(crate) syntax: SyntaxNode, +} +// impl ast::HasModuleItem for SourceFile {} +impl AstNode for Select { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::SELECT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct NameRef { + pub(crate) syntax: SyntaxNode, +} +impl NameRef { + #[inline] + pub fn ident_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::IDENT) + } + #[inline] + pub fn text(&self) -> TokenText<'_> { + text_of_first_token(self.syntax()) + } +} +impl AstNode for NameRef { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::NAME_REF + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct PathSegment { + pub(crate) syntax: SyntaxNode, +} +impl PathSegment { + #[inline] + pub fn name_ref(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn name(&self) -> Option { + support::child(&self.syntax) + } +} +impl AstNode for PathSegment { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::PATH_SEGMENT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Path { + pub(crate) syntax: SyntaxNode, +} +impl Path { + #[inline] + pub fn qualifier(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn segment(&self) -> Option { + support::child(&self.syntax) + } +} + +impl AstNode for Path { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::PATH + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ArrayType { + pub(crate) syntax: SyntaxNode, +} +impl ArrayType { + #[inline] + pub fn ty(&self) -> Option { + support::child(&self.syntax) + } +} +impl AstNode for ArrayType { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::ARRAY_TYPE + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct PercentType { + pub(crate) syntax: SyntaxNode, +} +impl PercentType { + #[inline] + pub fn path(&self) -> Option { + support::child(&self.syntax) + } +} +impl AstNode for PercentType { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::PERCENT_TYPE + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct PathType { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasArgList for PathType {} +impl PathType { + #[inline] + pub fn path(&self) -> Option { + support::child(&self.syntax) + } +} +impl AstNode for PathType { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::PATH_TYPE + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CharType { + pub(crate) syntax: SyntaxNode, +} + +impl ast::HasArgList for CharType {} + +impl CharType { + #[inline] + pub fn text(&self) -> TokenText<'_> { + text_of_first_token(self.syntax()) + } +} + +impl AstNode for CharType { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::CHAR_TYPE + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct BitType { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for BitType { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::BIT_TYPE + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DoubleType { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for DoubleType { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::DOUBLE_TYPE + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct WithTimezone { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for WithTimezone { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::WITH_TIMEZONE + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct WithoutTimezone { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for WithoutTimezone { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::WITHOUT_TIMEZONE + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct TimeType { + pub(crate) syntax: SyntaxNode, +} + +impl TimeType { + #[inline] + pub fn name_ref(&self) -> Option { + support::child(&self.syntax) + } + + #[inline] + pub fn without_timezone(&self) -> Option { + support::child(&self.syntax) + } + + #[inline] + pub fn with_timezone(&self) -> Option { + support::child(&self.syntax) + } +} + +impl AstNode for TimeType { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::TIME_TYPE + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct IntervalType { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for IntervalType { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::INTERVAL_TYPE + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Type { + ArrayType(ArrayType), + PercentType(PercentType), + PathType(PathType), + CharType(CharType), + BitType(BitType), + DoubleType(DoubleType), + TimeType(TimeType), + IntervalType(IntervalType), +} + +impl AstNode for Type { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + matches!( + kind, + SyntaxKind::ARRAY_TYPE + | SyntaxKind::PERCENT_TYPE + | SyntaxKind::PATH_TYPE + | SyntaxKind::CHAR_TYPE + | SyntaxKind::BIT_TYPE + | SyntaxKind::DOUBLE_TYPE + | SyntaxKind::TIME_TYPE + | SyntaxKind::INTERVAL_TYPE + ) + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + SyntaxKind::ARRAY_TYPE => Type::ArrayType(ArrayType { syntax }), + SyntaxKind::PATH_TYPE => Type::PathType(PathType { syntax }), + SyntaxKind::PERCENT_TYPE => Type::PercentType(PercentType { syntax }), + SyntaxKind::CHAR_TYPE => Type::CharType(CharType { syntax }), + SyntaxKind::BIT_TYPE => Type::BitType(BitType { syntax }), + SyntaxKind::DOUBLE_TYPE => Type::DoubleType(DoubleType { syntax }), + SyntaxKind::TIME_TYPE => Type::TimeType(TimeType { syntax }), + SyntaxKind::INTERVAL_TYPE => Type::IntervalType(IntervalType { syntax }), + _ => return None, + }; + Some(res) + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + match self { + Type::ArrayType(it) => &it.syntax, + Type::PathType(it) => &it.syntax, + Type::PercentType(it) => &it.syntax, + Type::CharType(it) => &it.syntax, + Type::BitType(it) => &it.syntax, + Type::DoubleType(it) => &it.syntax, + Type::TimeType(it) => &it.syntax, + Type::IntervalType(it) => &it.syntax, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct RetType { + pub(crate) syntax: SyntaxNode, +} +impl RetType { + #[inline] + pub fn ty(&self) -> Option { + support::child(&self.syntax) + } +} +impl AstNode for RetType { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::RET_TYPE + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct BeginFuncOption { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for BeginFuncOption { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::BEGIN_FUNC_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 + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ReturnFuncOption { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for ReturnFuncOption { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::RETURN_FUNC_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 + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct String { + pub(crate) syntax: SyntaxToken, +} + +impl AstToken for String { + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::STRING + } + fn cast(syntax: SyntaxToken) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxToken { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Null { + pub(crate) syntax: SyntaxToken, +} + +impl AstToken for Null { + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::NULL_KW + } + fn cast(syntax: SyntaxToken) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxToken { + &self.syntax + } +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum LiteralKind { + String(ast::String), + Null(ast::Null), + // ByteString(ast::ByteString), + // CString(ast::CString), + // IntNumber(ast::IntNumber), + // FloatNumber(ast::FloatNumber), + // Char(ast::Char), + // Byte(ast::Byte), + // Bool(bool), +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Literal { + pub(crate) syntax: SyntaxNode, +} + +impl Literal { + pub fn token(&self) -> SyntaxToken { + self.syntax() + .children_with_tokens() + .find(|e| !e.kind().is_trivia()) + .and_then(|e| e.into_token()) + .unwrap() + } + + #[inline] + pub fn kind(&self) -> LiteralKind { + let token = self.token(); + if let Some(t) = ast::String::cast(token.clone()) { + return LiteralKind::String(t); + } + if let Some(t) = ast::Null::cast(token.clone()) { + return LiteralKind::Null(t); + } + unreachable!() + } +} + +impl AstNode for Literal { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::LITERAL + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsFuncOption { + pub(crate) syntax: SyntaxNode, +} + +impl AsFuncOption { + #[inline] + pub fn strings(&self) -> AstChildren { + support::children(&self.syntax) + } +} + +impl AstNode for AsFuncOption { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::AS_FUNC_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 + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SupportFuncOption { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for SupportFuncOption { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::SUPPORT_FUNC_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 + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SetFuncOption { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for SetFuncOption { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::SET_FUNC_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 + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct RowsFuncOption { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for RowsFuncOption { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::ROWS_FUNC_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 + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CostFuncOption { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for CostFuncOption { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::COST_FUNC_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 + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ParallelFuncOption { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for ParallelFuncOption { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::PARALLEL_FUNC_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 + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SecurityFuncOption { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for SecurityFuncOption { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::SECURITY_FUNC_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 + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct StrictFuncOption { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for StrictFuncOption { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::SECURITY_FUNC_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 + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ResetFuncOption { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for ResetFuncOption { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::RESET_FUNC_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 + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct LeakproofFuncOption { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for LeakproofFuncOption { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::LEAKPROOF_FUNC_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 + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct VolatilityFuncOption { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for VolatilityFuncOption { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::VOLATILITY_FUNC_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 + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct WindowFuncOption { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for WindowFuncOption { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::WINDOW_FUNC_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 + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct TransformFuncOption { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for TransformFuncOption { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::TRANSFORM_FUNC_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 + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct LanguageFuncOption { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for LanguageFuncOption { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::LANGUAGE_FUNC_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 + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum FuncOption { + BeginFuncOption(BeginFuncOption), + ReturnFuncOption(ReturnFuncOption), + AsFuncOption(AsFuncOption), + SetFuncOption(SetFuncOption), + SupportFuncOption(SupportFuncOption), + RowsFuncOption(RowsFuncOption), + CostFuncOption(CostFuncOption), + ParallelFuncOption(ParallelFuncOption), + SecurityFuncOption(SecurityFuncOption), + StrictFuncOption(StrictFuncOption), + LeakproofFuncOption(LeakproofFuncOption), + ResetFuncOption(ResetFuncOption), + VolatilityFuncOption(VolatilityFuncOption), + WindowFuncOption(WindowFuncOption), + TransformFuncOption(TransformFuncOption), + LanguageFuncOption(LanguageFuncOption), +} + +impl AstNode for FuncOption { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + matches!( + kind, + SyntaxKind::BEGIN_FUNC_OPTION + | SyntaxKind::RETURN_FUNC_OPTION + | SyntaxKind::AS_FUNC_OPTION + | SyntaxKind::SET_FUNC_OPTION + | SyntaxKind::SUPPORT_FUNC_OPTION + | SyntaxKind::ROWS_FUNC_OPTION + | SyntaxKind::COST_FUNC_OPTION + | SyntaxKind::PARALLEL_FUNC_OPTION + | SyntaxKind::SECURITY_FUNC_OPTION + | SyntaxKind::STRICT_FUNC_OPTION + | SyntaxKind::LEAKPROOF_FUNC_OPTION + | SyntaxKind::RESET_FUNC_OPTION + | SyntaxKind::VOLATILITY_FUNC_OPTION + | SyntaxKind::WINDOW_FUNC_OPTION + | SyntaxKind::TRANSFORM_FUNC_OPTION + | SyntaxKind::LANGUAGE_FUNC_OPTION + ) + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + SyntaxKind::BEGIN_FUNC_OPTION => { + FuncOption::BeginFuncOption(BeginFuncOption { syntax }) + } + SyntaxKind::RETURN_FUNC_OPTION => { + FuncOption::ReturnFuncOption(ReturnFuncOption { syntax }) + } + SyntaxKind::AS_FUNC_OPTION => FuncOption::AsFuncOption(AsFuncOption { syntax }), + SyntaxKind::SET_FUNC_OPTION => FuncOption::SetFuncOption(SetFuncOption { syntax }), + SyntaxKind::SUPPORT_FUNC_OPTION => { + FuncOption::SupportFuncOption(SupportFuncOption { syntax }) + } + SyntaxKind::ROWS_FUNC_OPTION => FuncOption::RowsFuncOption(RowsFuncOption { syntax }), + SyntaxKind::COST_FUNC_OPTION => FuncOption::CostFuncOption(CostFuncOption { syntax }), + SyntaxKind::PARALLEL_FUNC_OPTION => { + FuncOption::ParallelFuncOption(ParallelFuncOption { syntax }) + } + SyntaxKind::SECURITY_FUNC_OPTION => { + FuncOption::SecurityFuncOption(SecurityFuncOption { syntax }) + } + SyntaxKind::STRICT_FUNC_OPTION => { + FuncOption::StrictFuncOption(StrictFuncOption { syntax }) + } + SyntaxKind::LEAKPROOF_FUNC_OPTION => { + FuncOption::LeakproofFuncOption(LeakproofFuncOption { syntax }) + } + SyntaxKind::RESET_FUNC_OPTION => { + FuncOption::ResetFuncOption(ResetFuncOption { syntax }) + } + SyntaxKind::VOLATILITY_FUNC_OPTION => { + FuncOption::VolatilityFuncOption(VolatilityFuncOption { syntax }) + } + SyntaxKind::WINDOW_FUNC_OPTION => { + FuncOption::WindowFuncOption(WindowFuncOption { syntax }) + } + SyntaxKind::TRANSFORM_FUNC_OPTION => { + FuncOption::TransformFuncOption(TransformFuncOption { syntax }) + } + SyntaxKind::LANGUAGE_FUNC_OPTION => { + FuncOption::LanguageFuncOption(LanguageFuncOption { syntax }) + } + + _ => return None, + }; + Some(res) + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + match self { + FuncOption::BeginFuncOption(it) => &it.syntax, + FuncOption::ReturnFuncOption(it) => &it.syntax, + FuncOption::AsFuncOption(it) => &it.syntax, + FuncOption::SetFuncOption(it) => &it.syntax, + FuncOption::ResetFuncOption(it) => &it.syntax, + FuncOption::SupportFuncOption(it) => &it.syntax, + FuncOption::RowsFuncOption(it) => &it.syntax, + FuncOption::CostFuncOption(it) => &it.syntax, + FuncOption::ParallelFuncOption(it) => &it.syntax, + FuncOption::SecurityFuncOption(it) => &it.syntax, + FuncOption::StrictFuncOption(it) => &it.syntax, + FuncOption::LeakproofFuncOption(it) => &it.syntax, + FuncOption::VolatilityFuncOption(it) => &it.syntax, + FuncOption::WindowFuncOption(it) => &it.syntax, + FuncOption::TransformFuncOption(it) => &it.syntax, + FuncOption::LanguageFuncOption(it) => &it.syntax, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct FuncOptionList { + pub(crate) syntax: SyntaxNode, +} + +impl FuncOptionList { + #[inline] + pub fn options(&self) -> AstChildren { + support::children(&self.syntax) + } +} + +impl AstNode for FuncOptionList { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::FUNC_OPTION_LIST + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ParamIn { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for ParamIn { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::PARAM_IN + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ParamOut { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for ParamOut { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::PARAM_OUT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ParamInOut { + pub(crate) syntax: SyntaxNode, +} +impl AstNode for ParamInOut { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::PARAM_INOUT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ParamVariadic { + pub(crate) syntax: SyntaxNode, +} + +impl AstNode for ParamVariadic { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::PARAM_VARIADIC + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ParamMode { + ParamIn(ParamIn), + ParamOut(ParamOut), + ParamInOut(ParamInOut), + ParamVariadic(ParamVariadic), +} + +impl AstNode for ParamMode { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + matches!( + kind, + SyntaxKind::PARAM_IN + | SyntaxKind::PARAM_OUT + | SyntaxKind::PARAM_INOUT + | SyntaxKind::VARIADIC_KW + ) + } + + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + SyntaxKind::PARAM_IN => ParamMode::ParamIn(ParamIn { syntax }), + SyntaxKind::PARAM_OUT => ParamMode::ParamOut(ParamOut { syntax }), + SyntaxKind::PARAM_INOUT => ParamMode::ParamInOut(ParamInOut { syntax }), + SyntaxKind::PARAM_VARIADIC => ParamMode::ParamVariadic(ParamVariadic { syntax }), + _ => return None, + }; + Some(res) + } + + #[inline] + fn syntax(&self) -> &SyntaxNode { + match self { + ParamMode::ParamIn(it) => &it.syntax, + ParamMode::ParamOut(it) => &it.syntax, + ParamMode::ParamInOut(it) => &it.syntax, + ParamMode::ParamVariadic(it) => &it.syntax, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Param { + pub(crate) syntax: SyntaxNode, +} +impl Param { + #[inline] + pub fn name(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn ty(&self) -> Option { + support::child(&self.syntax) + } + + #[inline] + pub fn mode(&self) -> Option { + support::child(&self.syntax) + } +} + +impl AstNode for Param { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::PARAM + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ParamList { + pub(crate) syntax: SyntaxNode, +} + +impl ParamList { + #[inline] + pub fn params(&self) -> AstChildren { + support::children(&self.syntax) + } +} + +impl AstNode for ParamList { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::PARAM_LIST + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CreateFunc { + pub(crate) syntax: SyntaxNode, +} +impl CreateFunc { + #[inline] + pub fn path(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn param_list(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn ret_type(&self) -> Option { + support::child(&self.syntax) + } + #[inline] + pub fn option_list(&self) -> Option { + support::child(&self.syntax) + } +} +impl AstNode for CreateFunc { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + kind == SyntaxKind::CREATE_FUNCTION_STMT + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} diff --git a/crates/squawk_syntax/src/ast/support.rs b/crates/squawk_syntax/src/ast/support.rs new file mode 100644 index 00000000..230fa8b3 --- /dev/null +++ b/crates/squawk_syntax/src/ast/support.rs @@ -0,0 +1,45 @@ +// via https://github.com/rust-lang/rust-analyzer/blob/d8887c0758bbd2d5f752d5bd405d4491e90e7ed6/crates/syntax/src/ast.rs +// +// Permission is hereby granted, free of charge, to any +// person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the +// Software without restriction, including without +// limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +use super::{AstChildren, AstNode, SyntaxKind, SyntaxNode, SyntaxToken}; + +#[inline] +pub(super) fn child(parent: &SyntaxNode) -> Option { + parent.children().find_map(N::cast) +} + +#[inline] +pub(super) fn children(parent: &SyntaxNode) -> AstChildren { + AstChildren::new(parent) +} + +#[inline] +pub(super) fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option { + parent + .children_with_tokens() + .filter_map(|it| it.into_token()) + .find(|it| it.kind() == kind) +} diff --git a/crates/squawk_syntax/src/ast/traits.rs b/crates/squawk_syntax/src/ast/traits.rs new file mode 100644 index 00000000..8d7d93e8 --- /dev/null +++ b/crates/squawk_syntax/src/ast/traits.rs @@ -0,0 +1,34 @@ +// based on rust-analyzer's ast traits +// https://github.com/rust-lang/rust-analyzer/blob/d8887c0758bbd2d5f752d5bd405d4491e90e7ed6/crates/syntax/src/ast/traits.rs +use crate::ast; +use crate::ast::{support, AstChildren, AstNode}; + +pub trait HasName: AstNode { + fn name(&self) -> Option { + support::child(self.syntax()) + } +} + +pub trait HasArgList: AstNode { + fn arg_list(&self) -> Option { + support::child(self.syntax()) + } +} + +pub trait HasModuleItem: AstNode { + fn items(&self) -> AstChildren { + support::children(self.syntax()) + } +} + +pub trait HasIfExists: AstNode { + fn if_exists(&self) -> Option { + support::child(self.syntax()) + } +} + +pub trait HasIfNotExists: AstNode { + fn if_not_exists(&self) -> Option { + support::child(self.syntax()) + } +} diff --git a/crates/squawk_syntax/src/lib.rs b/crates/squawk_syntax/src/lib.rs new file mode 100644 index 00000000..73d7a4b9 --- /dev/null +++ b/crates/squawk_syntax/src/lib.rs @@ -0,0 +1,384 @@ +// via https://github.com/rust-lang/rust-analyzer/blob/d8887c0758bbd2d5f752d5bd405d4491e90e7ed6/crates/syntax/src/lib.rs +// +// Permission is hereby granted, free of charge, to any +// person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the +// Software without restriction, including without +// limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +pub mod ast; +mod parsing; +pub mod syntax_error; +mod syntax_node; +mod token_text; +mod validation; + +#[cfg(test)] +mod test; + +use std::{marker::PhantomData, sync::Arc}; + +pub use parser::SyntaxKind; + +use ast::AstNode; +use rowan::GreenNode; +use syntax_error::SyntaxError; +pub use syntax_node::{SyntaxNode, SyntaxToken}; +pub use token_text::TokenText; + +/// `Parse` is the result of the parsing: a syntax tree and a collection of +/// errors. +/// +/// Note that we always produce a syntax tree, even for completely invalid +/// files. +#[derive(Debug, PartialEq, Eq)] +pub struct Parse { + green: GreenNode, + errors: Option>, + _ty: PhantomData T>, +} + +impl Clone for Parse { + fn clone(&self) -> Parse { + Parse { + green: self.green.clone(), + errors: self.errors.clone(), + _ty: PhantomData, + } + } +} + +impl Parse { + fn new(green: GreenNode, errors: Vec) -> Parse { + Parse { + green, + errors: if errors.is_empty() { + None + } else { + Some(errors.into()) + }, + _ty: PhantomData, + } + } + + pub fn syntax_node(&self) -> SyntaxNode { + SyntaxNode::new_root(self.green.clone()) + } + + pub fn errors(&self) -> Vec { + let mut errors = if let Some(e) = self.errors.as_deref() { + e.to_vec() + } else { + vec![] + }; + validation::validate(&self.syntax_node(), &mut errors); + errors + } +} + +impl Parse { + /// Converts this parse result into a parse result for an untyped syntax tree. + pub fn to_syntax(self) -> Parse { + Parse { + green: self.green, + errors: self.errors, + _ty: PhantomData, + } + } + + /// Gets the parsed syntax tree as a typed ast node. + /// + /// # Panics + /// + /// Panics if the root node cannot be casted into the typed ast node + /// (e.g. if it's an `ERROR` node). + pub fn tree(&self) -> T { + T::cast(self.syntax_node()).unwrap() + } + + /// Converts from `Parse` to [`Result>`]. + pub fn ok(self) -> Result> { + match self.errors() { + errors if !errors.is_empty() => Err(errors), + _ => Ok(self.tree()), + } + } +} + +impl Parse { + pub fn cast(self) -> Option> { + if N::cast(self.syntax_node()).is_some() { + Some(Parse { + green: self.green, + errors: self.errors, + _ty: PhantomData, + }) + } else { + None + } + } +} + +/// `SourceFile` represents a parse tree for a single SQL file. +pub use crate::ast::SourceFile; + +impl SourceFile { + pub fn parse(text: &str) -> Parse { + let (green, errors) = parsing::parse_text(text); + let root = SyntaxNode::new_root(green.clone()); + + assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE); + Parse::new(green, errors) + } +} + +/// Matches a `SyntaxNode` against an `ast` type. +/// +/// # Example: +/// +/// ```ignore +/// match_ast! { +/// match node { +/// ast::CallExpr(it) => { ... }, +/// ast::MethodCallExpr(it) => { ... }, +/// ast::MacroCall(it) => { ... }, +/// _ => None, +/// } +/// } +/// ``` +#[macro_export] +macro_rules! match_ast { + (match $node:ident { $($tt:tt)* }) => { $crate::match_ast!(match ($node) { $($tt)* }) }; + + (match ($node:expr) { + $( $( $path:ident )::+ ($it:pat) => $res:expr, )* + _ => $catch_all:expr $(,)? + }) => {{ + $( if let Some($it) = $($path::)+cast($node.clone()) { $res } else )* + { $catch_all } + }}; +} + +/// This test does not assert anything and instead just shows off the crate's +/// API. +#[test] +fn api_walkthrough() { + use ast::{HasModuleItem, SourceFile}; + use rowan::{Direction, NodeOrToken, SyntaxText, TextRange, WalkEvent}; + use std::fmt::Write; + + let source_code = " + create function foo(p int8) + returns int + as 'select 1 + 1' + language sql; + "; + // `SourceFile` is the main entry point. + // + // The `parse` method returns a `Parse` -- a pair of syntax tree and a list + // of errors. That is, syntax tree is constructed even in presence of errors. + let parse = SourceFile::parse(source_code); + assert!(parse.errors().is_empty()); + + // The `tree` method returns an owned syntax node of type `SourceFile`. + // Owned nodes are cheap: inside, they are `Rc` handles to the underling data. + let file: SourceFile = parse.tree(); + + // `SourceFile` is the root of the syntax tree. We can iterate file's items. + // Let's fetch the `foo` function. + let mut func = None; + for item in file.items() { + match item { + ast::Item::CreateFunc(f) => func = Some(f), + _ => unreachable!(), + } + } + let func: ast::CreateFunc = func.unwrap(); + + // Each AST node has a bunch of getters for children. All getters return + // `Option`s though, to account for incomplete code. Some getters are common + // for several kinds of node. In this case, a trait like `ast::NameOwner` + // usually exists. By convention, all ast types should be used with `ast::` + // qualifier. + let path: Option = func.path(); + let name: ast::Name = path.unwrap().segment().unwrap().name().unwrap(); + assert_eq!(name.text(), "foo"); + + // return + let ret_type: Option = func.ret_type(); + let r_ty = &ret_type.unwrap().ty().unwrap(); + let type_: &ast::PathType = match &r_ty { + ast::Type::PathType(r) => r, + _ => unreachable!(), + }; + let type_path: ast::Path = type_.path().unwrap(); + assert_eq!(type_path.syntax().to_string(), "int"); + + // params + let param_list: ast::ParamList = func.param_list().unwrap(); + let param: ast::Param = param_list.params().next().unwrap(); + + let param_name: ast::Name = param.name().unwrap(); + assert_eq!(param_name.syntax().to_string(), "p"); + + let param_ty: ast::Type = param.ty().unwrap(); + assert_eq!(param_ty.syntax().to_string(), "int8"); + + let func_option_list: ast::FuncOptionList = func.option_list().unwrap(); + + // Enums are used to group related ast nodes together, and can be used for + // matching. However, because there are no public fields, it's possible to + // match only the top level enum: that is the price we pay for increased API + // flexibility + let func_option = func_option_list.options().next().unwrap(); + let option = match &func_option { + ast::FuncOption::AsFuncOption(o) => o, + _ => unreachable!(), + }; + let string_text: ast::Literal = option.strings().next().unwrap(); + assert_eq!(string_text.syntax().to_string(), "'select 1 + 1'"); + + // Besides the "typed" AST API, there's an untyped CST one as well. + // To switch from AST to CST, call `.syntax()` method: + let func_option_syntax: &SyntaxNode = func_option.syntax(); + + // Note how `expr` and `bin_expr` are in fact the same node underneath: + assert!(func_option_syntax == option.syntax()); + + // To go from CST to AST, `AstNode::cast` function is used: + let _expr: ast::FuncOption = match ast::FuncOption::cast(func_option_syntax.clone()) { + Some(e) => e, + None => unreachable!(), + }; + + // The two properties each syntax node has is a `SyntaxKind`: + assert_eq!(func_option_syntax.kind(), SyntaxKind::AS_FUNC_OPTION); + + // And text range: + assert_eq!( + func_option_syntax.text_range(), + TextRange::new(65.into(), 82.into()) + ); + + // You can get node's text as a `SyntaxText` object, which will traverse the + // tree collecting token's text: + let text: SyntaxText = func_option_syntax.text(); + assert_eq!(text.to_string(), "as 'select 1 + 1'"); + + // There's a bunch of traversal methods on `SyntaxNode`: + assert_eq!( + func_option_syntax.parent().as_ref(), + Some(func_option_list.syntax()) + ); + assert_eq!( + param_list + .syntax() + .first_child_or_token() + .map(|it| it.kind()), + Some(SyntaxKind::L_PAREN) + ); + assert_eq!( + func_option_syntax + .next_sibling_or_token() + .map(|it| it.kind()), + Some(SyntaxKind::WHITESPACE) + ); + + // As well as some iterator helpers: + let f = func_option_syntax + .ancestors() + .find_map(ast::CreateFunc::cast); + assert_eq!(f, Some(func)); + assert!(param + .syntax() + .siblings_with_tokens(Direction::Next) + .any(|it| it.kind() == SyntaxKind::R_PAREN)); + assert_eq!( + func_option_syntax.descendants_with_tokens().count(), + 5, // 5 tokens `1`, ` `, `+`, ` `, `1` + // 2 child literal expressions: `1`, `1` + // 1 the node itself: `1 + 1` + ); + + // There's also a `preorder` method with a more fine-grained iteration control: + let mut buf = String::new(); + let mut indent = 0; + for event in func_option_syntax.preorder_with_tokens() { + match event { + WalkEvent::Enter(node) => { + let text = match &node { + NodeOrToken::Node(it) => it.text().to_string(), + NodeOrToken::Token(it) => it.text().to_owned(), + }; + buf.write_fmt(format_args!( + "{:indent$}{:?} {:?}\n", + " ", + text, + node.kind(), + indent = indent + )) + .unwrap(); + indent += 2; + } + WalkEvent::Leave(_) => indent -= 2, + } + } + assert_eq!(indent, 0); + assert_eq!( + buf.trim(), + r#" +"as 'select 1 + 1'" AS_FUNC_OPTION + "as" AS_KW + " " WHITESPACE + "'select 1 + 1'" LITERAL + "'select 1 + 1'" STRING + "# + .trim() + ); + + // To recursively process the tree, there are three approaches: + // 1. explicitly call getter methods on AST nodes. + // 2. use descendants and `AstNode::cast`. + // 3. use descendants and `match_ast!`. + // + // Here's how the first one looks like: + let exprs_cast: Vec = file + .syntax() + .descendants() + .filter_map(ast::FuncOption::cast) + .map(|expr| expr.syntax().text().to_string()) + .collect(); + + // An alternative is to use a macro. + let mut exprs_visit = Vec::new(); + for node in file.syntax().descendants() { + match_ast! { + match node { + ast::FuncOption(it) => { + let res = it.syntax().text().to_string(); + exprs_visit.push(res); + }, + _ => (), + } + } + } + assert_eq!(exprs_cast, exprs_visit); +} diff --git a/crates/squawk_syntax/src/parsing.rs b/crates/squawk_syntax/src/parsing.rs new file mode 100644 index 00000000..2ce3af42 --- /dev/null +++ b/crates/squawk_syntax/src/parsing.rs @@ -0,0 +1,105 @@ +// via https://github.com/rust-lang/rust-analyzer/blob/d8887c0758bbd2d5f752d5bd405d4491e90e7ed6/crates/syntax/src/parsing.rs +// +// Permission is hereby granted, free of charge, to any +// person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the +// Software without restriction, including without +// limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +use rowan::{GreenNode, TextRange}; + +use crate::{syntax_error::SyntaxError, syntax_node::SyntaxTreeBuilder}; + +// 1. lex input into vec of tokens +// 2. run the parser over the tokens generating an array of events +// 3. intersperse trivia TODO +pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec) { + let lexed = parser::LexedStr::new(text); + let parser_input = lexed.to_input(); + let parser_output = parser::parse(&parser_input); + let (node, errors, _eof) = build_tree(lexed, parser_output); + (node, errors) +} + +// 5. check for lexer errors and add them to the parser errors +// 6. return +// (node, errors) + +// 7. then some other function passes this stuff to SyntaxNode::new_root +// pub fn parse(text: &str, edition: Edition) -> Parse { +// let _p = tracing::info_span!("SourceFile::parse").entered(); +// let (green, errors) = parsing::parse_text(text, edition); +// let root = SyntaxNode::new_root(green.clone()); + +// assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE); +// Parse::new(green, errors) +// } + +// 8. rust analyzer has another validation layer that traverses the syntax tree to cover issues not caught by the lexer or parser + +// ast nodes wrap +// - rowan::syntax nodes +// - rowan::green nodes + +// +// they do some stuff to support having proper types for it +// +// also have can_cast and cast methods to support casting between the two +// +// #[derive(Debug, Clone, PartialEq, Eq, Hash)] +// pub struct IfExpr { +// pub(crate) syntax: SyntaxNode, +// } +// impl ast::HasAttrs for IfExpr {} +// impl IfExpr { +// #[inline] +// pub fn else_token(&self) -> Option { support::token(&self.syntax, T![else]) } +// #[inline] +// pub fn if_token(&self) -> Option { support::token(&self.syntax, T![if]) } +// } + +pub(crate) fn build_tree( + lexed: parser::LexedStr<'_>, + parser_output: parser::Output, +) -> (GreenNode, Vec, bool) { + let mut builder = SyntaxTreeBuilder::default(); + + let is_eof = lexed.intersperse_trivia(&parser_output, &mut |step| match step { + parser::StrStep::Token { kind, text } => builder.token(kind, text), + parser::StrStep::Enter { kind } => builder.start_node(kind), + parser::StrStep::Exit => builder.finish_node(), + parser::StrStep::Error { msg, pos } => { + builder.error(msg.to_owned(), pos.try_into().unwrap()) + } + }); + + let (node, mut errors) = builder.finish_raw(); + for (i, err) in lexed.errors() { + let text_range = lexed.text_range(i); + let text_range = TextRange::new( + text_range.start.try_into().unwrap(), + text_range.end.try_into().unwrap(), + ); + errors.push(SyntaxError::new(err, text_range)) + } + + (node, errors, is_eof) +} 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 new file mode 100644 index 00000000..0c4831be --- /dev/null +++ b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__alter_aggregate_params_validation.snap @@ -0,0 +1,64 @@ +--- +source: crates/syntax/src/test.rs +input_file: crates/syntax/test_data/validation/alter_aggregate_params.sql +--- +SOURCE_FILE@0..101 + COMMENT@0..38 "-- can't have out par ..." + WHITESPACE@38..39 "\n" + CREATE_AGGREGATE_STMT@39..99 + CREATE_KW@39..45 "create" + WHITESPACE@45..46 " " + AGGREGATE_KW@46..55 "aggregate" + WHITESPACE@55..56 " " + PATH@56..57 + PATH_SEGMENT@56..57 + NAME@56..57 + IDENT@56..57 "a" + PARAM_LIST@57..70 + L_PAREN@57..58 "(" + PARAM@58..62 + PARAM_IN@58..60 + IN_KW@58..60 "in" + WHITESPACE@60..61 " " + PATH_TYPE@61..62 + IDENT@61..62 "x" + COMMA@62..63 "," + WHITESPACE@63..64 " " + PARAM@64..69 + PARAM_OUT@64..67 + OUT_KW@64..67 "out" + WHITESPACE@67..68 " " + PATH_TYPE@68..69 + IDENT@68..69 "y" + R_PAREN@69..70 ")" + WHITESPACE@70..71 " " + L_PAREN@71..72 "(" + WHITESPACE@72..75 "\n " + NAME@75..80 + IDENT@75..80 "sfunc" + WHITESPACE@80..81 " " + EQ@81..82 "=" + WHITESPACE@82..83 " " + PATH_TYPE@83..84 + PATH@83..84 + PATH_SEGMENT@83..84 + NAME_REF@83..84 + IDENT@83..84 "f" + COMMA@84..85 "," + WHITESPACE@85..88 "\n " + NAME@88..93 + IDENT@88..93 "stype" + WHITESPACE@93..94 " " + EQ@94..95 "=" + WHITESPACE@95..96 " " + PATH_TYPE@96..97 + PATH@96..97 + PATH_SEGMENT@96..97 + NAME_REF@96..97 + IDENT@96..97 "t" + WHITESPACE@97..98 "\n" + R_PAREN@98..99 ")" + SEMICOLON@99..100 ";" + WHITESPACE@100..101 "\n" + +ERROR@64:67 "Out params are not allowed with aggregates." diff --git a/crates/squawk_syntax/src/snapshots/squawk_syntax__test__array_exprs_validation.snap b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__array_exprs_validation.snap new file mode 100644 index 00000000..1a5baeae --- /dev/null +++ b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__array_exprs_validation.snap @@ -0,0 +1,51 @@ +--- +source: crates/syntax/src/test.rs +input_file: crates/syntax/test_data/validation/array_exprs.sql +--- +SOURCE_FILE@0..42 + SELECT@0..22 + SELECT_CLAUSE@0..22 + SELECT_KW@0..6 "select" + WHITESPACE@6..7 " " + TARGET_LIST@7..22 + TARGET@7..22 + COL_LABEL@7..22 + ARRAY_EXPR@7..22 + L_BRACK@7..8 "[" + LITERAL@8..11 + STRING@8..11 "'a'" + COMMA@11..12 "," + WHITESPACE@12..13 " " + LITERAL@13..16 + STRING@13..16 "'b'" + COMMA@16..17 "," + WHITESPACE@17..18 " " + LITERAL@18..21 + STRING@18..21 "'c'" + R_BRACK@21..22 "]" + SEMICOLON@22..23 ";" + WHITESPACE@23..25 "\n\n" + SELECT@25..40 + SELECT_CLAUSE@25..40 + SELECT_KW@25..31 "select" + WHITESPACE@31..32 " " + TARGET_LIST@32..40 + TARGET@32..40 + COL_LABEL@32..40 + ARRAY_EXPR@32..40 + L_BRACK@32..33 "[" + ARRAY_EXPR@33..39 + L_BRACK@33..34 "[" + LITERAL@34..35 + INT_NUMBER@34..35 "1" + COMMA@35..36 "," + WHITESPACE@36..37 " " + LITERAL@37..38 + INT_NUMBER@37..38 "2" + R_BRACK@38..39 "]" + R_BRACK@39..40 "]" + SEMICOLON@40..41 ";" + WHITESPACE@41..42 "\n" + +ERROR@7 "Array missing ARRAY keyword." +ERROR@32 "Array missing ARRAY keyword." 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 new file mode 100644 index 00000000..2d26710f --- /dev/null +++ b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__create_aggregate_params_validation.snap @@ -0,0 +1,47 @@ +--- +source: crates/syntax/src/test.rs +input_file: crates/syntax/test_data/validation/create_aggregate_params.sql +--- +SOURCE_FILE@0..91 + COMMENT@0..38 "-- can't have out par ..." + WHITESPACE@38..39 "\n" + ALTER_AGGREGATE_STMT@39..88 + ALTER_KW@39..44 "alter" + WHITESPACE@44..45 " " + AGGREGATE_KW@45..54 "aggregate" + WHITESPACE@54..55 " " + PATH@55..56 + PATH_SEGMENT@55..56 + NAME_REF@55..56 + IDENT@55..56 "a" + WHITESPACE@56..57 " " + PARAM_LIST@57..70 + L_PAREN@57..58 "(" + PARAM@58..62 + PARAM_IN@58..60 + IN_KW@58..60 "in" + WHITESPACE@60..61 " " + PATH_TYPE@61..62 + IDENT@61..62 "t" + COMMA@62..63 "," + WHITESPACE@63..64 " " + PARAM@64..69 + PARAM_OUT@64..67 + OUT_KW@64..67 "out" + WHITESPACE@67..68 " " + PATH_TYPE@68..69 + IDENT@68..69 "u" + R_PAREN@69..70 ")" + WHITESPACE@70..76 " \n " + SET_KW@76..79 "set" + WHITESPACE@79..80 " " + SCHEMA_KW@80..86 "schema" + WHITESPACE@86..87 " " + PATH@87..88 + PATH_SEGMENT@87..88 + NAME_REF@87..88 + IDENT@87..88 "s" + SEMICOLON@88..89 ";" + WHITESPACE@89..91 "\n\n" + +ERROR@64:67 "Out params are not allowed with aggregates." diff --git a/crates/squawk_syntax/src/snapshots/squawk_syntax__test__custom_operators_validation.snap b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__custom_operators_validation.snap new file mode 100644 index 00000000..1b21e0fa --- /dev/null +++ b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__custom_operators_validation.snap @@ -0,0 +1,112 @@ +--- +source: crates/syntax/src/test.rs +input_file: crates/syntax/test_data/validation/custom_operators.sql +--- +SOURCE_FILE@0..108 + COMMENT@0..30 "-- disallowed prefix ..." + WHITESPACE@30..31 "\n" + SELECT@31..40 + SELECT_CLAUSE@31..40 + SELECT_KW@31..37 "select" + WHITESPACE@37..38 " " + TARGET_LIST@38..40 + TARGET@38..39 + STAR@38..39 "*" + TARGET@39..40 + COL_LABEL@39..40 + NAME_REF@39..40 + IDENT@39..40 "c" + SEMICOLON@40..41 ";" + WHITESPACE@41..42 "\n" + SELECT@42..51 + SELECT_CLAUSE@42..51 + SELECT_KW@42..48 "select" + WHITESPACE@48..49 " " + TARGET_LIST@49..51 + TARGET@49..51 + COL_LABEL@49..51 + PREFIX_EXPR@49..51 + CUSTOM_OP@49..50 + SLASH@49..50 "/" + NAME_REF@50..51 + IDENT@50..51 "d" + SEMICOLON@51..52 ";" + WHITESPACE@52..53 "\n" + SELECT@53..62 + SELECT_CLAUSE@53..62 + SELECT_KW@53..59 "select" + WHITESPACE@59..60 " " + TARGET_LIST@60..62 + TARGET@60..62 + COL_LABEL@60..62 + PREFIX_EXPR@60..62 + CUSTOM_OP@60..61 + L_ANGLE@60..61 "<" + NAME_REF@61..62 + IDENT@61..62 "e" + SEMICOLON@62..63 ";" + WHITESPACE@63..64 "\n" + SELECT@64..73 + SELECT_CLAUSE@64..73 + SELECT_KW@64..70 "select" + WHITESPACE@70..71 " " + TARGET_LIST@71..73 + TARGET@71..73 + COL_LABEL@71..73 + PREFIX_EXPR@71..73 + CUSTOM_OP@71..72 + R_ANGLE@71..72 ">" + NAME_REF@72..73 + IDENT@72..73 "f" + SEMICOLON@73..74 ";" + WHITESPACE@74..75 "\n" + SELECT@75..84 + SELECT_CLAUSE@75..84 + SELECT_KW@75..81 "select" + WHITESPACE@81..82 " " + TARGET_LIST@82..84 + TARGET@82..84 + COL_LABEL@82..84 + PREFIX_EXPR@82..84 + CUSTOM_OP@82..83 + EQ@82..83 "=" + NAME_REF@83..84 + IDENT@83..84 "g" + SEMICOLON@84..85 ";" + WHITESPACE@85..86 "\n" + SELECT@86..95 + SELECT_CLAUSE@86..95 + SELECT_KW@86..92 "select" + WHITESPACE@92..93 " " + TARGET_LIST@93..95 + TARGET@93..95 + COL_LABEL@93..95 + PREFIX_EXPR@93..95 + CUSTOM_OP@93..94 + PERCENT@93..94 "%" + NAME_REF@94..95 + IDENT@94..95 "l" + SEMICOLON@95..96 ";" + WHITESPACE@96..97 "\n" + SELECT@97..106 + SELECT_CLAUSE@97..106 + SELECT_KW@97..103 "select" + WHITESPACE@103..104 " " + TARGET_LIST@104..106 + TARGET@104..106 + COL_LABEL@104..106 + PREFIX_EXPR@104..106 + CUSTOM_OP@104..105 + CARET@104..105 "^" + NAME_REF@105..106 + IDENT@105..106 "m" + SEMICOLON@106..107 ";" + WHITESPACE@107..108 "\n" + +ERROR@39 "missing comma" +ERROR@49:50 "Invalid operator." +ERROR@60:61 "Invalid operator." +ERROR@71:72 "Invalid operator." +ERROR@82:83 "Invalid operator." +ERROR@93:94 "Invalid operator." +ERROR@104:105 "Invalid operator." 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 new file mode 100644 index 00000000..e138f26a --- /dev/null +++ b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__drop_aggregate_params_validation.snap @@ -0,0 +1,239 @@ +--- +source: crates/syntax/src/test.rs +input_file: crates/syntax/test_data/validation/drop_aggregate_params.sql +--- +SOURCE_FILE@0..389 + WHITESPACE@0..1 "\n" + COMMENT@1..8 "-- full" + WHITESPACE@8..9 "\n" + DROP_AGGREGATE_STMT@9..167 + DROP_KW@9..13 "drop" + WHITESPACE@13..14 " " + AGGREGATE_KW@14..23 "aggregate" + WHITESPACE@23..27 " \n " + IF_EXISTS@27..36 + IF_KW@27..29 "if" + WHITESPACE@29..30 " " + EXISTS_KW@30..36 "exists" + WHITESPACE@36..42 " \n " + CALL_EXPR@42..46 + PATH@42..43 + PATH_SEGMENT@42..43 + NAME_REF@42..43 + IDENT@42..43 "a" + PARAM_LIST@43..46 + L_PAREN@43..44 "(" + STAR@44..45 "*" + R_PAREN@45..46 ")" + COMMA@46..47 "," + WHITESPACE@47..53 " \n " + CALL_EXPR@53..63 + PATH@53..60 + PATH@53..56 + PATH_SEGMENT@53..56 + NAME_REF@53..56 + IDENT@53..56 "foo" + DOT@56..57 "." + PATH_SEGMENT@57..60 + NAME_REF@57..60 + IDENT@57..60 "bar" + PARAM_LIST@60..63 + L_PAREN@60..61 "(" + STAR@61..62 "*" + R_PAREN@62..63 ")" + COMMA@63..64 "," + WHITESPACE@64..70 " \n " + CALL_EXPR@70..146 + PATH@70..77 + PATH@70..73 + PATH_SEGMENT@70..73 + NAME_REF@70..73 + IDENT@70..73 "foo" + DOT@73..74 "." + PATH_SEGMENT@74..77 + NAME_REF@74..77 + IDENT@74..77 "bar" + PARAM_LIST@77..146 + L_PAREN@77..78 "(" + WHITESPACE@78..87 "\n " + PARAM@87..101 + PARAM_IN@87..89 + IN_KW@87..89 "in" + WHITESPACE@89..90 " " + NAME@90..93 + IDENT@90..93 "foo" + WHITESPACE@93..94 " " + PATH_TYPE@94..101 + PATH@94..101 + PATH_SEGMENT@94..101 + NAME_REF@94..101 + INTEGER_KW@94..101 "integer" + COMMA@101..102 "," + WHITESPACE@102..111 "\n " + PARAM@111..126 + PARAM_OUT@111..114 + OUT_KW@111..114 "out" + WHITESPACE@114..115 " " + NAME@115..118 + IDENT@115..118 "bar" + WHITESPACE@118..119 " " + PATH_TYPE@119..126 + PATH@119..126 + PATH_SEGMENT@119..126 + NAME_REF@119..126 + INTEGER_KW@119..126 "integer" + COMMA@126..127 "," + WHITESPACE@127..136 "\n " + PARAM@136..140 + PATH_TYPE@136..140 + TEXT_KW@136..140 "text" + WHITESPACE@140..145 "\n " + R_PAREN@145..146 ")" + COMMA@146..147 "," + WHITESPACE@147..153 " \n " + CALL_EXPR@153..157 + PATH@153..154 + PATH_SEGMENT@153..154 + NAME_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_KW@160..167 "cascade" + SEMICOLON@167..168 ";" + WHITESPACE@168..170 "\n\n" + COMMENT@170..182 "-- aggregate" + WHITESPACE@182..183 "\n" + DROP_AGGREGATE_STMT@183..299 + DROP_KW@183..187 "drop" + WHITESPACE@187..188 " " + AGGREGATE_KW@188..197 "aggregate" + WHITESPACE@197..198 " " + CALL_EXPR@198..290 + PATH@198..199 + PATH_SEGMENT@198..199 + NAME_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@203..210 + PATH_SEGMENT@203..210 + NAME_REF@203..210 + INTEGER_KW@203..210 "integer" + COMMA@210..211 "," + WHITESPACE@211..214 "\n " + PARAM@214..218 + PATH_TYPE@214..218 + TEXT_KW@214..218 "text" + COMMA@218..219 "," + WHITESPACE@219..222 "\n " + PARAM@222..229 + PATH_TYPE@222..229 + PATH@222..229 + PATH_SEGMENT@222..229 + NAME_REF@222..229 + NUMERIC_KW@222..229 "numeric" + WHITESPACE@229..232 "\n " + 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 " " + NAME@248..249 + IDENT@248..249 "a" + WHITESPACE@249..250 " " + TIME_TYPE@250..259 + NAME_REF@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 " " + NAME@269..270 + IDENT@269..270 "b" + WHITESPACE@270..271 " " + PATH_TYPE@271..278 + PATH@271..278 + PATH_SEGMENT@271..278 + NAME_REF@271..278 + NUMERIC_KW@271..278 "numeric" + COMMA@278..279 "," + WHITESPACE@279..284 "\n " + PARAM@284..288 + PATH_TYPE@284..288 + TEXT_KW@284..288 "text" + WHITESPACE@288..289 "\n" + R_PAREN@289..290 ")" + WHITESPACE@290..291 " " + RESTRICT_KW@291..299 "restrict" + SEMICOLON@299..300 ";" + WHITESPACE@300..302 "\n\n" + DROP_AGGREGATE_STMT@302..386 + DROP_KW@302..306 "drop" + WHITESPACE@306..307 " " + AGGREGATE_KW@307..316 "aggregate" + WHITESPACE@316..317 " " + CALL_EXPR@317..386 + PATH@317..324 + PATH@317..320 + PATH_SEGMENT@317..320 + NAME_REF@317..320 + IDENT@317..320 "foo" + DOT@320..321 "." + PATH_SEGMENT@321..324 + NAME_REF@321..324 + IDENT@321..324 "bar" + PARAM_LIST@324..386 + L_PAREN@324..325 "(" + WHITESPACE@325..328 "\n " + 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 " " + NAME@344..345 + IDENT@344..345 "a" + WHITESPACE@345..346 " " + TIME_TYPE@346..355 + NAME_REF@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 " " + NAME@365..366 + IDENT@365..366 "b" + WHITESPACE@366..367 " " + PATH_TYPE@367..374 + PATH@367..374 + PATH_SEGMENT@367..374 + NAME_REF@367..374 + NUMERIC_KW@367..374 "numeric" + COMMA@374..375 "," + WHITESPACE@375..380 "\n " + PARAM@380..384 + PATH_TYPE@380..384 + TEXT_KW@380..384 "text" + WHITESPACE@384..385 "\n" + R_PAREN@385..386 ")" + SEMICOLON@386..387 ";" + WHITESPACE@387..389 "\n\n" + +ERROR@111:114 "Out params are not allowed with aggregates." +ERROR@265:268 "Out params are not allowed with aggregates." +ERROR@361:364 "Out params are not allowed with aggregates." diff --git a/crates/squawk_syntax/src/syntax_error.rs b/crates/squawk_syntax/src/syntax_error.rs new file mode 100644 index 00000000..3297458a --- /dev/null +++ b/crates/squawk_syntax/src/syntax_error.rs @@ -0,0 +1,71 @@ +// via https://github.com/rust-lang/rust-analyzer/blob/d8887c0758bbd2d5f752d5bd405d4491e90e7ed6/crates/syntax/src/syntax_error.rs +// +// Permission is hereby granted, free of charge, to any +// person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the +// Software without restriction, including without +// limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +use std::fmt; + +use rowan::{TextRange, TextSize}; + +/// Represents the result of unsuccessful tokenization, parsing +/// or tree validation. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SyntaxError(String, TextRange); + +// FIXME: there was an unused SyntaxErrorKind previously (before this enum was removed) +// It was introduced in this PR: https://github.com/rust-lang/rust-analyzer/pull/846/files#diff-827da9b03b8f9faa1bade5cdd44d5dafR95 +// but it was not removed by a mistake. +// +// So, we need to find a place where to stick validation for attributes in match clauses. +// Code before refactor: +// InvalidMatchInnerAttr => { +// write!(f, "Inner attributes are only allowed directly after the opening brace of the match expression") +// } + +impl SyntaxError { + pub fn new(message: impl Into, range: TextRange) -> Self { + Self(message.into(), range) + } + pub fn new_at_offset(message: impl Into, offset: TextSize) -> Self { + Self(message.into(), TextRange::empty(offset)) + } + + pub fn range(&self) -> TextRange { + self.1 + } + + pub fn message(&self) -> &str { + &self.0 + } + pub fn with_range(mut self, range: TextRange) -> Self { + self.1 = range; + self + } +} + +impl fmt::Display for SyntaxError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} diff --git a/crates/squawk_syntax/src/syntax_node.rs b/crates/squawk_syntax/src/syntax_node.rs new file mode 100644 index 00000000..507f3bdf --- /dev/null +++ b/crates/squawk_syntax/src/syntax_node.rs @@ -0,0 +1,93 @@ +// via https://github.com/rust-lang/rust-analyzer/blob/d8887c0758bbd2d5f752d5bd405d4491e90e7ed6/crates/syntax/src/syntax_node.rs +// +// Permission is hereby granted, free of charge, to any +// person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the +// Software without restriction, including without +// limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +use rowan::{GreenNode, GreenNodeBuilder, Language, TextSize}; + +use crate::{syntax_error::SyntaxError, SyntaxKind}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Sql {} +impl Language for Sql { + type Kind = SyntaxKind; + + fn kind_from_raw(raw: rowan::SyntaxKind) -> SyntaxKind { + SyntaxKind::from(raw.0) + } + + fn kind_to_raw(kind: SyntaxKind) -> rowan::SyntaxKind { + rowan::SyntaxKind(kind.into()) + } +} + +pub type SyntaxNode = rowan::SyntaxNode; +pub type SyntaxToken = rowan::SyntaxToken; +// pub type SyntaxElement = rowan::SyntaxElement; +pub type SyntaxNodeChildren = rowan::SyntaxNodeChildren; +// pub type SyntaxElementChildren = rowan::SyntaxElementChildren; +// pub type PreorderWithTokens = rowan::api::PreorderWithTokens; + +#[derive(Default)] +pub struct SyntaxTreeBuilder { + errors: Vec, + inner: GreenNodeBuilder<'static>, +} + +impl SyntaxTreeBuilder { + pub(crate) fn finish_raw(self) -> (GreenNode, Vec) { + let green = self.inner.finish(); + (green, self.errors) + } + + // pub fn finish(self) -> Parse { + // let (green, errors) = self.finish_raw(); + // // // Disable block validation, see https://github.com/rust-lang/rust-analyzer/pull/10357 + // // #[allow(clippy::overly_complex_bool_expr)] + // // if cfg!(debug_assertions) && false { + // // let node = SyntaxNode::new_root(green.clone()); + // // crate::validation::validate_block_structure(&node); + // // } + // Parse::new(green, errors) + // } + + pub fn token(&mut self, kind: SyntaxKind, text: &str) { + let kind = Sql::kind_to_raw(kind); + self.inner.token(kind, text); + } + + pub fn start_node(&mut self, kind: SyntaxKind) { + let kind = Sql::kind_to_raw(kind); + self.inner.start_node(kind); + } + + pub fn finish_node(&mut self) { + self.inner.finish_node(); + } + + pub fn error(&mut self, error: String, text_pos: TextSize) { + self.errors + .push(SyntaxError::new_at_offset(error, text_pos)); + } +} diff --git a/crates/squawk_syntax/src/test.rs b/crates/squawk_syntax/src/test.rs new file mode 100644 index 00000000..ac09ddf4 --- /dev/null +++ b/crates/squawk_syntax/src/test.rs @@ -0,0 +1,51 @@ +// based on https://github.com/rust-lang/rust-analyzer/blob/d8887c0758bbd2d5f752d5bd405d4491e90e7ed6/crates/parser/src/tests.rs +use camino::Utf8Path; +use dir_test::{dir_test, Fixture}; +use insta::{assert_snapshot, with_settings}; + +use crate::SourceFile; + +#[dir_test( + dir: "$CARGO_MANIFEST_DIR/test_data", + glob: "**/*.sql", +)] +fn syntaxtest(fixture: Fixture<&str>) { + let content = fixture.content(); + let absolute_fixture_path = Utf8Path::new(fixture.path()); + let input_file = absolute_fixture_path; + let test_name = absolute_fixture_path + .file_name() + .and_then(|x| x.strip_suffix(".sql")) + .unwrap(); + + let parent_dir = input_file.parent().and_then(|x| x.file_name()).unwrap(); + let parse = SourceFile::parse(&content); + let mut buffer = format!("{:#?}", parse.syntax_node()); + let errors = parse.errors(); + for syntax_error in &errors { + let range = syntax_error.range(); + let text = syntax_error.message(); + // split into there own lines so that we can just grep + // for error without hitting this part + buffer += "\n"; + buffer += "ERROR"; + if range.start() == range.end() { + buffer += &format!("@{:?} {:?}", range.start(), text); + } else { + buffer += &format!("@{:?}:{:?} {:?}", range.start(), range.end(), text); + } + } + + with_settings!({ + omit_expression => true, + input_file => input_file, + }, { + assert_snapshot!(format!("{}_{}", test_name, parent_dir), buffer); + }); + + assert_ne!( + errors.len(), + 0, + "tests defined in the `syntax/test_data` must have errors." + ); +} diff --git a/crates/squawk_syntax/src/token_text.rs b/crates/squawk_syntax/src/token_text.rs new file mode 100644 index 00000000..fd890511 --- /dev/null +++ b/crates/squawk_syntax/src/token_text.rs @@ -0,0 +1,128 @@ +// via https://github.com/rust-lang/rust-analyzer/blob/d8887c0758bbd2d5f752d5bd405d4491e90e7ed6/crates/syntax/src/token_text.rs +// +// Permission is hereby granted, free of charge, to any +// person obtaining a copy of this software and associated +// documentation files (the "Software"), to deal in the +// Software without restriction, including without +// limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +//! Yet another version of owned string, backed by a syntax tree token. + +use std::{cmp::Ordering, fmt, ops}; + +use rowan::GreenToken; +use smol_str::SmolStr; + +pub struct TokenText<'a>(pub(crate) Repr<'a>); + +pub(crate) enum Repr<'a> { + Borrowed(&'a str), + Owned(GreenToken), +} + +impl<'a> TokenText<'a> { + pub fn borrowed(text: &'a str) -> Self { + TokenText(Repr::Borrowed(text)) + } + + pub(crate) fn owned(green: GreenToken) -> Self { + TokenText(Repr::Owned(green)) + } + + pub fn as_str(&self) -> &str { + match &self.0 { + &Repr::Borrowed(it) => it, + Repr::Owned(green) => green.text(), + } + } +} + +impl ops::Deref for TokenText<'_> { + type Target = str; + + fn deref(&self) -> &str { + self.as_str() + } +} +impl AsRef for TokenText<'_> { + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl From> for String { + fn from(token_text: TokenText<'_>) -> Self { + token_text.as_str().into() + } +} + +impl From> for SmolStr { + fn from(token_text: TokenText<'_>) -> Self { + SmolStr::new(token_text.as_str()) + } +} + +impl PartialEq<&'_ str> for TokenText<'_> { + fn eq(&self, other: &&str) -> bool { + self.as_str() == *other + } +} +impl PartialEq> for &'_ str { + fn eq(&self, other: &TokenText<'_>) -> bool { + other == self + } +} +impl PartialEq for TokenText<'_> { + fn eq(&self, other: &String) -> bool { + self.as_str() == other.as_str() + } +} +impl PartialEq> for String { + fn eq(&self, other: &TokenText<'_>) -> bool { + other == self + } +} +impl PartialEq for TokenText<'_> { + fn eq(&self, other: &TokenText<'_>) -> bool { + self.as_str() == other.as_str() + } +} +impl Eq for TokenText<'_> {} +impl Ord for TokenText<'_> { + fn cmp(&self, other: &Self) -> Ordering { + self.as_str().cmp(other.as_str()) + } +} +impl PartialOrd for TokenText<'_> { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} +impl fmt::Display for TokenText<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self.as_str(), f) + } +} +impl fmt::Debug for TokenText<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self.as_str(), f) + } +} diff --git a/crates/squawk_syntax/src/validation.rs b/crates/squawk_syntax/src/validation.rs new file mode 100644 index 00000000..8ef06c4e --- /dev/null +++ b/crates/squawk_syntax/src/validation.rs @@ -0,0 +1,106 @@ +// via: https://github.com/rust-lang/rust-analyzer/blob/d8887c0758bbd2d5f752d5bd405d4491e90e7ed6/crates/syntax/src/validation.rs + +//! This module implements syntax validation that the parser doesn't handle. +//! +//! A failed validation emits a diagnostic. + +use crate::ast::AstNode; +use crate::{ast, match_ast, syntax_error::SyntaxError, SyntaxNode}; +use parser::SyntaxKind::*; +use rowan::TextRange; + +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.param_list(), errors), + ast::CreateAggregate(it) => validate_aggregate_params(it.param_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), + _ => (), + } + } + } +} + +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()); + if matches!(parent_kind, Some(ARRAY_EXPR)) { + return; + } + let expr_range = array_expr.syntax().text_range(); + let range = TextRange::new(expr_range.start(), expr_range.start()); + acc.push(SyntaxError::new("Array missing ARRAY keyword.", range)); + } +} + +fn validate_prefix_expr(prefix_expr: ast::PrefixExpr, acc: &mut Vec) { + let Some(op) = prefix_expr + .syntax() + .children() + .into_iter() + .find_map(|x| ast::CustomOp::cast(x)) + else { + return; + }; + validate_custom_op(op, acc); +} + +// https://www.postgresql.org/docs/17/sql-createoperator.html +fn validate_custom_op(op: ast::CustomOp, acc: &mut Vec) { + // TODO: there's more we can validate + let mut found = 0; + for node_or_token in op.syntax().children_with_tokens().into_iter() { + match node_or_token { + rowan::NodeOrToken::Node(_) => (), + rowan::NodeOrToken::Token(_) => { + found += 1; + } + } + if found >= 2 { + return; + } + } + let token = op + .syntax() + .children_with_tokens() + .into_iter() + .find_map(|x| match x { + rowan::NodeOrToken::Node(_) => None, + rowan::NodeOrToken::Token(tk) => Some(tk.kind()), + }); + if let Some(STAR | SLASH | L_ANGLE | R_ANGLE | EQ | PERCENT | CARET) = token { + acc.push(SyntaxError::new( + "Invalid operator.", + op.syntax().text_range(), + )); + } +} + +fn validate_aggregate_params(aggregate_params: Option, acc: &mut Vec) { + if let Some(params) = aggregate_params { + for p in params.params() { + if let Some(mode) = p.mode() { + match mode { + ast::ParamMode::ParamOut(param_out) => acc.push(SyntaxError::new( + "Out params are not allowed with aggregates.", + param_out.syntax().text_range(), + )), + ast::ParamMode::ParamInOut(param_in_out) => acc.push(SyntaxError::new( + "In Out params are not allowed with aggregates.", + param_in_out.syntax().text_range(), + )), + ast::ParamMode::ParamIn(_) | ast::ParamMode::ParamVariadic(_) => (), + } + } + } + } +} diff --git a/crates/squawk_syntax/test_data/validation/alter_aggregate_params.sql b/crates/squawk_syntax/test_data/validation/alter_aggregate_params.sql new file mode 100644 index 00000000..3ce44c79 --- /dev/null +++ b/crates/squawk_syntax/test_data/validation/alter_aggregate_params.sql @@ -0,0 +1,5 @@ +-- can't have out params in aggregates +create aggregate a(in x, out y) ( + sfunc = f, + stype = t +); diff --git a/crates/squawk_syntax/test_data/validation/array_exprs.sql b/crates/squawk_syntax/test_data/validation/array_exprs.sql new file mode 100644 index 00000000..1985a2cb --- /dev/null +++ b/crates/squawk_syntax/test_data/validation/array_exprs.sql @@ -0,0 +1,3 @@ +select ['a', 'b', 'c']; + +select [[1, 2]]; diff --git a/crates/squawk_syntax/test_data/validation/create_aggregate_params.sql b/crates/squawk_syntax/test_data/validation/create_aggregate_params.sql new file mode 100644 index 00000000..9f07ea89 --- /dev/null +++ b/crates/squawk_syntax/test_data/validation/create_aggregate_params.sql @@ -0,0 +1,4 @@ +-- can't have out params in aggregates +alter aggregate a (in t, out u) + set schema s; + diff --git a/crates/squawk_syntax/test_data/validation/custom_operators.sql b/crates/squawk_syntax/test_data/validation/custom_operators.sql new file mode 100644 index 00000000..eb584ff3 --- /dev/null +++ b/crates/squawk_syntax/test_data/validation/custom_operators.sql @@ -0,0 +1,8 @@ +-- disallowed prefix operators +select *c; +select /d; +select f; +select =g; +select %l; +select ^m; diff --git a/crates/squawk_syntax/test_data/validation/drop_aggregate_params.sql b/crates/squawk_syntax/test_data/validation/drop_aggregate_params.sql new file mode 100644 index 00000000..13c6e8b1 --- /dev/null +++ b/crates/squawk_syntax/test_data/validation/drop_aggregate_params.sql @@ -0,0 +1,32 @@ + +-- full +drop aggregate + if exists + a(*), + foo.bar(*), + foo.bar( + in foo integer, + out bar integer, + text + ), + c(*) + cascade; + +-- aggregate +drop aggregate a( + integer, + text, + numeric + order by + in a timestamp, + out b numeric, + text +) restrict; + +drop aggregate foo.bar( + order by + in a timestamp, + out b numeric, + text +); + From 35288f8e9e895f5d8ea6a73d8ad0b0c5ef9e4df1 Mon Sep 17 00:00:00 2001 From: Steve Dignam Date: Sat, 3 May 2025 18:15:33 -0400 Subject: [PATCH 2/6] fix --- crates/squawk_syntax/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/squawk_syntax/Cargo.toml b/crates/squawk_syntax/Cargo.toml index e5f210c2..9378f0e1 100644 --- a/crates/squawk_syntax/Cargo.toml +++ b/crates/squawk_syntax/Cargo.toml @@ -7,7 +7,7 @@ authors.workspace = true license.workspace = true [dependencies] -parser.workspace = true +squawk_parser.workspace = true rowan.workspace = true smol_str.workspace = true From 1de33819db1d445c7e568d6b7986cffac4785018 Mon Sep 17 00:00:00 2001 From: Steve Dignam Date: Sat, 3 May 2025 18:16:52 -0400 Subject: [PATCH 3/6] fix --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 8f03ee50..cfd644b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ squawk-parser = { version = "0.0.0", path = "./crates/parser" } squawk-linter = { version = "0.0.0", path = "./crates/linter" } squawk-github = { version = "0.0.0", path = "./crates/github" } squawk_lexer = { version = "0.0.0", path = "./crates/squawk_lexer" } +squawk_parser = { version = "0.0.0", path = "./crates/squawk_parser" } [workspace.lints.clippy] collapsible_else_if = "allow" From c16e6f1c76532c2a33c1a28d342e81ac9335b55d Mon Sep 17 00:00:00 2001 From: Steve Dignam Date: Sat, 3 May 2025 18:18:47 -0400 Subject: [PATCH 4/6] rename --- crates/squawk_syntax/src/ast.rs | 2 +- crates/squawk_syntax/src/lib.rs | 2 +- crates/squawk_syntax/src/parsing.rs | 16 ++++++++-------- crates/squawk_syntax/src/validation.rs | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/crates/squawk_syntax/src/ast.rs b/crates/squawk_syntax/src/ast.rs index 4343298a..4acbb81b 100644 --- a/crates/squawk_syntax/src/ast.rs +++ b/crates/squawk_syntax/src/ast.rs @@ -33,7 +33,7 @@ mod node_ext; use std::marker::PhantomData; use crate::syntax_node::{SyntaxNode, SyntaxNodeChildren, SyntaxToken}; -use parser::SyntaxKind; +use squawk_parser::SyntaxKind; pub use self::{ nodes::*, diff --git a/crates/squawk_syntax/src/lib.rs b/crates/squawk_syntax/src/lib.rs index 73d7a4b9..d803b22c 100644 --- a/crates/squawk_syntax/src/lib.rs +++ b/crates/squawk_syntax/src/lib.rs @@ -36,7 +36,7 @@ mod test; use std::{marker::PhantomData, sync::Arc}; -pub use parser::SyntaxKind; +pub use squawk_parser::SyntaxKind; use ast::AstNode; use rowan::GreenNode; diff --git a/crates/squawk_syntax/src/parsing.rs b/crates/squawk_syntax/src/parsing.rs index 2ce3af42..01bc0ba9 100644 --- a/crates/squawk_syntax/src/parsing.rs +++ b/crates/squawk_syntax/src/parsing.rs @@ -32,9 +32,9 @@ use crate::{syntax_error::SyntaxError, syntax_node::SyntaxTreeBuilder}; // 2. run the parser over the tokens generating an array of events // 3. intersperse trivia TODO pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec) { - let lexed = parser::LexedStr::new(text); + let lexed = squawk_parser::LexedStr::new(text); let parser_input = lexed.to_input(); - let parser_output = parser::parse(&parser_input); + let parser_output = squawk_parser::parse(&parser_input); let (node, errors, _eof) = build_tree(lexed, parser_output); (node, errors) } @@ -77,16 +77,16 @@ pub(crate) fn parse_text(text: &str) -> (GreenNode, Vec) { // } pub(crate) fn build_tree( - lexed: parser::LexedStr<'_>, - parser_output: parser::Output, + lexed: squawk_parser::LexedStr<'_>, + parser_output: squawk_parser::Output, ) -> (GreenNode, Vec, bool) { let mut builder = SyntaxTreeBuilder::default(); let is_eof = lexed.intersperse_trivia(&parser_output, &mut |step| match step { - parser::StrStep::Token { kind, text } => builder.token(kind, text), - parser::StrStep::Enter { kind } => builder.start_node(kind), - parser::StrStep::Exit => builder.finish_node(), - parser::StrStep::Error { msg, pos } => { + squawk_parser::StrStep::Token { kind, text } => builder.token(kind, text), + squawk_parser::StrStep::Enter { kind } => builder.start_node(kind), + squawk_parser::StrStep::Exit => builder.finish_node(), + squawk_parser::StrStep::Error { msg, pos } => { builder.error(msg.to_owned(), pos.try_into().unwrap()) } }); diff --git a/crates/squawk_syntax/src/validation.rs b/crates/squawk_syntax/src/validation.rs index 8ef06c4e..69fa5988 100644 --- a/crates/squawk_syntax/src/validation.rs +++ b/crates/squawk_syntax/src/validation.rs @@ -6,8 +6,8 @@ use crate::ast::AstNode; use crate::{ast, match_ast, syntax_error::SyntaxError, SyntaxNode}; -use parser::SyntaxKind::*; use rowan::TextRange; +use squawk_parser::SyntaxKind::*; pub(crate) fn validate(root: &SyntaxNode, errors: &mut Vec) { for node in root.descendants() { From 51a4b1a6688fc0e7edccdeea697a1f931dfd9d6d Mon Sep 17 00:00:00 2001 From: Steve Dignam Date: Sat, 3 May 2025 18:23:37 -0400 Subject: [PATCH 5/6] fix --- crates/squawk_parser/src/test.rs | 6 +++--- crates/squawk_syntax/src/lib.rs | 2 +- crates/squawk_syntax/src/test.rs | 2 +- crates/squawk_syntax/src/validation.rs | 17 ++++++----------- 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/crates/squawk_parser/src/test.rs b/crates/squawk_parser/src/test.rs index 918e9968..ea2f705a 100644 --- a/crates/squawk_parser/src/test.rs +++ b/crates/squawk_parser/src/test.rs @@ -79,7 +79,7 @@ fn sqltest(fixture: Fixture<&str>) { let parent_dir = input_file.parent().and_then(|x| x.file_name()).unwrap(); - let (parsed, has_errors) = parse_text(&content); + let (parsed, has_errors) = parse_text(content); with_settings!({ omit_expression => true, @@ -97,12 +97,12 @@ fn sqltest(fixture: Fixture<&str>) { ); // skipping pg17 specific stuff since our parser isn't using the latest parser if !test_name.ends_with("pg17") { - let pg_result = pg_query::parse(&content); + let pg_result = pg_query::parse(content); if let Err(e) = &pg_result { assert!( &pg_result.is_ok(), "tests defined in the `ok` can't have Postgres parser errors. Found {}", - e.to_string() + e ); } } diff --git a/crates/squawk_syntax/src/lib.rs b/crates/squawk_syntax/src/lib.rs index d803b22c..0f045320 100644 --- a/crates/squawk_syntax/src/lib.rs +++ b/crates/squawk_syntax/src/lib.rs @@ -249,7 +249,7 @@ fn api_walkthrough() { // match only the top level enum: that is the price we pay for increased API // flexibility let func_option = func_option_list.options().next().unwrap(); - let option = match &func_option { + let option: &ast::AsFuncOption = match &func_option { ast::FuncOption::AsFuncOption(o) => o, _ => unreachable!(), }; diff --git a/crates/squawk_syntax/src/test.rs b/crates/squawk_syntax/src/test.rs index ac09ddf4..55d77be9 100644 --- a/crates/squawk_syntax/src/test.rs +++ b/crates/squawk_syntax/src/test.rs @@ -19,7 +19,7 @@ fn syntaxtest(fixture: Fixture<&str>) { .unwrap(); let parent_dir = input_file.parent().and_then(|x| x.file_name()).unwrap(); - let parse = SourceFile::parse(&content); + let parse = SourceFile::parse(content); let mut buffer = format!("{:#?}", parse.syntax_node()); let errors = parse.errors(); for syntax_error in &errors { diff --git a/crates/squawk_syntax/src/validation.rs b/crates/squawk_syntax/src/validation.rs index 69fa5988..b8f8542c 100644 --- a/crates/squawk_syntax/src/validation.rs +++ b/crates/squawk_syntax/src/validation.rs @@ -46,8 +46,7 @@ fn validate_prefix_expr(prefix_expr: ast::PrefixExpr, acc: &mut Vec let Some(op) = prefix_expr .syntax() .children() - .into_iter() - .find_map(|x| ast::CustomOp::cast(x)) + .find_map(ast::CustomOp::cast) else { return; }; @@ -58,7 +57,7 @@ fn validate_prefix_expr(prefix_expr: ast::PrefixExpr, acc: &mut Vec fn validate_custom_op(op: ast::CustomOp, acc: &mut Vec) { // TODO: there's more we can validate let mut found = 0; - for node_or_token in op.syntax().children_with_tokens().into_iter() { + for node_or_token in op.syntax().children_with_tokens() { match node_or_token { rowan::NodeOrToken::Node(_) => (), rowan::NodeOrToken::Token(_) => { @@ -69,14 +68,10 @@ fn validate_custom_op(op: ast::CustomOp, acc: &mut Vec) { return; } } - let token = op - .syntax() - .children_with_tokens() - .into_iter() - .find_map(|x| match x { - rowan::NodeOrToken::Node(_) => None, - rowan::NodeOrToken::Token(tk) => Some(tk.kind()), - }); + let token = op.syntax().children_with_tokens().find_map(|x| match x { + rowan::NodeOrToken::Node(_) => None, + rowan::NodeOrToken::Token(tk) => Some(tk.kind()), + }); if let Some(STAR | SLASH | L_ANGLE | R_ANGLE | EQ | PERCENT | CARET) = token { acc.push(SyntaxError::new( "Invalid operator.", From 3a70d0c25c6c690c030cb516e254278e4c6c9c2f Mon Sep 17 00:00:00 2001 From: Steve Dignam Date: Sat, 3 May 2025 18:31:46 -0400 Subject: [PATCH 6/6] fix --- ..._syntax__test__array_exprs_validation.snap | 52 +++++++------- ...ax__test__custom_operators_validation.snap | 71 +++++++++---------- 2 files changed, 57 insertions(+), 66 deletions(-) diff --git a/crates/squawk_syntax/src/snapshots/squawk_syntax__test__array_exprs_validation.snap b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__array_exprs_validation.snap index 1a5baeae..dcd5f199 100644 --- a/crates/squawk_syntax/src/snapshots/squawk_syntax__test__array_exprs_validation.snap +++ b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__array_exprs_validation.snap @@ -9,20 +9,19 @@ SOURCE_FILE@0..42 WHITESPACE@6..7 " " TARGET_LIST@7..22 TARGET@7..22 - COL_LABEL@7..22 - ARRAY_EXPR@7..22 - L_BRACK@7..8 "[" - LITERAL@8..11 - STRING@8..11 "'a'" - COMMA@11..12 "," - WHITESPACE@12..13 " " - LITERAL@13..16 - STRING@13..16 "'b'" - COMMA@16..17 "," - WHITESPACE@17..18 " " - LITERAL@18..21 - STRING@18..21 "'c'" - R_BRACK@21..22 "]" + ARRAY_EXPR@7..22 + L_BRACK@7..8 "[" + LITERAL@8..11 + STRING@8..11 "'a'" + COMMA@11..12 "," + WHITESPACE@12..13 " " + LITERAL@13..16 + STRING@13..16 "'b'" + COMMA@16..17 "," + WHITESPACE@17..18 " " + LITERAL@18..21 + STRING@18..21 "'c'" + R_BRACK@21..22 "]" SEMICOLON@22..23 ";" WHITESPACE@23..25 "\n\n" SELECT@25..40 @@ -31,19 +30,18 @@ SOURCE_FILE@0..42 WHITESPACE@31..32 " " TARGET_LIST@32..40 TARGET@32..40 - COL_LABEL@32..40 - ARRAY_EXPR@32..40 - L_BRACK@32..33 "[" - ARRAY_EXPR@33..39 - L_BRACK@33..34 "[" - LITERAL@34..35 - INT_NUMBER@34..35 "1" - COMMA@35..36 "," - WHITESPACE@36..37 " " - LITERAL@37..38 - INT_NUMBER@37..38 "2" - R_BRACK@38..39 "]" - R_BRACK@39..40 "]" + ARRAY_EXPR@32..40 + L_BRACK@32..33 "[" + ARRAY_EXPR@33..39 + L_BRACK@33..34 "[" + LITERAL@34..35 + INT_NUMBER@34..35 "1" + COMMA@35..36 "," + WHITESPACE@36..37 " " + LITERAL@37..38 + INT_NUMBER@37..38 "2" + R_BRACK@38..39 "]" + R_BRACK@39..40 "]" SEMICOLON@40..41 ";" WHITESPACE@41..42 "\n" diff --git a/crates/squawk_syntax/src/snapshots/squawk_syntax__test__custom_operators_validation.snap b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__custom_operators_validation.snap index 1b21e0fa..77003b51 100644 --- a/crates/squawk_syntax/src/snapshots/squawk_syntax__test__custom_operators_validation.snap +++ b/crates/squawk_syntax/src/snapshots/squawk_syntax__test__custom_operators_validation.snap @@ -13,9 +13,8 @@ SOURCE_FILE@0..108 TARGET@38..39 STAR@38..39 "*" TARGET@39..40 - COL_LABEL@39..40 - NAME_REF@39..40 - IDENT@39..40 "c" + NAME_REF@39..40 + IDENT@39..40 "c" SEMICOLON@40..41 ";" WHITESPACE@41..42 "\n" SELECT@42..51 @@ -24,12 +23,11 @@ SOURCE_FILE@0..108 WHITESPACE@48..49 " " TARGET_LIST@49..51 TARGET@49..51 - COL_LABEL@49..51 - PREFIX_EXPR@49..51 - CUSTOM_OP@49..50 - SLASH@49..50 "/" - NAME_REF@50..51 - IDENT@50..51 "d" + PREFIX_EXPR@49..51 + CUSTOM_OP@49..50 + SLASH@49..50 "/" + NAME_REF@50..51 + IDENT@50..51 "d" SEMICOLON@51..52 ";" WHITESPACE@52..53 "\n" SELECT@53..62 @@ -38,12 +36,11 @@ SOURCE_FILE@0..108 WHITESPACE@59..60 " " TARGET_LIST@60..62 TARGET@60..62 - COL_LABEL@60..62 - PREFIX_EXPR@60..62 - CUSTOM_OP@60..61 - L_ANGLE@60..61 "<" - NAME_REF@61..62 - IDENT@61..62 "e" + PREFIX_EXPR@60..62 + CUSTOM_OP@60..61 + L_ANGLE@60..61 "<" + NAME_REF@61..62 + IDENT@61..62 "e" SEMICOLON@62..63 ";" WHITESPACE@63..64 "\n" SELECT@64..73 @@ -52,12 +49,11 @@ SOURCE_FILE@0..108 WHITESPACE@70..71 " " TARGET_LIST@71..73 TARGET@71..73 - COL_LABEL@71..73 - PREFIX_EXPR@71..73 - CUSTOM_OP@71..72 - R_ANGLE@71..72 ">" - NAME_REF@72..73 - IDENT@72..73 "f" + PREFIX_EXPR@71..73 + CUSTOM_OP@71..72 + R_ANGLE@71..72 ">" + NAME_REF@72..73 + IDENT@72..73 "f" SEMICOLON@73..74 ";" WHITESPACE@74..75 "\n" SELECT@75..84 @@ -66,12 +62,11 @@ SOURCE_FILE@0..108 WHITESPACE@81..82 " " TARGET_LIST@82..84 TARGET@82..84 - COL_LABEL@82..84 - PREFIX_EXPR@82..84 - CUSTOM_OP@82..83 - EQ@82..83 "=" - NAME_REF@83..84 - IDENT@83..84 "g" + PREFIX_EXPR@82..84 + CUSTOM_OP@82..83 + EQ@82..83 "=" + NAME_REF@83..84 + IDENT@83..84 "g" SEMICOLON@84..85 ";" WHITESPACE@85..86 "\n" SELECT@86..95 @@ -80,12 +75,11 @@ SOURCE_FILE@0..108 WHITESPACE@92..93 " " TARGET_LIST@93..95 TARGET@93..95 - COL_LABEL@93..95 - PREFIX_EXPR@93..95 - CUSTOM_OP@93..94 - PERCENT@93..94 "%" - NAME_REF@94..95 - IDENT@94..95 "l" + PREFIX_EXPR@93..95 + CUSTOM_OP@93..94 + PERCENT@93..94 "%" + NAME_REF@94..95 + IDENT@94..95 "l" SEMICOLON@95..96 ";" WHITESPACE@96..97 "\n" SELECT@97..106 @@ -94,12 +88,11 @@ SOURCE_FILE@0..108 WHITESPACE@103..104 " " TARGET_LIST@104..106 TARGET@104..106 - COL_LABEL@104..106 - PREFIX_EXPR@104..106 - CUSTOM_OP@104..105 - CARET@104..105 "^" - NAME_REF@105..106 - IDENT@105..106 "m" + PREFIX_EXPR@104..106 + CUSTOM_OP@104..105 + CARET@104..105 "^" + NAME_REF@105..106 + IDENT@105..106 "m" SEMICOLON@106..107 ";" WHITESPACE@107..108 "\n"