From 2de6e082612f12de40cb862af2986b0b268c4fe7 Mon Sep 17 00:00:00 2001 From: Josh Pschorr Date: Wed, 26 Mar 2025 13:22:39 -0700 Subject: [PATCH 1/9] GRAPH_TABLE Syntax --- partiql-parser/src/lexer/partiql.rs | 344 +++++++--- partiql-parser/src/parse/partiql.lalrpop | 794 ++++++++++++++++------- 2 files changed, 820 insertions(+), 318 deletions(-) diff --git a/partiql-parser/src/lexer/partiql.rs b/partiql-parser/src/lexer/partiql.rs index a8a494f9..62855b35 100644 --- a/partiql-parser/src/lexer/partiql.rs +++ b/partiql-parser/src/lexer/partiql.rs @@ -203,8 +203,6 @@ pub enum Token<'input> { Plus, #[token("*")] Star, - #[token("?")] - SqlParameter, #[token("%")] Percent, #[token("/")] @@ -213,10 +211,64 @@ pub enum Token<'input> { Caret, #[token(".")] Period, - #[token("~")] - Tilde, #[token("||")] DblPipe, + #[token("|")] + Pipe, + #[token("&")] + Ampersand, + #[token("!")] + Bang, + #[token("?")] + QuestionMark, + + // Graph Symbols + #[token("|+|")] + PipePlusPipe, + #[token("<-")] + LeftArrow, + #[token("~")] + Tilde, + #[token("->")] + RightArrow, + #[token("<~")] + LeftArrowTilde, + #[token("~>")] + TildeRightArrow, + #[token("<-[")] + LeftArrowBracket, + #[token("]-")] + RightBracketMinus, + #[token("~[")] + TildeLeftBracket, + #[token("]~")] + RightBracketTilde, + #[token("-[")] + MinusLeftBracket, + #[token("]->")] + BracketRightArrow, + #[token("<~[")] + LeftArrowTildeBracket, + #[token("]~>")] + BracketTildeRightArrow, + #[token("<->")] + LeftMinusRight, + #[token("<-/")] + LeftArrowSlash, + #[token("/-")] + RightSlashMinus, + #[token("~/")] + TildeLeftSlash, + #[token("/~")] + RightSlashTilde, + #[token("-/")] + MinusLeftSlash, + #[token("/->")] + SlashRightArrow, + #[token("<~/")] + LeftArrowTildeSlash, + #[token("/~>")] + SlashTildeRightArrow, // unquoted identifiers #[regex("[a-zA-Z_$][a-zA-Z0-9_$]*", |lex| lex.slice())] @@ -262,8 +314,6 @@ pub enum Token<'input> { EmbeddedDoc(&'input str), // Keywords - #[regex("(?i:Acyclic)")] - Acyclic, #[regex("(?i:All)")] All, #[regex("(?i:Asc)")] @@ -282,6 +332,8 @@ pub enum Token<'input> { By, #[regex("(?i:Case)")] Case, + #[regex("(?i:Columns)")] + Columns, #[regex("(?i:Cross)")] Cross, #[regex("(?i:Cycle)")] @@ -292,6 +344,8 @@ pub enum Token<'input> { Desc, #[regex("(?i:Distinct)")] Distinct, + #[regex("(?i:Element)")] + Element, #[regex("(?i:Else)")] Else, #[regex("(?i:End)")] @@ -302,6 +356,8 @@ pub enum Token<'input> { Except, #[regex("(?i:Exclude)")] Exclude, + #[regex("(?i:Export)")] + Export, #[regex("(?i:False)")] False, #[regex("(?i:First)")] @@ -314,6 +370,8 @@ pub enum Token<'input> { From, #[regex("(?i:Group)")] Group, + #[regex("(?i:Groups)")] + Groups, #[regex("(?i:Having)")] Having, #[regex("(?i:In)")] @@ -326,6 +384,8 @@ pub enum Token<'input> { Intersect, #[regex("(?i:Join)")] Join, + #[regex("(?i:Keep)")] + Keep, #[regex("(?i:Last)")] Last, #[regex("(?i:Lateral)")] @@ -342,6 +402,8 @@ pub enum Token<'input> { Missing, #[regex("(?i:Natural)")] Natural, + #[regex("(?i:No)")] + No, #[regex("(?i:Not)")] Not, #[regex("(?i:Null)")] @@ -352,22 +414,32 @@ pub enum Token<'input> { Offset, #[regex("(?i:On)")] On, + #[regex("(?i:One)")] + One, #[regex("(?i:Or)")] Or, #[regex("(?i:Order)")] Order, #[regex("(?i:Outer)")] Outer, + #[regex("(?i:Path)")] + Path, #[regex("(?i:Partial)")] Partial, + #[regex("(?i:Per)")] + Per, #[regex("(?i:Pivot)")] Pivot, #[regex("(?i:Preserve)")] Preserve, - #[regex("(?i:Right)")] - Right, #[regex("(?i:Recursive)")] Recursive, + #[regex("(?i:REPEATABLE)")] + Repeatable, + #[regex("(?i:Right)")] + Right, + #[regex("(?i:Row)")] + Row, #[regex("(?i:Select)")] Select, #[regex("(?i:Search)")] @@ -380,12 +452,8 @@ pub enum Token<'input> { Timestamp, #[regex("(?i:Simple)")] Simple, - #[regex("(?i:Shortest)")] - Shortest, #[regex("(?i:Then)")] Then, - #[regex("(?i:Trail)")] - Trail, #[regex("(?i:True)")] True, #[regex("(?i:Union)")] @@ -408,21 +476,151 @@ pub enum Token<'input> { Without, #[regex("(?i:Zone)")] Zone, + + // Graph Keywords; reserved + #[regex("(?i:ALL_DIFFERENT)")] + AllDifferent, + #[regex("(?i:BINDING_COUNT)")] + BindingCount, + #[regex("(?i:ELEMENT_ID)")] + ElementId, + #[regex("(?i:ELEMENT_NUMBER)")] + ElementNumber, + #[regex("(?i:GRAPH)")] + Graph, + #[regex("(?i:GRAPH_TABLE)")] + GraphTable, + #[regex("(?i:MATCHNUM)")] + MatchNum, + #[regex("(?i:PATH_LENGTH)")] + PathLength, + #[regex("(?i:PATH_NAME)")] + PathName, + #[regex("(?i:PROPERTY_EXISTS)")] + PropertyExists, + #[regex("(?i:SAME)")] + Same, + + // Graph Keywords; non-reserved + #[regex("(?i:ACYCLIC)")] + Acyclic, + #[regex("(?i:BINDINGS)")] + Bindings, + #[regex("(?i:BOUND)")] + Bound, + #[regex("(?i:DESTINATION)")] + Destination, + #[regex("(?i:DIFFERENT)")] + Different, + #[regex("(?i:DIRECTED)")] + Directed, + #[regex("(?i:EDGE)")] + Edge, + #[regex("(?i:EDGES)")] + Edges, + #[regex("(?i:ELEMENTS)")] + Elements, + #[regex("(?i:LABEL)")] + Label, + #[regex("(?i:LABELED)")] + Labeled, + #[regex("(?i:NODE)")] + Node, + #[regex("(?i:PATHS)")] + Paths, + #[regex("(?i:PROPERTIES)")] + Properties, + #[regex("(?i:PROPERTY)")] + Property, + #[regex("(?i:PROPERTY_GRAPH_CATALOG)")] + PropertyGraphCatalog, + #[regex("(?i:PROPERTY_GRAPH_NAME)")] + PropertyGraphName, + #[regex("(?i:PROPERTY_GRAPH_SCHEMA)")] + PropertyGraphSchema, + #[regex("(?i:RELATIONSHIP)")] + Relationship, + #[regex("(?i:RELATIONSHIPS)")] + Relationships, + #[regex("(?i:SHORTEST)")] + Shortest, + #[regex("(?i:SINGLETONS)")] + Singletons, + #[regex("(?i:STEP)")] + Step, + #[regex("(?i:TABLES)")] + Tables, + #[regex("(?i:TRAIL)")] + Trail, + #[regex("(?i:VERTEX)")] + Vertex, + #[regex("(?i:WALK)")] + Walk, } impl Token<'_> { + #[inline] pub fn is_var_non_reserved(&self) -> bool { + matches!(self, Token::Any | Token::Simple) || self.is_graph_non_reserved() + } + + #[inline] + pub fn is_fn_non_reserved(&self) -> bool { + matches!(self, Token::Any | Token::Simple) || self.is_graph_non_reserved() + } + + #[inline] + pub fn is_graph_reserved(&self) -> bool { matches!( self, - Token::Acyclic | Token::Any | Token::Simple | Token::Shortest | Token::Trail + Token::AllDifferent + | Token::BindingCount + | Token::ElementId + | Token::ElementNumber + | Token::Graph + | Token::GraphTable + | Token::MatchNum + | Token::PathLength + | Token::PathName + | Token::PropertyExists + | Token::Same ) } - pub fn is_fn_non_reserved(&self) -> bool { + + #[inline] + pub fn is_graph_non_reserved(&self) -> bool { matches!( self, - Token::Acyclic | Token::Any | Token::Simple | Token::Shortest | Token::Trail + Token::Acyclic + | Token::Bindings + | Token::Bound + | Token::Destination + | Token::Different + | Token::Directed + | Token::Edge + | Token::Edges + | Token::Elements + | Token::Label + | Token::Labeled + | Token::Node + | Token::Paths + | Token::Properties + | Token::Property + | Token::PropertyGraphCatalog + | Token::PropertyGraphName + | Token::PropertyGraphSchema + | Token::Relationship + | Token::Relationships + | Token::Shortest + | Token::Singletons + | Token::Step + | Token::Tables + | Token::Trail + | Token::Vertex + | Token::Walk ) } + pub fn is_keyword(&self) -> bool { matches!( self, @@ -441,6 +639,7 @@ impl Token<'_> { | Token::Date | Token::Desc | Token::Distinct + | Token::Element | Token::Escape | Token::Except | Token::First @@ -448,12 +647,14 @@ impl Token<'_> { | Token::Full | Token::From | Token::Group + | Token::Groups | Token::Having | Token::In | Token::Inner | Token::Is | Token::Intersect | Token::Join + | Token::Keep | Token::Last | Token::Lateral | Token::Left @@ -470,11 +671,13 @@ impl Token<'_> { | Token::Or | Token::Order | Token::Outer + | Token::Path | Token::Partial | Token::Pivot | Token::Preserve | Token::Right | Token::Recursive + | Token::Repeatable | Token::Search | Token::Select | Token::Table @@ -491,7 +694,8 @@ impl Token<'_> { | Token::Values | Token::Where | Token::With - ) + ) || self.is_graph_reserved() + || self.is_graph_non_reserved() } } @@ -524,13 +728,41 @@ impl fmt::Display for Token<'_> { Token::Minus => write!(f, "-"), Token::Plus => write!(f, "+"), Token::Star => write!(f, "*"), - Token::SqlParameter => write!(f, "?"), Token::Percent => write!(f, "%"), Token::Slash => write!(f, "/"), Token::Caret => write!(f, "^"), Token::Period => write!(f, "."), - Token::Tilde => write!(f, "~"), Token::DblPipe => write!(f, "||"), + Token::Pipe => write!(f, "|"), + Token::Ampersand => write!(f, "&"), + Token::Bang => write!(f, "!"), + Token::QuestionMark => write!(f, "?"), + // graph symbols + Token::PipePlusPipe => write!(f, "|+|"), + Token::LeftArrow => write!(f, "<-"), + Token::Tilde => write!(f, "~"), + Token::RightArrow => write!(f, "->"), + Token::LeftArrowTilde => write!(f, "<~"), + Token::TildeRightArrow => write!(f, "~>"), + Token::LeftArrowBracket => write!(f, "<-["), + Token::RightBracketMinus => write!(f, "]-"), + Token::TildeLeftBracket => write!(f, "~["), + Token::RightBracketTilde => write!(f, "]~"), + Token::MinusLeftBracket => write!(f, "-["), + Token::BracketRightArrow => write!(f, "]->"), + Token::LeftArrowTildeBracket => write!(f, "<~["), + Token::BracketTildeRightArrow => write!(f, "]~>"), + Token::LeftMinusRight => write!(f, "<->"), + Token::LeftArrowSlash => write!(f, "<-/"), + Token::RightSlashMinus => write!(f, "/-"), + Token::TildeLeftSlash => write!(f, "~/"), + Token::RightSlashTilde => write!(f, "/~"), + Token::MinusLeftSlash => write!(f, "-/"), + Token::SlashRightArrow => write!(f, "/->"), + Token::LeftArrowTildeSlash => write!(f, "<~/"), + Token::SlashTildeRightArrow => write!(f, "/~>"), + + // other Token::UnquotedIdent(id) => write!(f, "<{id}:UNQUOTED_IDENT>"), Token::QuotedIdent(id) => write!(f, "<{id}:QUOTED_IDENT>"), Token::UnquotedAtIdentifier(id) => write!(f, "<{id}:UNQUOTED_ATIDENT>"), @@ -542,80 +774,8 @@ impl fmt::Display for Token<'_> { Token::EmbeddedDocQuote => write!(f, ""), Token::EmbeddedDoc(txt) => write!(f, "<```{txt}```:DOC>"), Token::EmptyEmbeddedDocQuote => write!(f, "<``:DOC>"), - - Token::Acyclic - | Token::All - | Token::Asc - | Token::And - | Token::Any - | Token::As - | Token::At - | Token::Between - | Token::By - | Token::Case - | Token::Cross - | Token::Cycle - | Token::Date - | Token::Desc - | Token::Distinct - | Token::Else - | Token::End - | Token::Escape - | Token::Except - | Token::Exclude - | Token::False - | Token::First - | Token::For - | Token::Full - | Token::From - | Token::Group - | Token::Having - | Token::In - | Token::Inner - | Token::Is - | Token::Intersect - | Token::Join - | Token::Last - | Token::Lateral - | Token::Left - | Token::Like - | Token::Limit - | Token::Match - | Token::Missing - | Token::Natural - | Token::Not - | Token::Null - | Token::Nulls - | Token::Offset - | Token::On - | Token::Or - | Token::Order - | Token::Outer - | Token::Partial - | Token::Pivot - | Token::Preserve - | Token::Right - | Token::Recursive - | Token::Search - | Token::Select - | Token::Table - | Token::Time - | Token::Timestamp - | Token::Simple - | Token::Shortest - | Token::Then - | Token::Trail - | Token::True - | Token::Union - | Token::Unpivot - | Token::Using - | Token::Value - | Token::Values - | Token::When - | Token::Where - | Token::With - | Token::Without - | Token::Zone => { + // keywords + _ => { write!(f, "{}", format!("{self:?}").to_uppercase()) } } diff --git a/partiql-parser/src/parse/partiql.lalrpop b/partiql-parser/src/parse/partiql.lalrpop index df131416..dac119f2 100644 --- a/partiql-parser/src/parse/partiql.lalrpop +++ b/partiql-parser/src/parse/partiql.lalrpop @@ -334,17 +334,446 @@ TableBaseReference: ast::AstNode = { by_alias }, lo..hi) }, - => { + , +} + + +// 7.1 + +GraphTable: ast::AstNode = { + "GRAPH_TABLE" "(" ")" => { + // TODO: Dummy node for parser testing state.node(ast::FromLet { - expr: Box::new(e), + expr: Box::new(ast::Expr::Error), // TODO kind: ast::FromLetKind::Scan, - as_alias, - at_alias, - by_alias + as_alias: None, + at_alias: None, + by_alias: None, }, lo..hi) }, } +GraphTableShape: () = { + , + , + , +} + +GraphTableRowsClause: () = { + "ONE" "ROW" "PER" "MATCH" => { + todo!("ONE ROW PER MATCH") + }, + "ONE" "ROW" "PER" + "(" ")" + => { + todo!("ONE ROW PER VERTEX") + }, + "ONE" "ROW" "PER" "STEP" + "(" "," "," ")" + => { + todo!("ONE ROW PER STEP") + }, +} + +GraphInPathsClause: () = { + "IN" "(" > ")" => { + todo!("GRAPH IN PATHS CLAUSE") + } +} + +GraphTableColumnsClause: () = { + "COLUMNS" "(" > ")" => { + todo!("GRAPH COLUMNS") + } +} + +GraphTableColumnDef: () = { + "." "*" => { + todo!("GRAPH COLUMN DEFINITION") + } +} + +GraphTableExportClause: () = { + "EXPORT" "ALL" "SINGLETONS" => { + todo!("EXPORT ALL SINGLETONS") + }, + "EXPORT" "SINGLETONS" "(" > ")" => { + todo!("EXPORT SINGLETONS") + }, + "EXPORT" "NO" "SINGLETONS" => { + todo!("EXPORT NO SINGLETONS") + } +} + +#[inline] +GraphTableExportExcept: () = { + "EXCEPT" "(" > ")" => { + todo!("GRAPH EXPORT EXCEPT") + }, +} + +GraphRefMatch: () = { + "MATCH" => { + todo!("GRAPH MATCH") + }, +} + +#[inline] +GraphReference = ; + +// 10.4 + +GraphPattern: () = { + => { + () + }, +} + +GraphMatchMode: () = { + "REPEATABLE" "ELEMENT" "BINDINGS"? => todo!("GRAPH MATCH MODE"), + "REPEATABLE" "ELEMENTS" => todo!("GRAPH MATCH MODE"), + "DIFFERENT" "BINDINGS"? => todo!("GRAPH MATCH MODE"), + "DIFFERENT" => todo!("GRAPH MATCH MODE"), +} + +GraphPathPatternList: () = { + => { + todo!("Path Pattern") + } +} + +GraphPathVariableDecl: () = { + "=" => { + todo!("Path Variable Decl") + } +} + +GraphPathPattern: () = { + > => { + todo!("Path Pattern List") + } +} + +GraphKeepClause: () = { + "KEEP" => todo!("GRAPH KEEP CLAUSE"), +} + +GraphPatternWhereClause: () = { + "WHERE" => todo!("GRAPH WHERE CLAUSE"), +} + +// 10.5 + +GraphPathPatternPrefix: () = { + , + , +} + +GraphPathModePrefix: () = { + => todo!("GRAPH PATH MODE PREFIX"), +} + +#[inline] +GraphPathOrPaths= { + "PATH" , "PATHS" +} + +#[inline] +GraphPathMode: () = { + "WALK" => todo!("GraphPathMode"), + "TRAIL" => todo!("GraphPathMode"), + "SIMPLE" => todo!("GraphPathMode"), + "ACYCLIC" => todo!("GraphPathMode"), +} + +#[inline] +GraphPathSearchPrefix: () = { + "ALL" => todo!("GraphPathSearchPrefix"), + "ANY" => todo!("GraphPathSearchPrefix"), + "ALL" "SHORTEST" => todo!("GraphPathSearchPrefix"), + "ANY" "SHORTEST" => todo!("GraphPathSearchPrefix"), + "SHORTEST" => todo!("GraphPathSearchPrefix"), + "SHORTEST" "GROUP" => todo!("GraphPathSearchPrefix"), + "SHORTEST" "GROUPS" => todo!("GraphPathSearchPrefix"), +} + +// 10.6 + +GraphPathPatternExpr: () = { + => todo!("GRAPH Path Pattern expression"), + => todo!("GRAPH Path Pattern expression"), + => todo!("GRAPH Path Pattern expression"), +} + +GraphPathMultisetAlternation: () = { + > => todo!("GRAPH Path Pattern multiset"), +} + +GraphPathMultisetAlternationContinue: () = { + "|+|" => todo!("GRAPH Path Pattern multiset"), +} + +GraphPathUnion: () = { + > => todo!("GRAPH Path Pattern unio"), +} + +GraphPathUnionContinue: () = { + "|" => todo!("GRAPH Path Pattern union"), +} + +GraphPathTerm: () = { + => todo!("GRAPH Path Pattern term"), + => todo!("GRAPH Path Pattern term"), +} + +GraphPathFactor: () = { + => todo!("GRAPH Path Pattern factor"), + => todo!("GRAPH Path Pattern factor"), + "?" => todo!("GRAPH Path Pattern factor"), +} + +GraphPathPrimary: () = { + => todo!("GRAPH Path Pattern primary"), + => todo!("GRAPH Path Pattern primary"), + => todo!("GRAPH Path Pattern primary"), +} + +GraphElementPattern: () = { + => todo!("GRAPH Element Pattern"), + => todo!("GRAPH Element Pattern"), +} + +GraphVertexPattern: () = { + "(" ")" => todo!("GRAPH Vertex Pattern"), +} + +GraphElementPatternFiller: () = { + + => todo!("GRAPH Element Pattern Filler"), +} + +#[inline] +GraphElementVariableDecl = ; + +GraphIsLabelExpression: () = { + ":" => todo!("GRAPH is label expr"), + "IS" => todo!("GRAPH is label expr"), +} + +GraphElementPatternWhereClause: () = { + "WHERE" => todo!("GRAPH element WHERE CLAUSE"), +} + +GraphEdgePattern: () = { + , + , +} + +GraphFullEdgePattern: () = { + "<-[" "]-" => todo!("GRAPH Edge Pattern"), + "~[" "]~" => todo!("GRAPH Edge Pattern"), + "-[" "]->" => todo!("GRAPH Edge Pattern"), + "<~[" "]~" => todo!("GRAPH Edge Pattern"), + "~[" "]~>" => todo!("GRAPH Edge Pattern"), + "<-[" "]->" => todo!("GRAPH Edge Pattern"), + "-[" "]-" => todo!("GRAPH Edge Pattern"), +} + +GraphAbbreviatedEdgePattern: () = { + "<-" => todo!("GRAPH Edge Pattern"), + "~" => todo!("GRAPH Edge Pattern"), + "->" => todo!("GRAPH Edge Pattern"), + "<~" => todo!("GRAPH Edge Pattern"), + "~>" => todo!("GRAPH Edge Pattern"), + "<->" => todo!("GRAPH Edge Pattern"), + "-" => todo!("GRAPH Edge Pattern"), +} + + +GraphParenthesizedPathPatternExpr: () = { + "(" + + + + ")" => todo!("GRAPH Paren Path Pattern "), +} + +GraphSubPathVariableDecl: () = { + "=" => { + todo!("SubPath Variable Decl") + } +} + +GraphParenthesizedPathPatternWhereClause: () = { + "WHERE" => todo!("GRAPH SubPath WHERE CLAUSE"), +} + + +// 10.7 + +#[inline] +GraphPatternQuantifier: () = { + "*" => todo!("GraphPatternQuantifier"), + "+" => todo!("GraphPatternQuantifier"), + "{" "}" => todo!("GraphPatternQuantifier"), + "{" "," "}" => todo!("GraphPatternQuantifier"), +} + +// 10.8 + +GraphLabelExpr: () = { + => todo!("GraphLabelExpr"), + => todo!("GraphLabelExpr"), +} + +GraphLabelDisjunction: () = { + "|" => todo!("GraphLabelDisjunction"), +} + +GraphLabelTerm: () = { + => todo!("GraphLabelTerm"), + => todo!("GraphLabelTerm"), +} + +GraphLabelConjunction: () = { + "&" => todo!("GraphLabelConjunction"), +} + +GraphLabelFactor: () = { + => todo!("GraphLabelFactor"), +} + +#[inline] +GraphLabelPrimary: () = { + => todo!("GraphLabelPrimary"), + "%" => todo!("GraphLabelPrimary"), + "(" ")" => todo!("GraphLabelPrimary"), + +} + +// 10.9 +GraphSimplifiedPathPatternExpression: () = { + "<-/" "/-" => todo!("GraphSimplifiedPathPatternExpression"), + "~/" "/~" => todo!("GraphSimplifiedPathPatternExpression"), + "-/" "/->" => todo!("GraphSimplifiedPathPatternExpression"), + "<~/" "/~" => todo!("GraphSimplifiedPathPatternExpression"), + "~/" "/~>" => todo!("GraphSimplifiedPathPatternExpression"), + "<-/" "/->" => todo!("GraphSimplifiedPathPatternExpression"), + "-/" "/-" => todo!("GraphSimplifiedPathPatternExpression"), +} + +GraphSimplifiedContents: () = { + , + , + , +} + +GraphSimplifiedMultiSetAlternation: () = { + > => todo!("GRAPH Path Pattern multiset"), +} + +GraphSimplifiedMultiSetAlternationContinue: () = { + "|+|" => todo!("GRAPH Path Pattern multiset"), +} + +GraphSimplifiedPathUnion: () = { + > => todo!("GRAPH Path Pattern unio"), +} + +GraphSimplifiedPathUnionContinue: () = { + "|" => todo!("GRAPH Path Pattern union"), +} + +GraphSimplifiedTerm: () = { + => todo!("GraphSimplifiedTerm"), +} + +GraphSimplifiedFactorLow: () = { + => todo!("GraphSimplifiedFactorLow"), + => todo!("GraphSimplifiedFactorLow"), +} + +GraphSimplifiedConjunction: () = { + "&" => todo!("GraphSimplifiedConjunction"), +} + +GraphSimplifiedFactorHigh: () = { + => todo!("GraphSimplifiedFactorHigh"), + => todo!("GraphSimplifiedFactorHigh"), + "?" => todo!("GraphSimplifiedFactorHigh"), +} + +GraphSimplifiedTertiary: () = { + => todo!("GraphSimplifiedTertiary"), + "<" => todo!("GraphSimplifiedTertiary"), + "~" => todo!("GraphSimplifiedTertiary"), + ">" => todo!("GraphSimplifiedTertiary"), + "<~" => todo!("GraphSimplifiedTertiary"), + "~" ">" => todo!("GraphSimplifiedTertiary"), + "<" ">" => todo!("GraphSimplifiedTertiary"), + "-" => todo!("GraphSimplifiedTertiary"), +} + +GraphSimplifiedSecondary: () = { + => todo!("GraphSimplifiedSecondary"), + => todo!("GraphSimplifiedSecondary"), +} + +GraphSimplifiedNegation: () = { + => todo!("GraphLabelFactor"), +} + +GraphSimplifiedPrimary: () = { + => todo!("GraphSimplifiedPrimary"), + "(" ")" => todo!("GraphSimplifiedPrimary"), +} + +// 10.10 +#[inline] +GraphElementReference = GraphElementVariable; + +// 10.11 +#[inline] +GraphPathReference = GraphPathVariable; + +// 5.3 + + +#[inline] +GraphName = ; + +#[inline] +GraphVertexVariable = ; + +#[inline] +GraphEdgeVariable = ; + +#[inline] +GraphElementVariable: () = { + => { () } +} +#[inline] +GraphLabelName: () = { + => { () } +} +#[inline] +GraphPathOrSubPathVariable: () = { + //, + //, + => { () } +} +#[inline] +GraphPathVariable = ; +#[inline] +GraphSubPathVariable = ; +#[inline] +GraphPatternVariable: () = { + //, + //, + => { () } +} + + #[inline] TableUnpivot: ast::AstNode = { "UNPIVOT" => { @@ -573,7 +1002,7 @@ OffsetByClause: Box = { "OFFSET" } // | 10 | = <> != | | equality operators | // | 11 | IS | | IS [NOT] NULL | // | 12 | NOT | right | logical negate | -// | 13 | AND | left | logical conjuct | +// | 13 | AND | left | logical conjunct | // | 14 | OR | left | logical disjunct | // |-------+-------------------+---------------+------------------------------------| // @@ -751,7 +1180,6 @@ ExprPrecedence09: Synth = { }, lo..hi) )) }, - , , } @@ -760,41 +1188,6 @@ LikeEscape: Box = { "ESCAPE" => Box::new(e.data) } -#[inline] -GraphMatchExpr: Synth = { - "(" "MATCH" ")" => { - Synth::empty(ast::Expr::GraphMatch( - state.node(ast::GraphMatch { - expr: Box::new(l.data), - graph_expr: Box::new(r), - }, lo..hi) - )) - } -} - -#[inline] -GraphMatchExprNoParens: ast::Expr = { - "MATCH" => - ast::Expr::GraphMatch( - state.node(ast::GraphMatch { - expr: Box::new(l.data), - graph_expr: Box::new(r), - }, lo..hi) - ) -} - -GraphMatch: ast::AstNode = { - => { - state.node(ast::GraphMatchExpr{selector, patterns: vec![patterns]}, lo..hi) - }, -} - -GraphMatchList: ast::AstNode = { - > => { - state.node(ast::GraphMatchExpr{selector, patterns: patterns}, lo..hi) - } -} - ExprPrecedence08: Synth = { "||" => Synth::empty(ast::Expr::BinOp( @@ -1000,188 +1393,6 @@ SubQueryAst: ast::AstNode = { // // // ------------------------------------------------------------------------------ // -//#[inline] -MatchPatternSelector: ast::GraphMatchSelector = { - "ANY" "SHORTEST" => ast::GraphMatchSelector::AnyShortest, - "ALL" "SHORTEST" => ast::GraphMatchSelector::AllShortest, - "ANY" => { - // TODO handle bad number parse - k.map(|n| ast::GraphMatchSelector::AnyK(n.parse().unwrap())).unwrap_or(ast::GraphMatchSelector::Any) - }, - "SHORTEST" => { - // TODO handle bad number parse - ast::GraphMatchSelector::ShortestK(k.parse().unwrap()) - }, - "SHORTEST" "GROUP" => { - // TODO handle bad number parse - ast::GraphMatchSelector::ShortestKGroup(k.parse().unwrap()) - } -} - -MatchPattern: ast::AstNode = { - => { - state.node(ast::GraphMatchPattern{ - restrictor, - quantifier: None, - prefilter: None, - variable, - parts, - }, lo..hi) - }, - "(" ")" => { - state.node(ast::GraphMatchPattern{ - quantifier, - prefilter, - ..pattern.node - }, lo..hi) - }, - "[" "]" => { - state.node(ast::GraphMatchPattern{ - quantifier, - prefilter, - ..pattern.node - }, lo..hi) - }, -} - -MatchPatternNested: ast::AstNode = { - => { - state.node(ast::GraphMatchPattern{ - restrictor, - quantifier: None, - prefilter: None, - variable, - parts, - }, lo..hi) - } -} - -//#[inline] -MatchPatternRestrictor: ast::GraphMatchRestrictor = { - "TRAIL" => ast::GraphMatchRestrictor::Trail, - "ACYCLIC" => ast::GraphMatchRestrictor::Acyclic, - "SIMPLE" => ast::GraphMatchRestrictor::Simple, -} - -//#[inline] -MatchPatternParts: Vec = { - => { - let node = ast::GraphMatchPatternPart::Node(n); - std::iter::once(node).chain(parts.into_iter().flatten()).collect() - } -} - -//#[inline] -MatchPatternPartsNested: Vec = { - , - , - => vec![ast::GraphMatchPatternPart::Edge(e)], -} - -MatchPatternPartContinue: Vec = { - => vec![ast::GraphMatchPatternPart::Edge(e),ast::GraphMatchPatternPart::Node(n)], - => vec![ast::GraphMatchPatternPart::Pattern(p),ast::GraphMatchPatternPart::Node(n)], -} - -MatchPatternPartParen: ast::AstNode = { - "(" ")" => { - state.node(ast::GraphMatchPattern { - prefilter, - quantifier, - ..pattern.node - }, lo..hi) - }, - "[" "]" => { - state.node(ast::GraphMatchPattern { - prefilter, - quantifier, - ..pattern.node - }, lo..hi) - }, -} - -//#[inline] -MatchPatternQuantifier: ast::AstNode = { - "*" => state.node(ast::GraphMatchQuantifier{ lower:0, upper:None }, lo..hi), - "+" => state.node(ast::GraphMatchQuantifier{ lower:1, upper:None }, lo..hi), - "{" "," "}" => { - // TODO error on invalid literal - state.node(ast::GraphMatchQuantifier{ lower: lower.parse().unwrap(), upper: upper.map(|n| n.parse().unwrap()) }, lo..hi) - }, -} - -MatchPatternPartNode: ast::AstNode = { - "(" ")" => { - state.node(ast::GraphMatchNode { - prefilter, - variable, - label, - }, lo..hi) - }, -} - -MatchPatternPartEdge: ast::AstNode = { - => state.node(ast::GraphMatchEdge{ quantifier, ..spec}, lo..hi), - => state.node(ast::GraphMatchEdge{ quantifier, ..spec}, lo..hi), -} - -MatchPatternPartEdgeWSpec: ast::GraphMatchEdge = { - "-" "-" ">" => ast::GraphMatchEdge{ direction: ast::GraphMatchDirection::Right, ..spec}, - "~" "~" => ast::GraphMatchEdge{ direction: ast::GraphMatchDirection::Undirected, ..spec}, - "<" "-" "-" => ast::GraphMatchEdge{ direction: ast::GraphMatchDirection::Left, ..spec}, - "~" "~" ">" => ast::GraphMatchEdge{ direction: ast::GraphMatchDirection::UndirectedOrRight, ..spec}, - "<" "~" "~" => ast::GraphMatchEdge{ direction: ast::GraphMatchDirection::LeftOrUndirected, ..spec}, - "<" "-" "-" ">" => ast::GraphMatchEdge{ direction: ast::GraphMatchDirection::LeftOrRight, ..spec}, - "-" "-" => ast::GraphMatchEdge{ direction: ast::GraphMatchDirection::LeftOrUndirectedOrRight, ..spec}, -} - -MatchPatternPartEdgeSpec: ast::GraphMatchEdge = { - "[" "]" => { - ast::GraphMatchEdge { - direction: ast::GraphMatchDirection::Undirected, - quantifier: None, - prefilter, - variable, - label, - } - } -} - -MatchPatternPartEdgeAbbr: ast::GraphMatchEdge = { - "-" ">" => ast::GraphMatchEdge{ direction: ast::GraphMatchDirection::Right, quantifier: None, prefilter: None, variable: None, label: Default::default() }, - "~" => ast::GraphMatchEdge{ direction: ast::GraphMatchDirection::Undirected, quantifier: None, prefilter: None, variable: None, label: Default::default() }, - "<" "-" => ast::GraphMatchEdge{ direction: ast::GraphMatchDirection::Left, quantifier: None, prefilter: None, variable: None, label: Default::default() }, - "~" ">" => ast::GraphMatchEdge{ direction: ast::GraphMatchDirection::UndirectedOrRight, quantifier: None, prefilter: None, variable: None, label: Default::default() }, - "<" "~" => ast::GraphMatchEdge{ direction: ast::GraphMatchDirection::LeftOrUndirected, quantifier: None, prefilter: None, variable: None, label: Default::default() }, - "<" "-" ">" => ast::GraphMatchEdge{ direction: ast::GraphMatchDirection::LeftOrRight, quantifier: None, prefilter: None, variable: None, label: Default::default() }, - "-" => ast::GraphMatchEdge{ direction: ast::GraphMatchDirection::LeftOrUndirectedOrRight, quantifier: None, prefilter: None, variable: None, label: Default::default() }, -} - -//#[inline] -MatchPatternPartName: ast::SymbolPrimitive = { - => { - ast::SymbolPrimitive { - value: name.to_owned(), - case: ast::CaseSensitivity::CaseSensitive - } - } -} - -//#[inline] // TODO conjunction/disjunction/negation -MatchPatternPartLabel: Vec = { - ":" => vec![l] -} - -//#[inline] -MatchPatternPartPrefilter: Box = { - "WHERE" -} - -//#[inline] -MatchPatternPathVariable: ast::SymbolPrimitive = { - "=" -} - // ------------------------------------------------------------------------------ // @@ -1686,11 +1897,61 @@ FnNonReservedKeyword: &'static str = { #[inline] NonReservedKeyword: &'static str = { - "ACYCLIC" => "ACYCLIC", "ANY" => "ANY", "SIMPLE" => "SIMPLE", + +} + + +//C.f. SQL '23, section 16, 5.2 +#[inline] +GraphNonReservedKeyword: &'static str = { + "ACYCLIC" => "ACYCLIC", + "BINDINGS" => "BINDINGS", + "BOUND" => "BOUND", + "DESTINATION" => "DESTINATION", + "DIFFERENT" => "DIFFERENT", + "DIRECTED" => "DIRECTED", + "EDGE" => "EDGE", + "EDGES" => "EDGES", + "ELEMENTS" => "ELEMENTS", + "LABEL" => "LABEL", + "LABELED" => "LABELED", + "NODE" => "NODE", + "PATHS" => "PATHS", + "PROPERTIES" => "PROPERTIES", + "PROPERTY" => "PROPERTY", + "PROPERTY_GRAPH_CATALOG" => "PROPERTY_GRAPH_CATALOG", + "PROPERTY_GRAPH_NAME" => "PROPERTY_GRAPH_NAME", + "PROPERTY_GRAPH_SCHEMA" => "PROPERTY_GRAPH_SCHEMA", + "RELATIONSHIP" => "RELATIONSHIP", + "RELATIONSHIPS" => "RELATIONSHIPS", "SHORTEST" => "SHORTEST", + "SINGLETONS" => "SINGLETONS", + "STEP" => "STEP", + "TABLES" => "TABLES", "TRAIL" => "TRAIL", + "VERTEX" => "VERTEX", + "WALK" => "WALK", +} + + +#[inline] +GraphEdgeSynonym = { + "EDGE", + "RELATIONSHIP", +} + +#[inline] +GraphEdgesSynonym = { + "EDGES", + "RELATIONSHIPS", +} + +#[inline] +GraphVertexSynonym = { + "NODE", + "VERTEX", } // The lexer is external; See [`lexer.rs`] for its definition. @@ -1724,6 +1985,10 @@ extern { "%" => lexer::Token::Percent, "^" => lexer::Token::Caret, "||" => lexer::Token::DblPipe, + "|" => lexer::Token::Pipe, + "&" => lexer::Token::Ampersand, + "!" => lexer::Token::Bang, + "?" => lexer::Token::QuestionMark, "=" => lexer::Token::Equal, "==" => lexer::Token::EqualEqual, @@ -1736,6 +2001,30 @@ extern { "<=" => lexer::Token::LessEqual, ">=" => lexer::Token::GreaterEqual, + // Graph + "|+|" => lexer::Token::PipePlusPipe, + "<-" => lexer::Token::LeftArrow, + "~" => lexer::Token::Tilde, + "->" => lexer::Token::RightArrow, + "<~" => lexer::Token::LeftArrowTilde, + "~>" => lexer::Token::TildeRightArrow, + "<-[" => lexer::Token::LeftArrowBracket, + "]-" => lexer::Token::RightBracketMinus, + "~[" => lexer::Token::TildeLeftBracket, + "]~" => lexer::Token::RightBracketTilde, + "-[" => lexer::Token::MinusLeftBracket, + "]->" => lexer::Token::BracketRightArrow, + "<~[" => lexer::Token::LeftArrowTildeBracket, + "]~>" => lexer::Token::BracketTildeRightArrow, + "<->" => lexer::Token::LeftMinusRight, + "<-/" => lexer::Token::LeftArrowSlash, + "/-" => lexer::Token::RightSlashMinus, + "~/" => lexer::Token::TildeLeftSlash, + "/~" => lexer::Token::RightSlashTilde, + "-/" => lexer::Token::MinusLeftSlash, + "/->" => lexer::Token::SlashRightArrow, + "<~/" => lexer::Token::LeftArrowTildeSlash, + "/~>" => lexer::Token::SlashTildeRightArrow, // Types "UnquotedIdent" => lexer::Token::UnquotedIdent(<&'input str>), @@ -1759,28 +2048,33 @@ extern { "BETWEEN" => lexer::Token::Between, "BY" => lexer::Token::By, "CASE" => lexer::Token::Case, + "COLUMNS" => lexer::Token::Columns, "CROSS" => lexer::Token::Cross, "CYCLE" => lexer::Token::Cycle, "DATE" => lexer::Token::Date, "DESC" => lexer::Token::Desc, "DISTINCT" => lexer::Token::Distinct, + "ELEMENT" => lexer::Token::Element, "EXCLUDE" => lexer::Token::Exclude, "ELSE" => lexer::Token::Else, "END" => lexer::Token::End, "ESCAPE" => lexer::Token::Escape, "EXCEPT" => lexer::Token::Except, + "EXPORT" => lexer::Token::Export, "FALSE" => lexer::Token::False, "FIRST" => lexer::Token::First, "FOR" => lexer::Token::For, "FULL" => lexer::Token::Full, "FROM" => lexer::Token::From, "GROUP" => lexer::Token::Group, + "GROUPS" => lexer::Token::Groups, "HAVING" => lexer::Token::Having, "IN" => lexer::Token::In, "INNER" => lexer::Token::Inner, "INTERSECT" => lexer::Token::Intersect, "IS" => lexer::Token::Is, "JOIN" => lexer::Token::Join, + "KEEP" => lexer::Token::Keep, "LAST" => lexer::Token::Last, "LATERAL" => lexer::Token::Lateral, "LEFT" => lexer::Token::Left, @@ -1789,19 +2083,25 @@ extern { "MATCH" => lexer::Token::Match, "MISSING" => lexer::Token::Missing, "NATURAL" => lexer::Token::Natural, + "NO" => lexer::Token::No, "NOT" => lexer::Token::Not, "NULL" => lexer::Token::Null, "NULLS" => lexer::Token::Nulls, "OFFSET" => lexer::Token::Offset, "ON" => lexer::Token::On, + "ONE" => lexer::Token::One, "OR" => lexer::Token::Or, "ORDER" => lexer::Token::Order, "OUTER" => lexer::Token::Outer, + "PATH" => lexer::Token::Path, "PARTIAL" => lexer::Token::Partial, "PIVOT" => lexer::Token::Pivot, + "PER" => lexer::Token::Per, "PRESERVE" => lexer::Token::Preserve, - "RIGHT" => lexer::Token::Right, "RECURSIVE" => lexer::Token::Recursive, + "REPEATABLE" => lexer::Token::Repeatable, + "RIGHT" => lexer::Token::Right, + "ROW" => lexer::Token::Row, "SELECT" => lexer::Token::Select, "SEARCH" => lexer::Token::Search, "TABLE" => lexer::Token::Table, @@ -1822,5 +2122,47 @@ extern { "WITH" => lexer::Token::With, "WITHOUT" => lexer::Token::Without, "ZONE" => lexer::Token::Zone, + + // Graph Keywords; reserved + "ALL_DIFFERENT" => lexer::Token::AllDifferent, + "BINDING_COUNT" => lexer::Token::BindingCount, + "ELEMENT_ID" => lexer::Token::ElementId, + "ELEMENT_NUMBER" => lexer::Token::ElementNumber, + "GRAPH" => lexer::Token::Graph, + "GRAPH_TABLE" => lexer::Token::GraphTable, + "MATCHNUM" => lexer::Token::MatchNum, + "PATH_LENGTH" => lexer::Token::PathLength, + "PATH_NAME" => lexer::Token::PathName, + "PROPERTY_EXISTS" => lexer::Token::PropertyExists, + "SAME" => lexer::Token::Same, + + // Graph Keywords; non-reserved + "ACYCLIC" => lexer::Token::Acyclic, + "BINDINGS" => lexer::Token::Bindings, + "BOUND" => lexer::Token::Bound, + "DESTINATION" => lexer::Token::Destination, + "DIFFERENT" => lexer::Token::Different, + "DIRECTED" => lexer::Token::Directed, + "EDGE" => lexer::Token::Edge, + "EDGES" => lexer::Token::Edges, + "ELEMENTS" => lexer::Token::Elements, + "LABEL" => lexer::Token::Label, + "LABELED" => lexer::Token::Labeled, + "NODE" => lexer::Token::Node, + "PATHS" => lexer::Token::Paths, + "PROPERTIES" => lexer::Token::Properties, + "PROPERTY" => lexer::Token::Property, + "PROPERTY_GRAPH_CATALOG" => lexer::Token::PropertyGraphCatalog, + "PROPERTY_GRAPH_NAME" => lexer::Token::PropertyGraphName, + "PROPERTY_GRAPH_SCHEMA" => lexer::Token::PropertyGraphSchema, + "RELATIONSHIP" => lexer::Token::Relationship, + "RELATIONSHIPS" => lexer::Token::Relationships, + "SHORTEST" => lexer::Token::Shortest, + "SINGLETONS" => lexer::Token::Singletons, + "STEP" => lexer::Token::Step, + "TABLES" => lexer::Token::Tables, + "TRAIL" => lexer::Token::Trail, + "VERTEX" => lexer::Token::Vertex, + "WALK" => lexer::Token::Walk, } } From 282a6c6770794e7aa8f198f25b67564387ddc9cd Mon Sep 17 00:00:00 2001 From: Josh Pschorr Date: Tue, 1 Apr 2025 14:02:22 -0700 Subject: [PATCH 2/9] GRAPH_TABLE AST --- partiql-ast/src/ast.rs | 298 ++++++++--- partiql-logical-planner/src/graph.rs | 12 +- partiql-logical/src/lib.rs | 2 +- partiql-parser/src/parse/mod.rs | 136 ++--- partiql-parser/src/parse/partiql.lalrpop | 647 +++++++++++++---------- 5 files changed, 692 insertions(+), 403 deletions(-) diff --git a/partiql-ast/src/ast.rs b/partiql-ast/src/ast.rs index 4539d812..9792069c 100644 --- a/partiql-ast/src/ast.rs +++ b/partiql-ast/src/ast.rs @@ -10,11 +10,11 @@ use rust_decimal::Decimal as RustDecimal; -use std::fmt; -use std::num::NonZeroU32; - #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +use std::fmt; +use std::num::NonZeroU32; +use std::ops::Deref; use partiql_ast_macros::Visit; use partiql_common::node::NodeId; @@ -27,6 +27,14 @@ pub struct AstNode { pub node: T, } +impl Deref for AstNode { + type Target = T; + + fn deref(&self) -> &T { + &self.node + } +} + #[derive(Visit, Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Item { @@ -835,12 +843,102 @@ pub enum JoinSpec { Natural, } +// TODO #[derive(Visit, Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct GraphTable { + pub graph_match: AstNode, +} + /// ` MATCH ` #[derive(Visit, Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct GraphMatch { pub expr: Box, - pub graph_expr: Box>, + // TODO remove + #[visit(skip)] + pub pattern: AstNode, + // TODO remove + #[visit(skip)] + pub shape: Option, +} + +// TODO #[derive(Visit, Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct GraphPattern { + // TODO #[visit(skip)] + pub mode: Option, + pub patterns: Vec>, + // TODO #[visit(skip)] + pub keep: Option, + pub where_clause: Option>, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum GraphMatchMode { + DifferentEdges, + RepeatableElements, +} + +/// A graph match clause as defined in GPML +/// See https://arxiv.org/abs/2112.06217 +#[derive(Visit, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct GraphMatchExpr { + #[visit(skip)] // TODO + pub patterns: Vec>, +} + +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum GraphTableShape { + Rows(AstNode), + Columns(AstNode), + Export(AstNode), +} + +#[derive(Clone, Default, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum GraphTableRows { + #[default] + OneRowPerMatch, + OneRowPerVertex { + v: SymbolPrimitive, + in_paths: Option>, + }, + OneRowPerStep { + v1: SymbolPrimitive, + e: SymbolPrimitive, + v2: SymbolPrimitive, + in_paths: Option>, + }, +} + +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct GraphTableColumns { + pub columns: Vec, +} + +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum GraphTableColumnDef { + Expr(Box, Option), + AllProperties(SymbolPrimitive), +} + +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum GraphTableExport { + AllSingletons { + except: Option>, + }, + Singletons { + exports: Vec, + }, + NoSingletons, } /// The direction of an edge @@ -853,9 +951,6 @@ pub struct GraphMatch { /// | Undirected or right | ~[ spec ]~> | ~> | /// | Left or right | <−[ spec ]−> | <−> | /// | Left, undirected or right | −[ spec ]− | − | -/// -/// Fig. 5. Table of edge patterns: -/// https://arxiv.org/abs/2112.06217 #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum GraphMatchDirection { @@ -868,20 +963,6 @@ pub enum GraphMatchDirection { LeftOrUndirectedOrRight, } -/// A part of a graph pattern -#[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum GraphMatchPatternPart { - /// A single node in a graph pattern. - Node(AstNode), - - /// A single edge in a graph pattern. - Edge(AstNode), - - /// A sub-pattern. - Pattern(AstNode), -} - /// A quantifier for graph edges or patterns. (e.g., the `{2,5}` in `MATCH (x)->{2,5}(y)`) #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -890,18 +971,18 @@ pub struct GraphMatchQuantifier { pub upper: Option, } -/// A path restrictor +/// A path mode /// | Keyword | Description /// |----------------+-------------- +/// | WALK | /// | TRAIL | No repeated edges. /// | ACYCLIC | No repeated nodes. /// | SIMPLE | No repeated nodes, except that the first and last nodes may be the same. -/// -/// Fig. 7. Table of restrictors: -/// https://arxiv.org/abs/2112.06217 + #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum GraphMatchRestrictor { +pub enum GraphPathMode { + Walk, Trail, Acyclic, Simple, @@ -911,14 +992,14 @@ pub enum GraphMatchRestrictor { #[derive(Visit, Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct GraphMatchNode { - /// an optional node pre-filter, e.g.: `WHERE c.name='Alarm'` in `MATCH (c WHERE c.name='Alarm')` - pub prefilter: Option>, /// the optional element variable of the node match, e.g.: `x` in `MATCH (x)` #[visit(skip)] pub variable: Option, /// the optional label(s) to match for the node, e.g.: `Entity` in `MATCH (x:Entity)` #[visit(skip)] - pub label: Option>, + pub label: Option>, + /// an optional node where clause, e.g.: `WHERE c.name='Alarm'` in `MATCH (c WHERE c.name='Alarm')` + pub where_clause: Option>, } /// A single edge in a graph pattern. @@ -931,67 +1012,150 @@ pub struct GraphMatchEdge { /// an optional quantifier for the edge match #[visit(skip)] pub quantifier: Option>, - /// an optional edge pre-filter, e.g.: `WHERE t.capacity>100` in `MATCH −[t:hasSupply WHERE t.capacity>100]−>` - pub prefilter: Option>, /// the optional element variable of the edge match, e.g.: `t` in `MATCH −[t]−>` #[visit(skip)] pub variable: Option, /// the optional label(s) to match for the edge. e.g.: `Target` in `MATCH −[t:Target]−>` #[visit(skip)] - pub label: Option>, + pub label: Option>, + /// an optional edge where clause, e.g.: `WHERE t.capacity>100` in `MATCH −[t:hasSupply WHERE t.capacity>100]−>` + pub where_clause: Option>, } -/// A single graph match pattern. -#[derive(Visit, Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct GraphMatchPattern { - /// an optional restrictor for the entire pattern match - #[visit(skip)] - pub restrictor: Option, - /// an optional quantifier for the entire pattern match - #[visit(skip)] - pub quantifier: Option>, - /// an optional pattern pre-filter, e.g.: `WHERE a.name=b.name` in `MATCH [(a)->(b) WHERE a.name=b.name]` - pub prefilter: Option>, +pub enum GraphMatchLabel { + Name(SymbolPrimitive), + Wildcard, + Negated(Box>), + Conjunction(Vec>), + Disjunction(Vec>), +} + +// TODO #[derive(Visit, Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct GraphPathPattern { /// the optional element variable of the pattern, e.g.: `p` in `MATCH p = (a) −[t]−> (b)` - #[visit(skip)] + // TODO #[visit(skip)] pub variable: Option, + // TODO #[visit(skip)] + pub prefix: Option, /// the ordered pattern parts - #[visit(skip)] - pub parts: Vec, + // TODO #[visit(skip)] + pub path: AstNode, +} + +// TODO #[derive(Visit, Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct GraphPathSubPattern { + /// the optional element variable of the pattern, e.g.: `p` in `MATCH p = (a) −[t]−> (b)` + // TODO #[visit(skip)] + pub variable: Option, + // TODO #[visit(skip)] + pub mode: Option, + /// the ordered pattern parts + // TODO #[visit(skip)] + pub path: AstNode, + /// an optional pattern where e.g.: `WHERE a.name=b.name` in `MATCH [(a)->(b) WHERE a.name=b.name]` + pub where_clause: Option>, +} + +// TODO #[derive(Visit, Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum GraphMatchPathPattern { + Path(Vec>), + Union(Vec>), + Multiset(Vec>), + + Questioned(Box>), + Quantified( + Box>, + AstNode, + ), + + Sub(Box>), + + /// A single node in a graph pattern. + Node(AstNode), + + /// A single edge in a graph pattern. + Edge(AstNode), + + Simplified(AstNode), +} + +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct GraphMatchElement { + pub variable: Option, + pub label: Option>, + pub where_clause: Option>, +} + +// TODO #[derive(Visit, Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct GraphMatchSimplified { + pub dir: GraphMatchDirection, + pub pattern: AstNode, +} + +// TODO #[derive(Visit, Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum GraphMatchSimplifiedPattern { + Union(Vec>), + Multiset(Vec>), + + Path(Vec>), + + Conjunction(Vec>), + + Questioned(Box>), + Quantified( + Box>, + AstNode, + ), + + /// Direction override + Direction( + GraphMatchDirection, + Box>, + ), + + Negated(Box>), + Label(SymbolPrimitive), +} + +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum GraphPathPrefix { + Mode(GraphPathMode), + Search(GraphPathSearchPrefix, Option), } -/// A path selector /// | Keyword /// |------------------ -/// | ANY SHORTEST -/// | ALL SHORTEST -/// | ANY +/// | ALL +/// | Any /// | ANY k +/// | ALL SHORTEST +/// | ANY SHORTEST /// | SHORTEST k /// | SHORTEST k GROUP -/// -/// Fig. 8. Table of restrictors: -/// https://arxiv.org/abs/2112.06217 #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum GraphMatchSelector { - AnyShortest, - AllShortest, +pub enum GraphPathSearchPrefix { + All, Any, AnyK(NonZeroU32), + AllShortest, + AnyShortest, ShortestK(NonZeroU32), - ShortestKGroup(NonZeroU32), -} - -/// A graph match clause as defined in GPML -/// See https://arxiv.org/abs/2112.06217 -#[derive(Visit, Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct GraphMatchExpr { - #[visit(skip)] - pub selector: Option, - pub patterns: Vec>, + ShortestKGroup(Option), } /// GROUP BY <`grouping_strategy`> <`group_key`>[, <`group_key`>]... \[AS \] diff --git a/partiql-logical-planner/src/graph.rs b/partiql-logical-planner/src/graph.rs index d620bffb..d23c090d 100644 --- a/partiql-logical-planner/src/graph.rs +++ b/partiql-logical-planner/src/graph.rs @@ -1,6 +1,6 @@ use num::Integer; use partiql_ast::ast; -use partiql_ast::ast::{GraphMatchDirection, GraphMatchPatternPart}; +use partiql_ast::ast::{GraphMatchDirection, GraphPathPatternPart}; use partiql_logical::graph::bind_name::FreshBinder; use partiql_logical::graph::{ BindSpec, DirectionFilter, EdgeFilter, EdgeMatch, LabelFilter, NodeFilter, NodeMatch, @@ -186,7 +186,7 @@ impl GraphToLogical { fn plan_graph_pattern( &self, - pattern: &ast::GraphMatchPattern, + pattern: &ast::GraphPathPattern, ) -> Result, String> { if pattern.restrictor.is_some() { not_yet_implemented_result!("MATCH pattern restrictors are not yet supported."); @@ -212,12 +212,12 @@ impl GraphToLogical { fn plan_graph_pattern_part( &self, - part: &ast::GraphMatchPatternPart, + part: &ast::GraphPathPatternPart, ) -> Result, String> { match part { - GraphMatchPatternPart::Node(n) => self.plan_graph_pattern_part_node(&n.node), - GraphMatchPatternPart::Edge(e) => self.plan_graph_pattern_part_edge(&e.node), - GraphMatchPatternPart::Pattern(pattern) => self.plan_graph_pattern(&pattern.node), + GraphPathPatternPart::Node(n) => self.plan_graph_pattern_part_node(&n.node), + GraphPathPatternPart::Edge(e) => self.plan_graph_pattern_part_edge(&e.node), + GraphPathPatternPart::Pattern(pattern) => self.plan_graph_pattern(&pattern.node), } } diff --git a/partiql-logical/src/lib.rs b/partiql-logical/src/lib.rs index a24c66c4..84972f24 100644 --- a/partiql-logical/src/lib.rs +++ b/partiql-logical/src/lib.rs @@ -612,7 +612,7 @@ pub struct GraphMatchExpr { } #[derive(Debug, Clone, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct GraphMatchPattern {} +pub struct GraphPathPattern {} /// Represents a sub-query expression, e.g. `SELECT v.a*2 AS u FROM t AS v` in /// `SELECT t.a, s FROM data AS t, (SELECT v.a*2 AS u FROM t AS v) AS s` diff --git a/partiql-parser/src/parse/mod.rs b/partiql-parser/src/parse/mod.rs index 48341141..9ca5aa53 100644 --- a/partiql-parser/src/parse/mod.rs +++ b/partiql-parser/src/parse/mod.rs @@ -757,134 +757,152 @@ mod tests { #[test] fn no_labels() { - parse!(r#"SELECT 1 FROM my_graph MATCH ()"#); - parse!(r#"SELECT 1 FROM (my_graph MATCH ())"#); + parse!(r#"SELECT 1 FROM GRAPH_TABLE (my_graph MATCH ())"#); - parse!(r#"SELECT 1 FROM my_graph MATCH () WHERE contains_value('1')"#); - parse!(r#"SELECT 1 FROM (my_graph MATCH ()) WHERE contains_value('1')"#); + parse!(r#"SELECT 1 FROM GRAPH_TABLE (my_graph MATCH ()) WHERE contains_value('1')"#); - parse!(r#"SELECT x.info AS info FROM my_graph MATCH (x) WHERE x.name LIKE 'foo'"#); - parse!(r#"SELECT x.info AS info FROM (my_graph MATCH (x)) WHERE x.name LIKE 'foo'"#); - // TODO fails due to edge first - // parse!(r#"SELECT 1 FROM (g MATCH -[]->) "#); + parse!( + r#"SELECT x.info AS info FROM GRAPH_TABLE (my_graph MATCH (x)) WHERE x.name LIKE 'foo'"# + ); + parse!(r#"SELECT 1 FROM GRAPH_TABLE (g MATCH -[]->) "#); } #[test] fn lone_match_expr() { parse!(r#"(MyGraph MATCH (x))"#); parse!(r#"(MyGraph MATCH (x), (y) )"#); - // TODO fails due to edge first - //parse!(r#"(MyGraph MATCH (x), -[u]-> )"#); + parse!(r#"(MyGraph MATCH (x), -[u]-> )"#); } #[test] fn labelled_nodes() { - parse!(r#"SELECT x AS target FROM my_graph MATCH (x:Label) WHERE x.has_data = true"#); + parse!(r#"SELECT x AS target FROM (my_graph MATCH (x:Label) WHERE x.has_data = true)"#); } #[test] fn edges() { - parse!(r#"SELECT a,b FROM g MATCH (a:A) -[e:E]-> (b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A) -> (b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A) ~[e:E]~ (b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A) ~ (b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A) <-[e:E]- (b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A) <- (b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A) ~[e:E]~> (b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A) ~> (b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A) <~[e:E]~ (b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A) <~ (b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A) <-[e:E]-> (b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A) <-> (b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A) -[e:E]- (b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A) - (b:B)"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) -[e:E]-> (b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) -> (b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) ~[e:E]~ (b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) ~ (b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) <-[e:E]- (b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) <- (b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) ~[e:E]~> (b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) ~> (b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) <~[e:E]~ (b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) <~ (b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) <-[e:E]-> (b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) <-> (b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) -[e:E]- (b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) - (b:B))"#); } #[test] fn quantifiers() { - parse!(r#"SELECT a,b FROM g MATCH (a:A)-[:edge]->*(b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A)<-[:edge]-+(b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A)~[:edge]~{5,}(b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A)-[e:edge]-{2,6}(b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A)->*(b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A)<-+(b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A)~{5,}(b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A)-{2,6}(b:B)"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A)-[:edge]->*(b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A)<-[:edge]-+(b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A)~[:edge]~{5,}(b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A)-[e:edge]-{2,6}(b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A)->*(b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A)<-+(b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A)~{5,}(b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A)-{2,6}(b:B))"#); } #[test] fn patterns() { parse!( - r#"SELECT the_a.name AS src, the_b.name AS dest FROM my_graph MATCH (the_a:a) -[the_y:y]-> (the_b:b) WHERE the_y.score > 10"# + r#"SELECT the_a.name AS src, the_b.name AS dest FROM (my_graph MATCH (the_a:a) -[the_y:y]-> (the_b:b) WHERE the_y.score > 10)"# + ); + parse!(r#""SELECT a,b FROM (g MATCH (a)-[:has]->()-[:contains]->(b))""#); + parse!( + r#"SELECT a,b FROM GRAPH_TABLE (g MATCH (a) -[:has]-> (x), (x)-[:contains]->(b))"# ); - parse!(r#""SELECT a,b FROM g MATCH (a)-[:has]->()-[:contains]->(b)""#); - parse!(r#"SELECT a,b FROM (g MATCH (a) -[:has]-> (x), (x)-[:contains]->(b))"#); } #[test] fn path_var() { - parse!(r#"SELECT a,b FROM (g MATCH p = (a:A) -[e:E]-> (b:B))"#); + parse!(r#"SELECT a,b FROM GRAPH_TABLE (g MATCH p = (a:A) -[e:E]-> (b:B))"#); } #[test] fn paranthesized() { parse!( - r#"SELECT a,b FROM (g MATCH [(a:A)-[e:Edge]->(b:A) WHERE a.owner=b.owner]{2,5})"# + r#"SELECT a,b FROM GRAPH_TABLE (g MATCH ((a:A)-[e:Edge]->(b:A) WHERE a.owner=b.owner){2,5})"# ); - parse!(r#"SELECT a,b FROM (g MATCH pathVar = (a:A)[()-[e:Edge]->()]{1,3}(b:B))"#); - - // brackets - parse!(r#"SELECT a,b FROM (g MATCH pathVar = (a:A)[-[e:Edge]->]*(b:B))"#); - // parens - parse!(r#"SELECT a,b FROM (g MATCH pathVar = (a:A)(-[e:Edge]->)*(b:B))"#); + parse!( + r#"SELECT a,b FROM GRAPH_TABLE (g MATCH pathVar = (a:A)(()-[e:Edge]->()){1,3}(b:B))"# + ); + parse!(r#"SELECT a,b FROM GRAPH_TABLE (g MATCH pathVar = (a:A)(-[e:Edge]->)*(b:B))"#); } #[test] fn filters() { parse!( - r#"SELECT u as banCandidate FROM g MATCH (p:Post Where p.isFlagged = true) <-[:createdPost]- (u:User WHERE u.isBanned = false AND u.karma < 20) -[:createdComment]->(c:Comment WHERE c.isFlagged = true) WHERE p.title LIKE '%considered harmful%'"# + r#"SELECT u as banCandidate + FROM (g MATCH + (p:Post Where p.isFlagged = true) + <-[:createdPost]- + (u:User WHERE u.isBanned = false AND u.karma < 20) + -[:createdComment]-> + (c:Comment WHERE c.isFlagged = true) + WHERE p.title LIKE '%considered harmful%')"# + ); + parse!( + r#"SELECT u as banCandidate + FROM (g MATCH + (p:Post Where p.isFlagged = true) + <-[:createdPost]- + (u:User WHERE u.isBanned = false AND u.karma < 20) + -[:createdComment]-> + (c:Comment WHERE c.isFlagged = true) + ) + WHERE p.title LIKE '%considered harmful%'"# ); } #[test] - fn restrictors() { + fn path_mode() { parse!( - r#"SELECT p FROM g MATCH TRAIL p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"# + r#"SELECT p FROM (g MATCH p = TRAIL (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"# ); parse!( - r#"SELECT p FROM g MATCH SIMPLE p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"# + r#"SELECT p FROM (g MATCH p = SIMPLE (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"# ); parse!( - r#"SELECT p FROM g MATCH ACYCLIC p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"# + r#"SELECT p FROM (g MATCH p = ACYCLIC (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"# ); } #[test] - fn selectors() { + fn search_prefix() { parse!( - r#"SELECT p FROM g MATCH ANY SHORTEST p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"# + r#"SELECT p FROM (g MATCH p = ANY SHORTEST (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"# ); parse!( - r#"SELECT p FROM g MATCH ALL SHORTEST p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"# + r#"SELECT p FROM (g MATCH p = ALL SHORTEST (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"# ); parse!( - r#"SELECT p FROM g MATCH ANY p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"# + r#"SELECT p FROM (g MATCH p = ANY (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"# ); parse!( - r#"SELECT p FROM g MATCH ANY 5 p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"# + r#"SELECT p FROM (g MATCH p = ANY 5 (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"# ); parse!( - r#"SELECT p FROM g MATCH SHORTEST 5 p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"# + r#"SELECT p FROM (g MATCH p = SHORTEST 5 (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"# ); parse!( - r#"SELECT p FROM g MATCH SHORTEST 5 GROUP p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"# + r#"SELECT p FROM (g MATCH p = SHORTEST 5 GROUP (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"# ); } #[test] fn match_and_join() { parse!( - r#"SELECT a,b,c, t1.x as x, t2.y as y FROM (graph MATCH (a) -> (b), (a) -> (c)), table1 as t1, table2 as t2"# + r#"SELECT a,b,c, t1.x as x, t2.y as y FROM GRAPH_TABLE (g MATCH (a) -> (b), (a) -> (c)), table1 as t1, table2 as t2"# + ); + parse!( + r#"SELECT a,b,c, t1.x as x, t2.y as y FROM table1 as t1, table2 as t2, GRAPH_TABLE (g MATCH (a) -> (b), (a) -> (c))"# ); } @@ -896,8 +914,8 @@ mod tests { #[test] fn etc() { - parse!("SELECT * FROM g MATCH ALL SHORTEST [ (x)-[e]->*(y) ]"); - parse!("SELECT * FROM g MATCH ALL SHORTEST [ TRAIL (x)-[e]->*(y) ]"); + parse!("SELECT * FROM (g MATCH ALL SHORTEST ( (x)-[e]->*(y) ))"); + parse!("SELECT * FROM (g MATCH ALL SHORTEST ( TRAIL (x)-[e]->*(y) ))"); } } diff --git a/partiql-parser/src/parse/partiql.lalrpop b/partiql-parser/src/parse/partiql.lalrpop index dac119f2..4a0cca9b 100644 --- a/partiql-parser/src/parse/partiql.lalrpop +++ b/partiql-parser/src/parse/partiql.lalrpop @@ -341,10 +341,9 @@ TableBaseReference: ast::AstNode = { // 7.1 GraphTable: ast::AstNode = { - "GRAPH_TABLE" "(" ")" => { - // TODO: Dummy node for parser testing + "GRAPH_TABLE" => { state.node(ast::FromLet { - expr: Box::new(ast::Expr::Error), // TODO + expr: Box::new(e), kind: ast::FromLetKind::Scan, as_alias: None, at_alias: None, @@ -353,69 +352,63 @@ GraphTable: ast::AstNode = { }, } -GraphTableShape: () = { - , - , - , +ParenGraphMatch: ast::Expr = { + "(" ")", } -GraphTableRowsClause: () = { +GraphMatch: ast::Expr = { + "MATCH" + => ast::Expr::GraphMatch(state.node(ast::GraphMatch{expr, pattern, shape}, lo..hi)), +} + +GraphTableShape: ast::GraphTableShape = { + => ast::GraphTableShape::Rows(rows), + => ast::GraphTableShape::Columns(cols), + => ast::GraphTableShape::Export(export), +} + +GraphTableRowsClause: ast::AstNode = { "ONE" "ROW" "PER" "MATCH" => { - todo!("ONE ROW PER MATCH") + state.node(ast::GraphTableRows::OneRowPerMatch, lo..hi) }, "ONE" "ROW" "PER" - "(" ")" - => { - todo!("ONE ROW PER VERTEX") + "(" ")" + => { + state.node(ast::GraphTableRows::OneRowPerVertex{v, in_paths}, lo..hi) }, "ONE" "ROW" "PER" "STEP" "(" "," "," ")" - => { - todo!("ONE ROW PER STEP") + => { + state.node(ast::GraphTableRows::OneRowPerStep{v1, e, v2, in_paths}, lo..hi) }, } -GraphInPathsClause: () = { - "IN" "(" > ")" => { - todo!("GRAPH IN PATHS CLAUSE") - } +GraphInPathsClause: Vec = { + "IN" "(" > ")" } -GraphTableColumnsClause: () = { - "COLUMNS" "(" > ")" => { - todo!("GRAPH COLUMNS") +GraphTableColumnsClause: ast::AstNode = { + "COLUMNS" "(" > ")" => { + state.node(ast::GraphTableColumns{columns}, lo..hi) } } -GraphTableColumnDef: () = { - "." "*" => { - todo!("GRAPH COLUMN DEFINITION") - } +GraphTableColumnDef: ast::GraphTableColumnDef = { + )?> => ast::GraphTableColumnDef::Expr(v, as_ident), + // "." "*" => ast::GraphTableColumnDef::AllProperties(var), } -GraphTableExportClause: () = { - "EXPORT" "ALL" "SINGLETONS" => { - todo!("EXPORT ALL SINGLETONS") - }, - "EXPORT" "SINGLETONS" "(" > ")" => { - todo!("EXPORT SINGLETONS") - }, - "EXPORT" "NO" "SINGLETONS" => { - todo!("EXPORT NO SINGLETONS") - } +GraphTableExportClause: ast::AstNode = { + "EXPORT" "ALL" "SINGLETONS" + => state.node(ast::GraphTableExport::AllSingletons{except}, lo..hi), + "EXPORT" "SINGLETONS" "(" > ")" + => state.node(ast::GraphTableExport::Singletons{exports}, lo..hi), + "EXPORT" "NO" "SINGLETONS" => state.node(ast::GraphTableExport::NoSingletons, lo..hi), } #[inline] -GraphTableExportExcept: () = { - "EXCEPT" "(" > ")" => { - todo!("GRAPH EXPORT EXCEPT") - }, -} - -GraphRefMatch: () = { - "MATCH" => { - todo!("GRAPH MATCH") - }, +GraphTableExportExcept: Vec = { + "EXCEPT" "(" > ")", } #[inline] @@ -423,355 +416,407 @@ GraphReference = ; // 10.4 -GraphPattern: () = { - => { - () +GraphPattern: ast::AstNode = { + > => { + state.node(ast::GraphPattern{mode, patterns, keep, where_clause}, lo..hi) }, } -GraphMatchMode: () = { - "REPEATABLE" "ELEMENT" "BINDINGS"? => todo!("GRAPH MATCH MODE"), - "REPEATABLE" "ELEMENTS" => todo!("GRAPH MATCH MODE"), - "DIFFERENT" "BINDINGS"? => todo!("GRAPH MATCH MODE"), - "DIFFERENT" => todo!("GRAPH MATCH MODE"), -} - -GraphPathPatternList: () = { - => { - todo!("Path Pattern") - } +GraphMatchMode: ast::GraphMatchMode = { + "REPEATABLE" "ELEMENT" "BINDINGS"? => ast::GraphMatchMode::RepeatableElements, + "REPEATABLE" "ELEMENTS" => ast::GraphMatchMode::RepeatableElements, + "DIFFERENT" "BINDINGS"? => ast::GraphMatchMode::DifferentEdges, + "DIFFERENT" => ast::GraphMatchMode::DifferentEdges, } -GraphPathVariableDecl: () = { - "=" => { - todo!("Path Variable Decl") - } +GraphPathPattern: ast::AstNode = { + + => state.node(ast::GraphPathPattern{variable, prefix, path}, lo..hi), } -GraphPathPattern: () = { - > => { - todo!("Path Pattern List") - } +GraphPathVariableDecl: ast::SymbolPrimitive = { + "=" } -GraphKeepClause: () = { - "KEEP" => todo!("GRAPH KEEP CLAUSE"), +GraphKeepClause: ast::GraphPathPrefix = { + "KEEP" , } -GraphPatternWhereClause: () = { - "WHERE" => todo!("GRAPH WHERE CLAUSE"), +GraphPatternWhereClause: Box = { + "WHERE" , } // 10.5 -GraphPathPatternPrefix: () = { - , - , -} - -GraphPathModePrefix: () = { - => todo!("GRAPH PATH MODE PREFIX"), +GraphPathPatternPrefix: ast::GraphPathPrefix = { + => ast::GraphPathPrefix::Mode(mode), + => ast::GraphPathPrefix::Search(search.0, search.1), } -#[inline] -GraphPathOrPaths= { - "PATH" , "PATHS" +GraphPathModePrefix: ast::GraphPathMode = { + , + "PATH", + "PATHS", } #[inline] -GraphPathMode: () = { - "WALK" => todo!("GraphPathMode"), - "TRAIL" => todo!("GraphPathMode"), - "SIMPLE" => todo!("GraphPathMode"), - "ACYCLIC" => todo!("GraphPathMode"), +GraphPathMode: ast::GraphPathMode = { + "WALK" => ast::GraphPathMode::Walk, + "TRAIL" => ast::GraphPathMode::Trail, + "SIMPLE" => ast::GraphPathMode::Simple, + "ACYCLIC" => ast::GraphPathMode::Acyclic, } #[inline] -GraphPathSearchPrefix: () = { - "ALL" => todo!("GraphPathSearchPrefix"), - "ANY" => todo!("GraphPathSearchPrefix"), - "ALL" "SHORTEST" => todo!("GraphPathSearchPrefix"), - "ANY" "SHORTEST" => todo!("GraphPathSearchPrefix"), - "SHORTEST" => todo!("GraphPathSearchPrefix"), - "SHORTEST" "GROUP" => todo!("GraphPathSearchPrefix"), - "SHORTEST" "GROUPS" => todo!("GraphPathSearchPrefix"), +GraphPathSearchPrefix: (ast::GraphPathSearchPrefix, Option) = { + // TODO handle invalid number parse + "ALL" + => (ast::GraphPathSearchPrefix::All, mode), + "ANY" + => (ast::GraphPathSearchPrefix::Any, mode), + "ANY" + => (ast::GraphPathSearchPrefix::AnyK(std::num::NonZeroU32::new(k.parse().unwrap()).unwrap()), mode), + "ALL" "SHORTEST" + => (ast::GraphPathSearchPrefix::AllShortest, mode), + "ANY" "SHORTEST" + => (ast::GraphPathSearchPrefix::AnyShortest, mode), + "SHORTEST" + => (ast::GraphPathSearchPrefix::ShortestK(std::num::NonZeroU32::new(k.parse().unwrap()).unwrap()), mode), + "SHORTEST" "GROUP" + => (ast::GraphPathSearchPrefix::ShortestKGroup(k.and_then(|n| std::num::NonZeroU32::new(n.parse().unwrap()))), mode), + "SHORTEST" "GROUPS" + => (ast::GraphPathSearchPrefix::ShortestKGroup(k.and_then(|n| std::num::NonZeroU32::new(n.parse().unwrap()))), mode), } // 10.6 -GraphPathPatternExpr: () = { - => todo!("GRAPH Path Pattern expression"), - => todo!("GRAPH Path Pattern expression"), - => todo!("GRAPH Path Pattern expression"), -} - -GraphPathMultisetAlternation: () = { - > => todo!("GRAPH Path Pattern multiset"), -} - -GraphPathMultisetAlternationContinue: () = { - "|+|" => todo!("GRAPH Path Pattern multiset"), -} - -GraphPathUnion: () = { - > => todo!("GRAPH Path Pattern unio"), -} - -GraphPathUnionContinue: () = { - "|" => todo!("GRAPH Path Pattern union"), -} - -GraphPathTerm: () = { - => todo!("GRAPH Path Pattern term"), - => todo!("GRAPH Path Pattern term"), +GraphPathPatternExpr: ast::AstNode = { + , + > + => state.node(ast::GraphMatchPathPattern::Union(terms), lo..hi), + > + => state.node(ast::GraphMatchPathPattern::Multiset(terms), lo..hi), } -GraphPathFactor: () = { - => todo!("GRAPH Path Pattern factor"), - => todo!("GRAPH Path Pattern factor"), - "?" => todo!("GRAPH Path Pattern factor"), +GraphPathTerm: ast::AstNode = { + , + => { + let path = if let ast::GraphMatchPathPattern::Path(mut path) = term.node { + path.push(factor); + path + } else { + vec![term, factor] + }; + state.node(ast::GraphMatchPathPattern::Path(path), lo..hi) + } } -GraphPathPrimary: () = { - => todo!("GRAPH Path Pattern primary"), - => todo!("GRAPH Path Pattern primary"), - => todo!("GRAPH Path Pattern primary"), +GraphPathFactor: ast::AstNode = { + , + + => state.node(ast::GraphMatchPathPattern::Quantified(Box::new(p), q), lo..hi), + "?" + => state.node(ast::GraphMatchPathPattern::Questioned(Box::new(p)), lo..hi), } -GraphElementPattern: () = { - => todo!("GRAPH Element Pattern"), - => todo!("GRAPH Element Pattern"), +GraphPathPrimary: ast::AstNode = { + => state.node(ast::GraphMatchPathPattern::Node(pat), lo..hi), + => state.node(ast::GraphMatchPathPattern::Edge(pat), lo..hi), + => state.node(ast::GraphMatchPathPattern::Sub(Box::new(pat)), lo..hi), + => state.node(ast::GraphMatchPathPattern::Simplified(pat), lo..hi), } -GraphVertexPattern: () = { - "(" ")" => todo!("GRAPH Vertex Pattern"), +GraphVertexPattern: ast::AstNode = { + "(" ")" => { + let ast::GraphMatchElement{variable, label, where_clause} = pat; + state.node(ast::GraphMatchNode {variable, label, where_clause}, lo..hi) + } } -GraphElementPatternFiller: () = { - - => todo!("GRAPH Element Pattern Filler"), +GraphElementPatternFiller: ast::GraphMatchElement = { + + + => { + ast::GraphMatchElement {variable, label, where_clause} + } } #[inline] GraphElementVariableDecl = ; -GraphIsLabelExpression: () = { - ":" => todo!("GRAPH is label expr"), - "IS" => todo!("GRAPH is label expr"), +GraphIsLabelExpression: ast::AstNode = { + ":" => state.node(e.node, lo..hi), + "IS" => state.node(e.node, lo..hi), } -GraphElementPatternWhereClause: () = { - "WHERE" => todo!("GRAPH element WHERE CLAUSE"), +GraphElementPatternWhereClause: Box = { + "WHERE" } -GraphEdgePattern: () = { +GraphEdgePattern: ast::AstNode = { , , } -GraphFullEdgePattern: () = { - "<-[" "]-" => todo!("GRAPH Edge Pattern"), - "~[" "]~" => todo!("GRAPH Edge Pattern"), - "-[" "]->" => todo!("GRAPH Edge Pattern"), - "<~[" "]~" => todo!("GRAPH Edge Pattern"), - "~[" "]~>" => todo!("GRAPH Edge Pattern"), - "<-[" "]->" => todo!("GRAPH Edge Pattern"), - "-[" "]-" => todo!("GRAPH Edge Pattern"), +GraphFullEdgePattern: ast::AstNode = { + "<-[" "]-" + => state.node(ast::GraphMatchEdge {direction: ast::GraphMatchDirection::Left, ..pat}, lo..hi), + "~[" "]~" + => state.node(ast::GraphMatchEdge {direction: ast::GraphMatchDirection::Undirected, ..pat}, lo..hi), + "-[" "]->" + => state.node(ast::GraphMatchEdge {direction: ast::GraphMatchDirection::Right, ..pat}, lo..hi), + "<~[" "]~" + => state.node(ast::GraphMatchEdge {direction: ast::GraphMatchDirection::LeftOrUndirected, ..pat}, lo..hi), + "~[" "]~>" + => state.node(ast::GraphMatchEdge {direction: ast::GraphMatchDirection::UndirectedOrRight, ..pat}, lo..hi), + "<-[" "]->" + => state.node(ast::GraphMatchEdge {direction: ast::GraphMatchDirection::LeftOrRight, ..pat}, lo..hi), + "-[" "]-" + => state.node(ast::GraphMatchEdge {direction: ast::GraphMatchDirection::LeftOrUndirectedOrRight, ..pat}, lo..hi), +} + +GraphEdgePatternFiller: ast::GraphMatchEdge = { + => { + let ast::GraphMatchElement{variable, label, where_clause} = pat; + ast::GraphMatchEdge {direction: ast::GraphMatchDirection::Undirected, quantifier: None, variable, label, where_clause} + } +} + +GraphAbbreviatedEdgePattern: ast::AstNode = { + => { + state.node(ast::GraphMatchEdge { + direction, + quantifier: None, + variable: None, + label: None, + where_clause: None, + }, lo..hi) + } } -GraphAbbreviatedEdgePattern: () = { - "<-" => todo!("GRAPH Edge Pattern"), - "~" => todo!("GRAPH Edge Pattern"), - "->" => todo!("GRAPH Edge Pattern"), - "<~" => todo!("GRAPH Edge Pattern"), - "~>" => todo!("GRAPH Edge Pattern"), - "<->" => todo!("GRAPH Edge Pattern"), - "-" => todo!("GRAPH Edge Pattern"), +GraphAbbreviatedEdgePatternEdge: ast::GraphMatchDirection = { + "<-" => ast::GraphMatchDirection::Left, + "~" => ast::GraphMatchDirection::Undirected, + "->" => ast::GraphMatchDirection::Right, + "<~" => ast::GraphMatchDirection::LeftOrUndirected, + "~>" => ast::GraphMatchDirection::UndirectedOrRight, + "<->" => ast::GraphMatchDirection::LeftOrRight, + "-" => ast::GraphMatchDirection::LeftOrUndirectedOrRight, } -GraphParenthesizedPathPatternExpr: () = { - "(" +GraphParenthesizedPathPatternExpr: ast::AstNode = { + "(" - - - ")" => todo!("GRAPH Paren Path Pattern "), + + + ")" => { + state.node(ast::GraphPathSubPattern { + variable, + mode, + path, + where_clause, + }, lo..hi) + } } -GraphSubPathVariableDecl: () = { - "=" => { - todo!("SubPath Variable Decl") - } +GraphSubPathVariableDecl: ast::SymbolPrimitive = { + "=", } -GraphParenthesizedPathPatternWhereClause: () = { - "WHERE" => todo!("GRAPH SubPath WHERE CLAUSE"), +GraphParenthesizedPathPatternWhereClause: Box = { + "WHERE" , } // 10.7 #[inline] -GraphPatternQuantifier: () = { - "*" => todo!("GraphPatternQuantifier"), - "+" => todo!("GraphPatternQuantifier"), - "{" "}" => todo!("GraphPatternQuantifier"), - "{" "," "}" => todo!("GraphPatternQuantifier"), +GraphPatternQuantifier: ast::AstNode = { + "*" => state.node(ast::GraphMatchQuantifier{ lower:0, upper:None }, lo..hi), + "+" => state.node(ast::GraphMatchQuantifier{ lower:1, upper:None }, lo..hi), + "{" "}" => { + // TODO error on invalid literal + let k = std::num::NonZeroU32::new(k.parse().unwrap()).unwrap(); + state.node(ast::GraphMatchQuantifier{ lower: k.into(), upper: Some(k) }, lo..hi) + }, + "{" "," "}" => { + // TODO error on invalid literal + let lower = if let Some(l) = l { + l.parse().unwrap() + } else { + 0 + }; + let upper = if let Some(n) = l { + Some(std::num::NonZeroU32::new(n.parse().unwrap()).expect("non-zero upper bound")) + } else { + None + }; + state.node(ast::GraphMatchQuantifier{ lower, upper }, lo..hi) + }, } // 10.8 -GraphLabelExpr: () = { - => todo!("GraphLabelExpr"), - => todo!("GraphLabelExpr"), +GraphLabelExpr: ast::AstNode = { + , + , } -GraphLabelDisjunction: () = { - "|" => todo!("GraphLabelDisjunction"), +GraphLabelDisjunction: ast::AstNode = { + "|" => { + let d = if let ast::GraphMatchLabel::Disjunction(mut d) = l.node { + d.push(r); + d + } else { + vec![l,r] + }; + state.node(ast::GraphMatchLabel::Disjunction(d), lo..hi) + }, } -GraphLabelTerm: () = { - => todo!("GraphLabelTerm"), - => todo!("GraphLabelTerm"), +GraphLabelTerm: ast::AstNode = { + , + , } -GraphLabelConjunction: () = { - "&" => todo!("GraphLabelConjunction"), +GraphLabelConjunction: ast::AstNode = { + "&" => { + let c = if let ast::GraphMatchLabel::Conjunction(mut c) = l.node { + c.push(r); + c + } else { + vec![l,r] + }; + state.node(ast::GraphMatchLabel::Conjunction(c), lo..hi) + }, } -GraphLabelFactor: () = { - => todo!("GraphLabelFactor"), +GraphLabelFactor: ast::AstNode = { + , + "!" => state.node(ast::GraphMatchLabel::Negated(Box::new(l)), lo..hi), } #[inline] -GraphLabelPrimary: () = { - => todo!("GraphLabelPrimary"), - "%" => todo!("GraphLabelPrimary"), - "(" ")" => todo!("GraphLabelPrimary"), +GraphLabelPrimary: ast::AstNode = { + => state.node(ast::GraphMatchLabel::Name(l), lo..hi), + "%" => state.node(ast::GraphMatchLabel::Wildcard, lo..hi), + "(" ")" => state.node(e.node, lo..hi), } // 10.9 -GraphSimplifiedPathPatternExpression: () = { - "<-/" "/-" => todo!("GraphSimplifiedPathPatternExpression"), - "~/" "/~" => todo!("GraphSimplifiedPathPatternExpression"), - "-/" "/->" => todo!("GraphSimplifiedPathPatternExpression"), - "<~/" "/~" => todo!("GraphSimplifiedPathPatternExpression"), - "~/" "/~>" => todo!("GraphSimplifiedPathPatternExpression"), - "<-/" "/->" => todo!("GraphSimplifiedPathPatternExpression"), - "-/" "/-" => todo!("GraphSimplifiedPathPatternExpression"), +GraphSimplifiedPathPatternExpression: ast::AstNode = { + "<-/" "/-" => state.node(ast::GraphMatchSimplified{dir: ast::GraphMatchDirection::Left, pattern}, lo..hi), + "~/" "/~" => state.node(ast::GraphMatchSimplified{dir: ast::GraphMatchDirection::Undirected, pattern}, lo..hi), + "-/" "/->" => state.node(ast::GraphMatchSimplified{dir: ast::GraphMatchDirection::Right, pattern}, lo..hi), + "<~/" "/~" => state.node(ast::GraphMatchSimplified{dir: ast::GraphMatchDirection::LeftOrUndirected, pattern}, lo..hi), + "~/" "/~>" => state.node(ast::GraphMatchSimplified{dir: ast::GraphMatchDirection::UndirectedOrRight, pattern}, lo..hi), + "<-/" "/->" => state.node(ast::GraphMatchSimplified{dir: ast::GraphMatchDirection::LeftOrRight, pattern}, lo..hi), + "-/" "/-" => state.node(ast::GraphMatchSimplified{dir: ast::GraphMatchDirection::LeftOrUndirectedOrRight, pattern}, lo..hi), } -GraphSimplifiedContents: () = { +GraphSimplifiedContents: ast::AstNode = { , - , - , -} - -GraphSimplifiedMultiSetAlternation: () = { - > => todo!("GRAPH Path Pattern multiset"), -} - -GraphSimplifiedMultiSetAlternationContinue: () = { - "|+|" => todo!("GRAPH Path Pattern multiset"), + > => state.node(ast::GraphMatchSimplifiedPattern::Union(terms), lo..hi), + > => state.node(ast::GraphMatchSimplifiedPattern::Multiset(terms), lo..hi), } -GraphSimplifiedPathUnion: () = { - > => todo!("GRAPH Path Pattern unio"), -} - -GraphSimplifiedPathUnionContinue: () = { - "|" => todo!("GRAPH Path Pattern union"), -} - -GraphSimplifiedTerm: () = { - => todo!("GraphSimplifiedTerm"), -} - -GraphSimplifiedFactorLow: () = { - => todo!("GraphSimplifiedFactorLow"), - => todo!("GraphSimplifiedFactorLow"), +GraphSimplifiedTerm: ast::AstNode = { + => { + if t.is_none() { + return f; + } + let t = t.unwrap(); + let path = if let ast::GraphMatchSimplifiedPattern::Path(mut v) = t.node { + v.push(f); + v + } else { + vec![t, f] + }; + state.node(ast::GraphMatchSimplifiedPattern::Path(path), lo..hi) + } } -GraphSimplifiedConjunction: () = { - "&" => todo!("GraphSimplifiedConjunction"), +GraphSimplifiedFactorLow: ast::AstNode = { + , + , } -GraphSimplifiedFactorHigh: () = { - => todo!("GraphSimplifiedFactorHigh"), - => todo!("GraphSimplifiedFactorHigh"), - "?" => todo!("GraphSimplifiedFactorHigh"), +#[inline] +GraphSimplifiedConjunction: ast::AstNode = { + "&" => { + let conj = if let ast::GraphMatchSimplifiedPattern::Conjunction(mut conj) = l.node { + conj.push(r); + conj + } else { + vec![l,r] + }; + state.node(ast::GraphMatchSimplifiedPattern::Conjunction(conj), lo..hi) + } } -GraphSimplifiedTertiary: () = { - => todo!("GraphSimplifiedTertiary"), - "<" => todo!("GraphSimplifiedTertiary"), - "~" => todo!("GraphSimplifiedTertiary"), - ">" => todo!("GraphSimplifiedTertiary"), - "<~" => todo!("GraphSimplifiedTertiary"), - "~" ">" => todo!("GraphSimplifiedTertiary"), - "<" ">" => todo!("GraphSimplifiedTertiary"), - "-" => todo!("GraphSimplifiedTertiary"), +GraphSimplifiedFactorHigh: ast::AstNode = { + , + => state.node(ast::GraphMatchSimplifiedPattern::Quantified(Box::new(s), q), lo..hi), + "?" => state.node(ast::GraphMatchSimplifiedPattern::Questioned(Box::new(s)), lo..hi), } -GraphSimplifiedSecondary: () = { - => todo!("GraphSimplifiedSecondary"), - => todo!("GraphSimplifiedSecondary"), +GraphSimplifiedTertiary: ast::AstNode = { + , + "<" => state.node(ast::GraphMatchSimplifiedPattern::Direction(ast::GraphMatchDirection::Left, Box::new(s)), lo..hi), + "~" => state.node(ast::GraphMatchSimplifiedPattern::Direction(ast::GraphMatchDirection::Undirected, Box::new(s)), lo..hi), + ">" => state.node(ast::GraphMatchSimplifiedPattern::Direction(ast::GraphMatchDirection::Right, Box::new(s)), lo..hi), + "<~" => state.node(ast::GraphMatchSimplifiedPattern::Direction(ast::GraphMatchDirection::LeftOrUndirected, Box::new(s)), lo..hi), + "~" ">" => state.node(ast::GraphMatchSimplifiedPattern::Direction(ast::GraphMatchDirection::UndirectedOrRight, Box::new(s)), lo..hi), + "<" ">" => state.node(ast::GraphMatchSimplifiedPattern::Direction(ast::GraphMatchDirection::LeftOrRight, Box::new(s)), lo..hi), + "-" => state.node(ast::GraphMatchSimplifiedPattern::Direction(ast::GraphMatchDirection::LeftOrUndirectedOrRight, Box::new(s)), lo..hi), } -GraphSimplifiedNegation: () = { - => todo!("GraphLabelFactor"), +GraphSimplifiedSecondary: ast::AstNode = { + => { + if negated.is_some() { + state.node(ast::GraphMatchSimplifiedPattern::Negated(Box::new(simp)), lo..hi) + } else { + simp + } + } } -GraphSimplifiedPrimary: () = { - => todo!("GraphSimplifiedPrimary"), - "(" ")" => todo!("GraphSimplifiedPrimary"), +#[inline] +GraphSimplifiedPrimary: ast::AstNode = { + => state.node(ast::GraphMatchSimplifiedPattern::Label(l), lo..hi), + "(" ")", } // 10.10 #[inline] -GraphElementReference = GraphElementVariable; +GraphElementReference = ; // 10.11 #[inline] -GraphPathReference = GraphPathVariable; +GraphPathReference = ; // 5.3 - #[inline] -GraphName = ; - +GraphName = ; #[inline] GraphVertexVariable = ; - #[inline] GraphEdgeVariable = ; - #[inline] -GraphElementVariable: () = { - => { () } -} +GraphElementVariable = ; #[inline] -GraphLabelName: () = { - => { () } -} +GraphLabelName = ; #[inline] -GraphPathOrSubPathVariable: () = { - //, - //, - => { () } -} +GraphPathOrSubPathVariable = ; #[inline] GraphPathVariable = ; #[inline] GraphSubPathVariable = ; #[inline] -GraphPatternVariable: () = { - //, - //, - => { () } -} +GraphPatternVariable = ; #[inline] @@ -1180,6 +1225,8 @@ ExprPrecedence09: Synth = { }, lo..hi) )) }, + // PartiQL extension to treat `(MATCH )` as an expression. + => Synth::empty(gm), , } @@ -1856,6 +1903,44 @@ CommaSepPlus: Vec = { } } +// Comma as separator (i.e. ", , "); at least 1 arg +// This is a macro; see http://lalrpop.github.io/lalrpop/tutorial/006_macros.html +MultiSetSepPlus: Vec = { + )*> => { + v.push(e); + v + } +} + + +// This is a macro; see http://lalrpop.github.io/lalrpop/tutorial/006_macros.html +PuncSepStar: Vec = { + )*> => match e { + None => vec![], + Some(e) => { + v.insert(0,e); + v + } + } +} + +// This is a macro; see http://lalrpop.github.io/lalrpop/tutorial/006_macros.html +PuncSepPlus: Vec = { +

)*> => { + v.into_iter().map(|t|t.0).chain(std::iter::once(e)).collect() + } +} + +// This is a macro; see http://lalrpop.github.io/lalrpop/tutorial/006_macros.html +PuncSep2Plus: Vec = { +

)+> => { + v.into_iter().map(|t|t.0).chain(std::iter::once(e)).collect() + } +} + + +// TODO rename to Identifier? +#[inline] SymbolPrimitive: ast::SymbolPrimitive = { => ast::SymbolPrimitive { value: ident.to_owned(), @@ -1865,6 +1950,28 @@ SymbolPrimitive: ast::SymbolPrimitive = { value: ident.to_owned(), case: ast::CaseSensitivity::CaseSensitive, }, + /* + => ast::SymbolPrimitive { + value: ident.to_owned(), + case: ast::CaseSensitivity::CaseInsensitive + }, + */ +} + +#[inline] +Identifier: ast::SymbolPrimitive = { + => ast::SymbolPrimitive { + value: ident.to_owned(), + case: ast::CaseSensitivity::CaseInsensitive, + }, + => ast::SymbolPrimitive { + value: ident.to_owned(), + case: ast::CaseSensitivity::CaseSensitive, + }, + => ast::SymbolPrimitive { + value: ident.to_owned(), + case: ast::CaseSensitivity::CaseInsensitive + }, } AsIdent: ast::SymbolPrimitive = { @@ -1895,7 +2002,7 @@ FnNonReservedKeyword: &'static str = { } -#[inline] + NonReservedKeyword: &'static str = { "ANY" => "ANY", "SIMPLE" => "SIMPLE", From d887dc01cc3caf7ecac8da3fc85e8cc40c860f42 Mon Sep 17 00:00:00 2001 From: Josh Pschorr Date: Fri, 4 Apr 2025 11:00:39 -0700 Subject: [PATCH 3/9] GRAPH_TABLE pretty print & lexer/parser updates for non-reserved keywords --- .../src/ast_to_dot.rs | 1 + partiql-ast/src/ast.rs | 20 +- partiql-ast/src/pretty.rs | 382 ++++++++++++------ partiql-ast/src/visit.rs | 25 +- partiql-common/src/pretty.rs | 22 - partiql-parser/src/lexer/partiql.rs | 188 +++++---- partiql-parser/src/parse/partiql.lalrpop | 158 ++++---- partiql/tests/pretty.rs | 130 +++--- .../tests/snapshots/pretty__graph_edge_1.snap | 16 +- .../snapshots/pretty__graph_edge_10.snap | 17 +- .../snapshots/pretty__graph_edge_11.snap | 16 +- .../snapshots/pretty__graph_edge_12.snap | 17 +- .../snapshots/pretty__graph_edge_13.snap | 16 +- .../snapshots/pretty__graph_edge_14.snap | 16 +- .../tests/snapshots/pretty__graph_edge_2.snap | 17 +- .../tests/snapshots/pretty__graph_edge_3.snap | 16 +- .../tests/snapshots/pretty__graph_edge_4.snap | 16 +- .../tests/snapshots/pretty__graph_edge_5.snap | 16 +- .../tests/snapshots/pretty__graph_edge_6.snap | 17 +- .../tests/snapshots/pretty__graph_edge_7.snap | 16 +- .../tests/snapshots/pretty__graph_edge_8.snap | 17 +- .../tests/snapshots/pretty__graph_edge_9.snap | 16 +- .../tests/snapshots/pretty__graph_etc_1.snap | 21 +- .../tests/snapshots/pretty__graph_etc_2.snap | 21 +- .../pretty__graph_parenthesized_1.snap | 16 +- .../pretty__graph_parenthesized_2.snap | 17 +- .../pretty__graph_parenthesized_3.snap | 16 +- ..._2.snap => pretty__graph_path_mode_1.snap} | 16 +- ..._3.snap => pretty__graph_path_mode_2.snap} | 16 +- .../snapshots/pretty__graph_path_mode_3.snap | 33 ++ .../snapshots/pretty__graph_path_var_1.snap | 16 +- .../snapshots/pretty__graph_patterns_2.snap | 21 +- .../snapshots/pretty__graph_patterns_3.snap | 16 +- .../pretty__graph_quantifiers_1.snap | 16 +- .../pretty__graph_quantifiers_2.snap | 16 +- .../pretty__graph_quantifiers_3.snap | 16 +- .../pretty__graph_quantifiers_4.snap | 16 +- .../pretty__graph_quantifiers_5.snap | 17 +- .../pretty__graph_quantifiers_6.snap | 17 +- .../pretty__graph_quantifiers_7.snap | 16 +- .../pretty__graph_quantifiers_8.snap | 16 +- ...nap => pretty__graph_search_prefix_1.snap} | 23 +- .../pretty__graph_search_prefix_2.snap | 33 ++ ...nap => pretty__graph_search_prefix_3.snap} | 16 +- ...nap => pretty__graph_search_prefix_4.snap} | 24 +- ...nap => pretty__graph_search_prefix_5.snap} | 23 +- .../pretty__graph_search_prefix_6.snap | 34 ++ .../snapshots/pretty__graph_selectors_2.snap | 36 -- .../snapshots/pretty__graph_selectors_4.snap | 35 -- .../snapshots/pretty__graph_selectors_6.snap | 36 -- .../snapshots/pretty__graph_union_1.snap | 14 +- .../snapshots/pretty__graph_union_2.snap | 14 +- 52 files changed, 949 insertions(+), 833 deletions(-) rename partiql/tests/snapshots/{pretty__graph_restrictors_2.snap => pretty__graph_path_mode_1.snap} (54%) rename partiql/tests/snapshots/{pretty__graph_restrictors_3.snap => pretty__graph_path_mode_2.snap} (53%) create mode 100644 partiql/tests/snapshots/pretty__graph_path_mode_3.snap rename partiql/tests/snapshots/{pretty__graph_selectors_1.snap => pretty__graph_search_prefix_1.snap} (51%) create mode 100644 partiql/tests/snapshots/pretty__graph_search_prefix_2.snap rename partiql/tests/snapshots/{pretty__graph_restrictors_1.snap => pretty__graph_search_prefix_3.snap} (54%) rename partiql/tests/snapshots/{pretty__graph_selectors_3.snap => pretty__graph_search_prefix_4.snap} (52%) rename partiql/tests/snapshots/{pretty__graph_selectors_5.snap => pretty__graph_search_prefix_5.snap} (52%) create mode 100644 partiql/tests/snapshots/pretty__graph_search_prefix_6.snap delete mode 100644 partiql/tests/snapshots/pretty__graph_selectors_2.snap delete mode 100644 partiql/tests/snapshots/pretty__graph_selectors_4.snap delete mode 100644 partiql/tests/snapshots/pretty__graph_selectors_6.snap diff --git a/extension/partiql-extension-visualize/src/ast_to_dot.rs b/extension/partiql-extension-visualize/src/ast_to_dot.rs index 7f013a20..f369358c 100644 --- a/extension/partiql-extension-visualize/src/ast_to_dot.rs +++ b/extension/partiql-extension-visualize/src/ast_to_dot.rs @@ -508,6 +508,7 @@ impl ToDot for AstToDot { let lbl = match &ast.kind { ast::FromLetKind::Scan => "Scan", ast::FromLetKind::Unpivot => "Unpivot", + ast::FromLetKind::GraphTable => "GRAPH_TABLE", }; let id = out.node_auto_labelled(lbl).id(); diff --git a/partiql-ast/src/ast.rs b/partiql-ast/src/ast.rs index 9792069c..9e745272 100644 --- a/partiql-ast/src/ast.rs +++ b/partiql-ast/src/ast.rs @@ -811,6 +811,7 @@ pub struct FromLet { pub enum FromLetKind { Scan, Unpivot, + GraphTable, } #[derive(Visit, Clone, Debug, PartialEq)] @@ -860,7 +861,7 @@ pub struct GraphMatch { pub pattern: AstNode, // TODO remove #[visit(skip)] - pub shape: Option, + pub shape: GraphTableShape, } // TODO #[derive(Visit, Clone, Debug, PartialEq)] @@ -882,21 +883,12 @@ pub enum GraphMatchMode { RepeatableElements, } -/// A graph match clause as defined in GPML -/// See https://arxiv.org/abs/2112.06217 -#[derive(Visit, Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct GraphMatchExpr { - #[visit(skip)] // TODO - pub patterns: Vec>, -} - #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum GraphTableShape { - Rows(AstNode), - Columns(AstNode), - Export(AstNode), +pub struct GraphTableShape { + pub rows: Option>, + pub cols: Option>, + pub export: Option>, } #[derive(Clone, Default, Debug, PartialEq, Eq)] diff --git a/partiql-ast/src/pretty.rs b/partiql-ast/src/pretty.rs index a18222f9..0d80c6ea 100644 --- a/partiql-ast/src/pretty.rs +++ b/partiql-ast/src/pretty.rs @@ -1,11 +1,13 @@ use crate::ast::*; use partiql_common::pretty::{ - pretty_bracketed_doc, pretty_list, pretty_parenthesized_doc, pretty_prefixed_doc, - pretty_seperated, pretty_seperated_doc, pretty_seq, pretty_seq_doc, pretty_surrounded_doc, + pretty_list, pretty_parenthesized_doc, pretty_prefixed_doc, pretty_seperated, + pretty_seperated_doc, pretty_seq, pretty_seq_doc, pretty_surrounded, pretty_surrounded_doc, PrettyDoc, PRETTY_INDENT_MINOR_NEST, PRETTY_INDENT_SUBORDINATE_CLAUSE_NEST, }; use pretty::{DocAllocator, DocBuilder}; use std::borrow::Cow; +use std::num::NonZeroU32; + impl PrettyDoc for AstNode where T: PrettyDoc, @@ -927,6 +929,7 @@ impl PrettyDoc for FromLet { let clause = match kind { FromLetKind::Scan => expr, FromLetKind::Unpivot => pretty_prefixed_doc("UNPIVOT", expr, arena), + FromLetKind::GraphTable => pretty_prefixed_doc("GRAPH_TABLE", expr, arena), }; if aliases.is_empty() { @@ -1004,113 +1007,222 @@ impl PrettyDoc for GraphMatch { D::Doc: Clone, A: Clone, { - let head = arena.intersperse( - [self.expr.pretty_doc(arena), arena.text("MATCH")], - arena.space(), - ); - let patterns = self.graph_expr.pretty_doc(arena); - let match_expr = arena.intersperse([head, patterns], arena.space()); - - let parens_needed = self.graph_expr.node.patterns.len() > 1; - if parens_needed { - pretty_parenthesized_doc(match_expr, arena) - } else { - match_expr + let GraphMatch { + expr, + pattern, + shape, + } = self; + let head = arena.intersperse([expr.pretty_doc(arena), arena.text("MATCH")], arena.space()); + let patterns = pattern.pretty_doc(arena).group(); + let mut match_expr = arena.intersperse([head, patterns], arena.space()); + let shapes = [ + shape.rows.as_ref().map(|d| d.pretty_doc(arena)), + shape.cols.as_ref().map(|d| d.pretty_doc(arena)), + shape.export.as_ref().map(|d| d.pretty_doc(arena)), + ] + .into_iter() + .filter_map(|x| x) + .collect::>(); + if !shapes.is_empty() { + let docs = std::iter::once(match_expr).chain(shapes); + match_expr = arena.intersperse(docs, arena.hardline()); } + + pretty_parenthesized_doc(match_expr, arena) } } -impl PrettyDoc for GraphMatchExpr { +impl PrettyDoc for GraphTableRows { fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> where D: DocAllocator<'b, A>, D::Doc: Clone, A: Clone, { - let selector = self.selector.clone().map(|s| { - let parts: Vec> = match s { - GraphMatchSelector::AnyShortest => vec!["ANY".into(), "SHORTEST".into()], - GraphMatchSelector::AllShortest => vec!["ALL".into(), "SHORTEST".into()], - GraphMatchSelector::Any => vec!["ANY".into()], - GraphMatchSelector::AnyK(k) => vec!["ANY".into(), k.to_string().into()], - GraphMatchSelector::ShortestK(k) => vec!["SHORTEST".into(), k.to_string().into()], - GraphMatchSelector::ShortestKGroup(k) => { - vec!["SHORTEST".into(), k.to_string().into(), "GROUP".into()] - } - }; + todo!() + } +} - arena.intersperse(parts, arena.space()).group() - }); - let patterns = pretty_list(&self.patterns, PRETTY_INDENT_MINOR_NEST, arena); - if let Some(selector) = selector { - arena.intersperse([selector, patterns], arena.softline()) - } else { - patterns +impl PrettyDoc for GraphTableColumns { + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + todo!() + } +} + +impl PrettyDoc for GraphTableExport { + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + todo!() + } +} + +impl PrettyDoc for GraphPattern { + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + let GraphPattern { + mode, + patterns, + keep, + where_clause, + } = &self; + let patterns = pretty_list(patterns, PRETTY_INDENT_MINOR_NEST, arena).group(); + let parts = [ + mode.as_ref().map(|inner| inner.pretty_doc(arena)), + Some(patterns), + keep.as_ref().map(|inner| inner.pretty_doc(arena)), + self.where_clause.as_ref().map(|clause| { + arena.intersperse( + [arena.text("WHERE"), clause.pretty_doc(arena)], + arena.space(), + ) + }), + ] + .into_iter() + .filter_map(|x| x); + + arena.intersperse(parts, arena.space()) + } +} + +impl PrettyDoc for GraphMatchMode { + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + match self { + GraphMatchMode::DifferentEdges => arena.text("DIFFERENT EDGES"), + GraphMatchMode::RepeatableElements => arena.text("REPEATABLE ELEMENTS"), } - .group() } } -impl PrettyDoc for GraphMatchPattern { +impl PrettyDoc for GraphPathPrefix { fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> where D: DocAllocator<'b, A>, D::Doc: Clone, A: Clone, { - let pattern = pretty_seperated( - arena.nil().append(arena.space()), - &self.parts, - PRETTY_INDENT_MINOR_NEST, - arena, - ); - let mut doc = if let Some(r) = &self.restrictor { - match r { - GraphMatchRestrictor::Trail => arena.text("TRAIL"), - GraphMatchRestrictor::Acyclic => arena.text("ACYCLIC"), - GraphMatchRestrictor::Simple => arena.text("SIMPLE"), + match self { + GraphPathPrefix::Mode(mode) => mode.pretty_doc(arena), + GraphPathPrefix::Search(search, mode) => { + let mode = mode.as_ref().map(|mode| mode.pretty_doc(arena)); + let parts = match search { + GraphPathSearchPrefix::All => vec![Some(arena.text("ALL")), mode], + GraphPathSearchPrefix::Any => vec![Some(arena.text("ANY")), mode], + GraphPathSearchPrefix::AnyK(k) => vec![ + Some(arena.text("ANY")), + Some(arena.text(k.to_string())), + mode, + ], + GraphPathSearchPrefix::AllShortest => { + vec![Some(arena.text("ALL")), Some(arena.text("SHORTEST")), mode] + } + GraphPathSearchPrefix::AnyShortest => { + vec![Some(arena.text("ANY")), Some(arena.text("SHORTEST")), mode] + } + GraphPathSearchPrefix::ShortestK(k) => vec![ + Some(arena.text("SHORTEST")), + Some(arena.text(k.to_string())), + mode, + ], + GraphPathSearchPrefix::ShortestKGroup(k) => vec![ + Some(arena.text("SHORTEST")), + k.as_ref().map(|k| arena.text(k.to_string())), + mode, + Some(arena.text("GROUPS")), + ], + } + .into_iter() + .filter_map(|x| x) + .collect::>(); + arena.intersperse(parts, arena.space()) } - .append(arena.space()) - } else { - arena.nil() + } + } +} + +impl PrettyDoc for GraphPathMode { + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + let txt = match self { + GraphPathMode::Walk => "WALK", + GraphPathMode::Trail => "TRAIL", + GraphPathMode::Acyclic => "ACYCLIC", + GraphPathMode::Simple => "SIMPLE", }; + arena.text(txt) + } +} + +impl PrettyDoc for GraphPathPattern { + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + let var = self + .variable + .as_ref() + .map(|var| arena.intersperse([arena.text(&var.value), arena.text("=")], arena.space())); - let pattern = if let Some(v) = &self.variable { + let prefix = self.prefix.as_ref().map(|prefix| prefix.pretty_doc(arena)); + + let parts = [var, prefix, Some(self.path.pretty_doc(arena))] + .into_iter() + .filter_map(|x| x); + arena.intersperse(parts, arena.space()) + } +} + +impl PrettyDoc for GraphPathSubPattern { + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + let var = self + .variable + .as_ref() + .map(|var| arena.intersperse([arena.text(&var.value), arena.text("=")], arena.space())); + + let mode = self.mode.as_ref().map(|prefix| prefix.pretty_doc(arena)); + let where_clause = self.where_clause.as_ref().map(|clause| { arena.intersperse( - [arena.text(&v.value), arena.text("="), pattern], + [arena.text("WHERE"), clause.pretty_doc(arena)], arena.space(), ) - } else { - pattern - }; - doc = doc.append(pattern); - - let brackets = - self.restrictor.is_some() || self.quantifier.is_some() || self.prefilter.is_some(); + }); - if brackets { - let doc = if let Some(filter) = &self.prefilter { - arena.intersperse( - [doc, arena.text("WHERE"), filter.pretty_doc(arena)], - arena.space(), - ) - } else { - doc - }; - let bracketed = pretty_bracketed_doc(doc, arena); - if let Some(postfix) = &self.quantifier { - arena.concat([bracketed, postfix.pretty_doc(arena)]) - } else { - bracketed - } - } else { - doc - } - .group() + let parts = [var, mode, Some(self.path.pretty_doc(arena)), where_clause] + .into_iter() + .filter_map(|x| x); + arena.intersperse(parts, arena.space()) } } -impl PrettyDoc for GraphMatchPatternPart { +impl PrettyDoc for GraphMatchPathPattern { fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> where D: DocAllocator<'b, A>, @@ -1118,9 +1230,16 @@ impl PrettyDoc for GraphMatchPatternPart { A: Clone, { match self { - GraphMatchPatternPart::Node(n) => n.pretty_doc(arena), - GraphMatchPatternPart::Edge(e) => e.pretty_doc(arena), - GraphMatchPatternPart::Pattern(p) => p.pretty_doc(arena), + GraphMatchPathPattern::Path(path) => { + arena.intersperse(path.iter().map(|e| e.pretty_doc(arena)), arena.space()) + } + GraphMatchPathPattern::Quantified(path, quantified) => { + arena.concat([path.pretty_doc(arena), quantified.pretty_doc(arena)]) + } + GraphMatchPathPattern::Sub(path) => pretty_surrounded(path, "(", ")", arena), + GraphMatchPathPattern::Node(node) => node.pretty_doc(arena), + GraphMatchPathPattern::Edge(edge) => edge.pretty_doc(arena), + _ => todo!("{:?}", self), } } } @@ -1132,19 +1251,30 @@ impl PrettyDoc for GraphMatchNode { D::Doc: Clone, A: Clone, { - let mut spec = arena.nil(); - if let Some(r) = &self.variable { - spec = spec.append(arena.text(&r.value)); - } - if let Some(l) = &self.label { - debug_assert_eq!(l.len(), 1); // TODO - spec = spec.append(arena.text(":")).append(arena.text(&l[0].value)); - } - if let Some(r) = &self.prefilter { - let parts = [spec, arena.text("WHERE"), r.pretty_doc(arena)]; - spec = arena.intersperse(parts, arena.space()); - } - pretty_surrounded_doc(spec, "(", ")", arena) + let name: Vec<_> = [ + self.variable.as_ref().map(|var| arena.text(&var.value)), + self.label + .as_ref() + .map(|label| arena.concat([arena.text(":"), label.pretty_doc(arena)])), + ] + .into_iter() + .filter_map(|x| x) + .collect(); + let name = if name.is_empty() { + None + } else { + Some(arena.concat(name)) + }; + let where_clause = self.where_clause.as_ref().map(|clause| { + arena.intersperse( + [arena.text("WHERE"), clause.pretty_doc(arena)], + arena.space(), + ) + }); + let parts = [name, where_clause].into_iter().filter_map(|x| x); + + let spec = arena.intersperse(parts, arena.space()); + pretty_surrounded_doc(spec, "(", ")", arena).group() } } @@ -1155,31 +1285,32 @@ impl PrettyDoc for GraphMatchEdge { D::Doc: Clone, A: Clone, { - let mut spec = None; - if let Some(r) = &self.variable { - spec = spec - .unwrap_or_else(|| arena.nil()) - .append(arena.text(&r.value)) - .into(); - } - if let Some(l) = &self.label { - debug_assert_eq!(l.len(), 1); // TODO - spec = spec - .unwrap_or_else(|| arena.nil()) - .append(arena.text(":")) - .append(arena.text(&l[0].value)) - .into(); - } - if let Some(r) = &self.prefilter { - let parts = [ - spec.unwrap_or_else(|| arena.nil()), - arena.text("WHERE"), - r.pretty_doc(arena), - ]; - spec = Some(arena.intersperse(parts, arena.space())); - } + let name: Vec<_> = [ + self.variable.as_ref().map(|var| arena.text(&var.value)), + self.label + .as_ref() + .map(|label| arena.concat([arena.text(":"), label.pretty_doc(arena)])), + ] + .into_iter() + .filter_map(|x| x) + .collect(); + let name = if name.is_empty() { + None + } else { + Some(arena.concat(name)) + }; + let where_clause = self.where_clause.as_ref().map(|clause| { + arena.intersperse( + [arena.text("WHERE"), clause.pretty_doc(arena)], + arena.space(), + ) + }); + let parts = [name, where_clause] + .into_iter() + .filter_map(|x| x) + .collect::>(); - let mut edge = if let Some(spec) = spec { + let mut edge = if !parts.is_empty() { let (prefix, suffix) = match self.direction { GraphMatchDirection::Right => ("-[", "]->"), GraphMatchDirection::Left => ("<-[", "]-"), @@ -1189,6 +1320,7 @@ impl PrettyDoc for GraphMatchEdge { GraphMatchDirection::LeftOrRight => ("<-[", "]->"), GraphMatchDirection::LeftOrUndirectedOrRight => ("-[", "]-"), }; + let spec = arena.intersperse(parts, arena.space()); pretty_surrounded_doc(spec, prefix, suffix, arena) } else { let edge = match self.direction { @@ -1209,6 +1341,20 @@ impl PrettyDoc for GraphMatchEdge { } } +impl PrettyDoc for GraphMatchLabel { + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + match self { + GraphMatchLabel::Name(name) => arena.text(&name.value), + _ => todo!("{:?}", self), + } + } +} + impl PrettyDoc for GraphMatchQuantifier { fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> where diff --git a/partiql-ast/src/visit.rs b/partiql-ast/src/visit.rs index 0ec91fbc..0992b064 100644 --- a/partiql-ast/src/visit.rs +++ b/partiql-ast/src/visit.rs @@ -520,21 +520,16 @@ pub trait Visitor<'ast> { fn exit_graph_match(&mut self, _graph_pattern: &'ast ast::GraphMatch) -> Traverse { Traverse::Continue } - fn enter_graph_match_expr(&mut self, _graph_pattern: &'ast ast::GraphMatchExpr) -> Traverse { - Traverse::Continue - } - fn exit_graph_match_expr(&mut self, _graph_pattern: &'ast ast::GraphMatchExpr) -> Traverse { - Traverse::Continue - } + fn enter_graph_match_pattern( &mut self, - _graph_pattern: &'ast ast::GraphMatchPattern, + _graph_pattern: &'ast ast::GraphPathPattern, ) -> Traverse { Traverse::Continue } fn exit_graph_match_pattern( &mut self, - _graph_pattern: &'ast ast::GraphMatchPattern, + _graph_pattern: &'ast ast::GraphPathPattern, ) -> Traverse { Traverse::Continue } @@ -551,6 +546,20 @@ pub trait Visitor<'ast> { Traverse::Continue } + //TODO + fn enter_graph_match_element( + &mut self, + _graph_pattern: &'ast ast::GraphMatchElement, + ) -> Traverse { + Traverse::Continue + } + fn exit_graph_match_element( + &mut self, + _graph_pattern: &'ast ast::GraphMatchElement, + ) -> Traverse { + Traverse::Continue + } + fn enter_group_by_expr(&mut self, _group_by_expr: &'ast ast::GroupByExpr) -> Traverse { Traverse::Continue } diff --git a/partiql-common/src/pretty.rs b/partiql-common/src/pretty.rs index fd259e3c..ecdf2f06 100644 --- a/partiql-common/src/pretty.rs +++ b/partiql-common/src/pretty.rs @@ -196,28 +196,6 @@ where pretty_surrounded_doc(doc, "(", ")", arena) } -#[inline] -pub fn pretty_sp_bracketed_doc<'b, E, D, A>(doc: E, arena: &'b D) -> DocBuilder<'b, D, A> -where - E: Pretty<'b, D, A>, - D: DocAllocator<'b, A>, - D::Doc: Clone, - A: Clone, -{ - pretty_surrounded_doc(doc, "[ ", " ]", arena) -} - -#[inline] -pub fn pretty_bracketed_doc<'b, E, D, A>(doc: E, arena: &'b D) -> DocBuilder<'b, D, A> -where - E: Pretty<'b, D, A>, - D: DocAllocator<'b, A>, - D::Doc: Clone, - A: Clone, -{ - pretty_surrounded_doc(doc, "[", "]", arena) -} - #[inline] pub fn pretty_seq_doc<'i, 'b, I, E, D, A>( seq: I, diff --git a/partiql-parser/src/lexer/partiql.rs b/partiql-parser/src/lexer/partiql.rs index 62855b35..69f64b80 100644 --- a/partiql-parser/src/lexer/partiql.rs +++ b/partiql-parser/src/lexer/partiql.rs @@ -136,9 +136,6 @@ impl<'input> Iterator for PartiqlLexer<'input, '_> { } /// Tokens that the lexer can generate. -/// -/// # Note -/// Tokens with names beginning with `__` are used internally and not meant to be used outside lexing. #[derive(Logos, Debug, Clone, PartialEq, Eq, PartialOrd, Hash)] // TODO make pub(crate) ? // Skip whitespace @@ -320,8 +317,8 @@ pub enum Token<'input> { Asc, #[regex("(?i:And)")] And, - #[regex("(?i:Any)")] - Any, + #[regex("(?i:Any)", |lex| lex.slice())] + Any(&'input str), #[regex("(?i:As)")] As, #[regex("(?i:At)")] @@ -450,8 +447,8 @@ pub enum Token<'input> { Time, #[regex("(?i:Timestamp)")] Timestamp, - #[regex("(?i:Simple)")] - Simple, + #[regex("(?i:Simple)", |lex| lex.slice())] + Simple(&'input str), #[regex("(?i:Then)")] Then, #[regex("(?i:True)")] @@ -502,71 +499,72 @@ pub enum Token<'input> { Same, // Graph Keywords; non-reserved - #[regex("(?i:ACYCLIC)")] - Acyclic, - #[regex("(?i:BINDINGS)")] - Bindings, - #[regex("(?i:BOUND)")] - Bound, - #[regex("(?i:DESTINATION)")] - Destination, - #[regex("(?i:DIFFERENT)")] - Different, - #[regex("(?i:DIRECTED)")] - Directed, - #[regex("(?i:EDGE)")] - Edge, - #[regex("(?i:EDGES)")] - Edges, - #[regex("(?i:ELEMENTS)")] - Elements, - #[regex("(?i:LABEL)")] - Label, - #[regex("(?i:LABELED)")] - Labeled, - #[regex("(?i:NODE)")] - Node, - #[regex("(?i:PATHS)")] - Paths, - #[regex("(?i:PROPERTIES)")] - Properties, - #[regex("(?i:PROPERTY)")] - Property, - #[regex("(?i:PROPERTY_GRAPH_CATALOG)")] - PropertyGraphCatalog, - #[regex("(?i:PROPERTY_GRAPH_NAME)")] - PropertyGraphName, - #[regex("(?i:PROPERTY_GRAPH_SCHEMA)")] - PropertyGraphSchema, - #[regex("(?i:RELATIONSHIP)")] - Relationship, - #[regex("(?i:RELATIONSHIPS)")] - Relationships, - #[regex("(?i:SHORTEST)")] - Shortest, - #[regex("(?i:SINGLETONS)")] - Singletons, - #[regex("(?i:STEP)")] - Step, - #[regex("(?i:TABLES)")] - Tables, - #[regex("(?i:TRAIL)")] - Trail, - #[regex("(?i:VERTEX)")] - Vertex, - #[regex("(?i:WALK)")] - Walk, + // Note: non-reserved keywords carry their input text for usage as variable references, etc. + #[regex("(?i:ACYCLIC)", |lex| lex.slice())] + Acyclic(&'input str), + #[regex("(?i:BINDINGS)", |lex| lex.slice())] + Bindings(&'input str), + #[regex("(?i:BOUND)", |lex| lex.slice())] + Bound(&'input str), + #[regex("(?i:DESTINATION)", |lex| lex.slice())] + Destination(&'input str), + #[regex("(?i:DIFFERENT)", |lex| lex.slice())] + Different(&'input str), + #[regex("(?i:DIRECTED)", |lex| lex.slice())] + Directed(&'input str), + #[regex("(?i:EDGE)", |lex| lex.slice())] + Edge(&'input str), + #[regex("(?i:EDGES)", |lex| lex.slice())] + Edges(&'input str), + #[regex("(?i:ELEMENTS)", |lex| lex.slice())] + Elements(&'input str), + #[regex("(?i:LABEL)", |lex| lex.slice())] + Label(&'input str), + #[regex("(?i:LABELED)", |lex| lex.slice())] + Labeled(&'input str), + #[regex("(?i:NODE)", |lex| lex.slice())] + Node(&'input str), + #[regex("(?i:PATHS)", |lex| lex.slice())] + Paths(&'input str), + #[regex("(?i:PROPERTIES)", |lex| lex.slice())] + Properties(&'input str), + #[regex("(?i:PROPERTY)", |lex| lex.slice())] + Property(&'input str), + #[regex("(?i:PROPERTY_GRAPH_CATALOG)", |lex| lex.slice())] + PropertyGraphCatalog(&'input str), + #[regex("(?i:PROPERTY_GRAPH_NAME)", |lex| lex.slice())] + PropertyGraphName(&'input str), + #[regex("(?i:PROPERTY_GRAPH_SCHEMA)", |lex| lex.slice())] + PropertyGraphSchema(&'input str), + #[regex("(?i:RELATIONSHIP)", |lex| lex.slice())] + Relationship(&'input str), + #[regex("(?i:RELATIONSHIPS)", |lex| lex.slice())] + Relationships(&'input str), + #[regex("(?i:SHORTEST)", |lex| lex.slice())] + Shortest(&'input str), + #[regex("(?i:SINGLETONS)", |lex| lex.slice())] + Singletons(&'input str), + #[regex("(?i:STEP)", |lex| lex.slice())] + Step(&'input str), + #[regex("(?i:TABLES)", |lex| lex.slice())] + Tables(&'input str), + #[regex("(?i:TRAIL)", |lex| lex.slice())] + Trail(&'input str), + #[regex("(?i:VERTEX)", |lex| lex.slice())] + Vertex(&'input str), + #[regex("(?i:WALK)", |lex| lex.slice())] + Walk(&'input str), } impl Token<'_> { #[inline] pub fn is_var_non_reserved(&self) -> bool { - matches!(self, Token::Any | Token::Simple) || self.is_graph_non_reserved() + matches!(self, Token::Any(_) | Token::Simple(_)) || self.is_graph_non_reserved() } #[inline] pub fn is_fn_non_reserved(&self) -> bool { - matches!(self, Token::Any | Token::Simple) || self.is_graph_non_reserved() + matches!(self, Token::Any(_) | Token::Simple(_)) || self.is_graph_non_reserved() } #[inline] @@ -591,44 +589,44 @@ impl Token<'_> { pub fn is_graph_non_reserved(&self) -> bool { matches!( self, - Token::Acyclic - | Token::Bindings - | Token::Bound - | Token::Destination - | Token::Different - | Token::Directed - | Token::Edge - | Token::Edges - | Token::Elements - | Token::Label - | Token::Labeled - | Token::Node - | Token::Paths - | Token::Properties - | Token::Property - | Token::PropertyGraphCatalog - | Token::PropertyGraphName - | Token::PropertyGraphSchema - | Token::Relationship - | Token::Relationships - | Token::Shortest - | Token::Singletons - | Token::Step - | Token::Tables - | Token::Trail - | Token::Vertex - | Token::Walk + Token::Acyclic(_) + | Token::Bindings(_) + | Token::Bound(_) + | Token::Destination(_) + | Token::Different(_) + | Token::Directed(_) + | Token::Edge(_) + | Token::Edges(_) + | Token::Elements(_) + | Token::Label(_) + | Token::Labeled(_) + | Token::Node(_) + | Token::Paths(_) + | Token::Properties(_) + | Token::Property(_) + | Token::PropertyGraphCatalog(_) + | Token::PropertyGraphName(_) + | Token::PropertyGraphSchema(_) + | Token::Relationship(_) + | Token::Relationships(_) + | Token::Shortest(_) + | Token::Singletons(_) + | Token::Step(_) + | Token::Tables(_) + | Token::Trail(_) + | Token::Vertex(_) + | Token::Walk(_) ) } pub fn is_keyword(&self) -> bool { matches!( self, - Token::Acyclic + Token::Acyclic(_) | Token::All | Token::Asc | Token::And - | Token::Any + | Token::Any(_) | Token::As | Token::At | Token::Between @@ -683,10 +681,10 @@ impl Token<'_> { | Token::Table | Token::Time | Token::Timestamp - | Token::Simple - | Token::Shortest + | Token::Simple(_) + | Token::Shortest(_) | Token::Then - | Token::Trail + | Token::Trail(_) | Token::Union | Token::Unpivot | Token::Using diff --git a/partiql-parser/src/parse/partiql.lalrpop b/partiql-parser/src/parse/partiql.lalrpop index 4a0cca9b..56e74dac 100644 --- a/partiql-parser/src/parse/partiql.lalrpop +++ b/partiql-parser/src/parse/partiql.lalrpop @@ -344,7 +344,7 @@ GraphTable: ast::AstNode = { "GRAPH_TABLE" => { state.node(ast::FromLet { expr: Box::new(e), - kind: ast::FromLetKind::Scan, + kind: ast::FromLetKind::GraphTable, as_alias: None, at_alias: None, by_alias: None, @@ -357,14 +357,13 @@ ParenGraphMatch: ast::Expr = { } GraphMatch: ast::Expr = { - "MATCH" + "MATCH" => ast::Expr::GraphMatch(state.node(ast::GraphMatch{expr, pattern, shape}, lo..hi)), } GraphTableShape: ast::GraphTableShape = { - => ast::GraphTableShape::Rows(rows), - => ast::GraphTableShape::Columns(cols), - => ast::GraphTableShape::Export(export), + + => ast::GraphTableShape{rows, cols, export} } GraphTableRowsClause: ast::AstNode = { @@ -647,7 +646,7 @@ GraphPatternQuantifier: ast::AstNode = { } else { 0 }; - let upper = if let Some(n) = l { + let upper = if let Some(n) = u { Some(std::num::NonZeroU32::new(n.parse().unwrap()).expect("non-zero upper bound")) } else { None @@ -1570,8 +1569,7 @@ FunctionCall: CallSite = { #[inline] FunctionName: ast::SymbolPrimitive = { , - => - ast::SymbolPrimitive { value: ident.to_owned(), case: ast::CaseSensitivity::CaseInsensitive } + , } #[inline] @@ -1698,7 +1696,7 @@ VarRef: ast::AstNode = { qualifier: ast::ScopeQualifier::Unqualified },lo..hi), => state.node(ast::VarRef { - name: ast::SymbolPrimitive { value: ident.to_owned(), case: ast::CaseSensitivity::CaseInsensitive }, + name: ident, qualifier: ast::ScopeQualifier::Unqualified },lo..hi), } @@ -1968,10 +1966,7 @@ Identifier: ast::SymbolPrimitive = { value: ident.to_owned(), case: ast::CaseSensitivity::CaseSensitive, }, - => ast::SymbolPrimitive { - value: ident.to_owned(), - case: ast::CaseSensitivity::CaseInsensitive - }, + } AsIdent: ast::SymbolPrimitive = { @@ -1993,53 +1988,59 @@ ByIdent: ast::SymbolPrimitive = { // ------------------------------------------------------------------------------ // #[inline] -VarNonReservedKeyword: &'static str = { - +VarNonReservedKeyword: ast::SymbolPrimitive = { + => ast::SymbolPrimitive { + value: ident.to_owned(), + case: ast::CaseSensitivity::CaseInsensitive + }, } #[inline] -FnNonReservedKeyword: &'static str = { - +FnNonReservedKeyword: ast::SymbolPrimitive = { + => ast::SymbolPrimitive { + value: ident.to_owned(), + case: ast::CaseSensitivity::CaseInsensitive + }, } -NonReservedKeyword: &'static str = { - "ANY" => "ANY", - "SIMPLE" => "SIMPLE", +NonReservedKeyword: &'input str = { + "ANY", + "SIMPLE", } //C.f. SQL '23, section 16, 5.2 #[inline] -GraphNonReservedKeyword: &'static str = { - "ACYCLIC" => "ACYCLIC", - "BINDINGS" => "BINDINGS", - "BOUND" => "BOUND", - "DESTINATION" => "DESTINATION", - "DIFFERENT" => "DIFFERENT", - "DIRECTED" => "DIRECTED", - "EDGE" => "EDGE", - "EDGES" => "EDGES", - "ELEMENTS" => "ELEMENTS", - "LABEL" => "LABEL", - "LABELED" => "LABELED", - "NODE" => "NODE", - "PATHS" => "PATHS", - "PROPERTIES" => "PROPERTIES", - "PROPERTY" => "PROPERTY", - "PROPERTY_GRAPH_CATALOG" => "PROPERTY_GRAPH_CATALOG", - "PROPERTY_GRAPH_NAME" => "PROPERTY_GRAPH_NAME", - "PROPERTY_GRAPH_SCHEMA" => "PROPERTY_GRAPH_SCHEMA", - "RELATIONSHIP" => "RELATIONSHIP", - "RELATIONSHIPS" => "RELATIONSHIPS", - "SHORTEST" => "SHORTEST", - "SINGLETONS" => "SINGLETONS", - "STEP" => "STEP", - "TABLES" => "TABLES", - "TRAIL" => "TRAIL", - "VERTEX" => "VERTEX", - "WALK" => "WALK", +GraphNonReservedKeyword: &'input str = { + "ACYCLIC", + "BINDINGS", + "BOUND", + "DESTINATION", + "DIFFERENT", + "DIRECTED", + "EDGE", + "EDGES", + "ELEMENTS", + "LABEL", + "LABELED", + "NODE", + "PATHS", + "PROPERTIES", + "PROPERTY", + "PROPERTY_GRAPH_CATALOG", + "PROPERTY_GRAPH_NAME", + "PROPERTY_GRAPH_SCHEMA", + "RELATIONSHIP", + "RELATIONSHIPS", + "SHORTEST", + "SINGLETONS", + "STEP", + "TABLES", + "TRAIL", + "VERTEX", + "WALK", } @@ -2145,11 +2146,10 @@ extern { "EmbeddedDoc" => lexer::Token::EmbeddedDoc(<&'input str>), // Keywords - "ACYCLIC" => lexer::Token::Acyclic, "ALL" => lexer::Token::All, "ASC" => lexer::Token::Asc, "AND" => lexer::Token::And, - "ANY" => lexer::Token::Any, + "ANY" => lexer::Token::Any(<&'input str>), "AS" => lexer::Token::As, "AT" => lexer::Token::At, "BETWEEN" => lexer::Token::Between, @@ -2214,10 +2214,8 @@ extern { "TABLE" => lexer::Token::Table, "TIME" => lexer::Token::Time, "TIMESTAMP" => lexer::Token::Timestamp, - "SIMPLE" => lexer::Token::Simple, - "SHORTEST" => lexer::Token::Shortest, + "SIMPLE" => lexer::Token::Simple(<&'input str>), "THEN" => lexer::Token::Then, - "TRAIL" => lexer::Token::Trail, "TRUE" => lexer::Token::True, "UNION" => lexer::Token::Union, "UNPIVOT" => lexer::Token::Unpivot, @@ -2244,32 +2242,32 @@ extern { "SAME" => lexer::Token::Same, // Graph Keywords; non-reserved - "ACYCLIC" => lexer::Token::Acyclic, - "BINDINGS" => lexer::Token::Bindings, - "BOUND" => lexer::Token::Bound, - "DESTINATION" => lexer::Token::Destination, - "DIFFERENT" => lexer::Token::Different, - "DIRECTED" => lexer::Token::Directed, - "EDGE" => lexer::Token::Edge, - "EDGES" => lexer::Token::Edges, - "ELEMENTS" => lexer::Token::Elements, - "LABEL" => lexer::Token::Label, - "LABELED" => lexer::Token::Labeled, - "NODE" => lexer::Token::Node, - "PATHS" => lexer::Token::Paths, - "PROPERTIES" => lexer::Token::Properties, - "PROPERTY" => lexer::Token::Property, - "PROPERTY_GRAPH_CATALOG" => lexer::Token::PropertyGraphCatalog, - "PROPERTY_GRAPH_NAME" => lexer::Token::PropertyGraphName, - "PROPERTY_GRAPH_SCHEMA" => lexer::Token::PropertyGraphSchema, - "RELATIONSHIP" => lexer::Token::Relationship, - "RELATIONSHIPS" => lexer::Token::Relationships, - "SHORTEST" => lexer::Token::Shortest, - "SINGLETONS" => lexer::Token::Singletons, - "STEP" => lexer::Token::Step, - "TABLES" => lexer::Token::Tables, - "TRAIL" => lexer::Token::Trail, - "VERTEX" => lexer::Token::Vertex, - "WALK" => lexer::Token::Walk, + "ACYCLIC" => lexer::Token::Acyclic(<&'input str>), + "BINDINGS" => lexer::Token::Bindings(<&'input str>), + "BOUND" => lexer::Token::Bound(<&'input str>), + "DESTINATION" => lexer::Token::Destination(<&'input str>), + "DIFFERENT" => lexer::Token::Different(<&'input str>), + "DIRECTED" => lexer::Token::Directed(<&'input str>), + "EDGE" => lexer::Token::Edge(<&'input str>), + "EDGES" => lexer::Token::Edges(<&'input str>), + "ELEMENTS" => lexer::Token::Elements(<&'input str>), + "LABEL" => lexer::Token::Label(<&'input str>), + "LABELED" => lexer::Token::Labeled(<&'input str>), + "NODE" => lexer::Token::Node(<&'input str>), + "PATHS" => lexer::Token::Paths(<&'input str>), + "PROPERTIES" => lexer::Token::Properties(<&'input str>), + "PROPERTY" => lexer::Token::Property(<&'input str>), + "PROPERTY_GRAPH_CATALOG" => lexer::Token::PropertyGraphCatalog(<&'input str>), + "PROPERTY_GRAPH_NAME" => lexer::Token::PropertyGraphName(<&'input str>), + "PROPERTY_GRAPH_SCHEMA" => lexer::Token::PropertyGraphSchema(<&'input str>), + "RELATIONSHIP" => lexer::Token::Relationship(<&'input str>), + "RELATIONSHIPS" => lexer::Token::Relationships(<&'input str>), + "SHORTEST" => lexer::Token::Shortest(<&'input str>), + "SINGLETONS" => lexer::Token::Singletons(<&'input str>), + "STEP" => lexer::Token::Step(<&'input str>), + "TABLES" => lexer::Token::Tables(<&'input str>), + "TRAIL" => lexer::Token::Trail(<&'input str>), + "VERTEX" => lexer::Token::Vertex(<&'input str>), + "WALK" => lexer::Token::Walk(<&'input str>), } } diff --git a/partiql/tests/pretty.rs b/partiql/tests/pretty.rs index 2f53820c..5523a499 100644 --- a/partiql/tests/pretty.rs +++ b/partiql/tests/pretty.rs @@ -327,21 +327,20 @@ mod graph { parse_test!("edge", $q) }}; } - - parse!(r#"SELECT a,b FROM g MATCH (a:A) -[e:E]-> (b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A) -> (b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A) ~[e:E]~ (b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A) ~ (b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A) <-[e:E]- (b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A) <- (b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A) ~[e:E]~> (b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A) ~> (b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A) <~[e:E]~ (b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A) <~ (b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A) <-[e:E]-> (b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A) <-> (b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A) -[e:E]- (b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A) - (b:B)"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) -[e:E]-> (b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) -> (b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) ~[e:E]~ (b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) ~ (b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) <-[e:E]- (b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) <- (b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) ~[e:E]~> (b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) ~> (b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) <~[e:E]~ (b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) <~ (b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) <-[e:E]-> (b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) <-> (b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) -[e:E]- (b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) - (b:B))"#); } #[test] fn quantifiers() { @@ -350,14 +349,14 @@ mod graph { parse_test!("quantifiers", $q) }}; } - parse!(r#"SELECT a,b FROM g MATCH (a:A)-[:edge]->*(b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A)<-[:edge]-+(b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A)~[:edge]~{5,}(b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A)-[e:edge]-{2,6}(b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A)->*(b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A)<-+(b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A)~{5,}(b:B)"#); - parse!(r#"SELECT a,b FROM g MATCH (a:A)-{2,6}(b:B)"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A)-[:edge]->*(b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A)<-[:edge]-+(b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A)~[:edge]~{5,}(b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A)-[e:edge]-{2,6}(b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A)->*(b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A)<-+(b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A)~{5,}(b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A)-{2,6}(b:B))"#); } #[test] fn patterns() { @@ -367,10 +366,10 @@ mod graph { }}; } parse!( - r#"SELECT the_a.name AS src, the_b.name AS dest FROM my_graph MATCH (the_a:a) -[the_y:y]-> (the_b:b) WHERE the_y.score > 10"# + r#"SELECT the_a.name AS src, the_b.name AS dest FROM (my_graph MATCH (the_a:a) -[the_y:y]-> (the_b:b) WHERE the_y.score > 10)"# ); - parse!(r#"SELECT a,b FROM g MATCH (a)-[:has]->()-[:contains]->(b)"#); - parse!(r#"SELECT a,b FROM (g MATCH (a) -[:has]-> (x), (x)-[:contains]->(b))"#); + parse!(r#""SELECT a,b FROM (g MATCH (a)-[:has]->()-[:contains]->(b))""#); + parse!(r#"SELECT a,b FROM GRAPH_TABLE (g MATCH (a) -[:has]-> (x), (x)-[:contains]->(b))"#); } #[test] fn path_var() { @@ -379,8 +378,7 @@ mod graph { parse_test!("path_var", $q) }}; } - - parse!(r#"SELECT a,b FROM (g MATCH p = (a:A) -[e:E]-> (b:B))"#); + parse!(r#"SELECT a,b FROM GRAPH_TABLE (g MATCH p = (a:A) -[e:E]-> (b:B))"#); } #[test] fn parenthesized() { @@ -389,14 +387,13 @@ mod graph { parse_test!("parenthesized", $q) }}; } - - parse!(r#"SELECT a,b FROM (g MATCH [(a:A)-[e:Edge]->(b:A) WHERE a.owner=b.owner]{2,5})"#); - parse!(r#"SELECT a,b FROM (g MATCH pathVar = (a:A)[()-[e:Edge]->()]{1,3}(b:B))"#); - - // brackets - parse!(r#"SELECT a,b FROM (g MATCH pathVar = (a:A)[-[e:Edge]->]*(b:B))"#); - // parens - parse!(r#"SELECT a,b FROM (g MATCH pathVar = (a:A)(-[e:Edge]->)*(b:B))"#); + parse!( + r#"SELECT a,b FROM GRAPH_TABLE (g MATCH ((a:A)-[e:Edge]->(b:A) WHERE a.owner=b.owner){2,5})"# + ); + parse!( + r#"SELECT a,b FROM GRAPH_TABLE (g MATCH pathVar = (a:A)(()-[e:Edge]->()){1,3}(b:B))"# + ); + parse!(r#"SELECT a,b FROM GRAPH_TABLE (g MATCH pathVar = (a:A)(-[e:Edge]->)*(b:B))"#); } #[test] fn filters() { @@ -405,54 +402,69 @@ mod graph { parse_test!("filters", $q) }}; } - parse!( - r#"SELECT u as banCandidate FROM g MATCH (p:Post Where p.isFlagged = true) <-[:createdPost]- (u:User WHERE u.isBanned = false AND u.karma < 20) -[:createdComment]->(c:Comment WHERE c.isFlagged = true) WHERE p.title LIKE '%considered harmful%'"# + r#"SELECT u as banCandidate + FROM (g MATCH + (p:Post Where p.isFlagged = true) + <-[:createdPost]- + (u:User WHERE u.isBanned = false AND u.karma < 20) + -[:createdComment]-> + (c:Comment WHERE c.isFlagged = true) + WHERE p.title LIKE '%considered harmful%')"# + ); + parse!( + r#"SELECT u as banCandidate + FROM (g MATCH + (p:Post Where p.isFlagged = true) + <-[:createdPost]- + (u:User WHERE u.isBanned = false AND u.karma < 20) + -[:createdComment]-> + (c:Comment WHERE c.isFlagged = true) + ) + WHERE p.title LIKE '%considered harmful%'"# ); } #[test] - fn restrictors() { + fn path_mode() { macro_rules! parse { ($q:expr) => {{ - parse_test!("restrictors", $q) + parse_test!("path_mode", $q) }}; } - parse!( - r#"SELECT p FROM g MATCH TRAIL p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"# + r#"SELECT p FROM (g MATCH p = TRAIL (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"# ); parse!( - r#"SELECT p FROM g MATCH SIMPLE p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"# + r#"SELECT p FROM (g MATCH p = SIMPLE (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"# ); parse!( - r#"SELECT p FROM g MATCH ACYCLIC p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"# + r#"SELECT p FROM (g MATCH p = ACYCLIC (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"# ); } #[test] - fn selectors() { + fn search_prefix() { macro_rules! parse { ($q:expr) => {{ - parse_test!("selectors", $q) + parse_test!("search_prefix", $q) }}; } - parse!( - r#"SELECT p FROM g MATCH ANY SHORTEST p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"# + r#"SELECT p FROM (g MATCH p = ANY SHORTEST (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"# ); parse!( - r#"SELECT p FROM g MATCH ALL SHORTEST p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"# + r#"SELECT p FROM (g MATCH p = ALL SHORTEST (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"# ); parse!( - r#"SELECT p FROM g MATCH ANY p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"# + r#"SELECT p FROM (g MATCH p = ANY (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"# ); parse!( - r#"SELECT p FROM g MATCH ANY 5 p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"# + r#"SELECT p FROM (g MATCH p = ANY 5 (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"# ); parse!( - r#"SELECT p FROM g MATCH SHORTEST 5 p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"# + r#"SELECT p FROM (g MATCH p = SHORTEST 5 (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"# ); parse!( - r#"SELECT p FROM g MATCH SHORTEST 5 GROUP p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')"# + r#"SELECT p FROM (g MATCH p = SHORTEST 5 GROUP (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"# ); } #[test] @@ -462,9 +474,11 @@ mod graph { parse_test!("match_and_join", $q) }}; } - parse!( - r#"SELECT a,b,c, t1.x as x, t2.y as y FROM (graph MATCH (a) -> (b), (a) -> (c)), table1 as t1, table2 as t2"# + r#"SELECT a,b,c, t1.x as x, t2.y as y FROM GRAPH_TABLE (g MATCH (a) -> (b), (a) -> (c)), table1 as t1, table2 as t2"# + ); + parse!( + r#"SELECT a,b,c, t1.x as x, t2.y as y FROM table1 as t1, table2 as t2, GRAPH_TABLE (g MATCH (a) -> (b), (a) -> (c))"# ); } #[test] @@ -474,7 +488,6 @@ mod graph { parse_test!("union", $q) }}; } - parse!(r#"(MyGraph MATCH (x)) UNION SELECT * FROM tbl1"#); parse!(r#"SELECT * FROM tbl1 UNION (MyGraph MATCH (x))"#); } @@ -485,9 +498,8 @@ mod graph { parse_test!("etc", $q) }}; } - - parse!("SELECT * FROM g MATCH ALL SHORTEST [ (x)-[e]->*(y) ]"); - parse!("SELECT * FROM g MATCH ALL SHORTEST [ TRAIL (x)-[e]->*(y) ]"); + parse!("SELECT * FROM (g MATCH ALL SHORTEST ( (x)-[e]->*(y) ))"); + parse!("SELECT * FROM (g MATCH ALL SHORTEST ( TRAIL (x)-[e]->*(y) ))"); } } diff --git a/partiql/tests/snapshots/pretty__graph_edge_1.snap b/partiql/tests/snapshots/pretty__graph_edge_1.snap index 639eca91..189244e9 100644 --- a/partiql/tests/snapshots/pretty__graph_edge_1.snap +++ b/partiql/tests/snapshots/pretty__graph_edge_1.snap @@ -3,31 +3,31 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b FROM g MATCH (a:A) -[e:E]-> (b:B) +SELECT a,b FROM (g MATCH (a:A) -[e:E]-> (b:B)) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) -[e:E]-> (b:B) +SELECT a, b FROM (g MATCH (a:A) -[e:E]-> (b:B)) ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) -[e:E]-> (b:B) +SELECT a, b FROM (g MATCH (a:A) -[e:E]-> (b:B)) -------------------------------------------------------------------------------- -SELECT a, b FROM g MATCH (a:A) -[e:E]-> (b:B) +SELECT a, b FROM (g MATCH (a:A) -[e:E]-> (b:B)) ---------------------------------------- SELECT a, b -FROM g MATCH (a:A) -[e:E]-> (b:B) +FROM (g MATCH (a:A) -[e:E]-> (b:B)) ------------------------------ SELECT a, b -FROM g MATCH (a:A) -[e:E]-> (b:B) +FROM (g MATCH (a:A) -[e:E]-> (b:B)) -------------------- SELECT a, b -FROM g MATCH (a:A) -[e:E]-> (b:B) +FROM (g MATCH (a:A) -[e:E]-> (b:B)) ---------- SELECT a, b -FROM g MATCH (a:A) -[e:E]-> (b:B) +FROM (g MATCH (a:A) -[e:E]-> (b:B)) diff --git a/partiql/tests/snapshots/pretty__graph_edge_10.snap b/partiql/tests/snapshots/pretty__graph_edge_10.snap index 256f8fb9..649a21f8 100644 --- a/partiql/tests/snapshots/pretty__graph_edge_10.snap +++ b/partiql/tests/snapshots/pretty__graph_edge_10.snap @@ -3,30 +3,31 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b FROM g MATCH (a:A) <~ (b:B) +SELECT a,b FROM (g MATCH (a:A) <~ (b:B)) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) <~ (b:B) +SELECT a, b FROM (g MATCH (a:A) <~ (b:B)) ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) <~ (b:B) +SELECT a, b FROM (g MATCH (a:A) <~ (b:B)) -------------------------------------------------------------------------------- -SELECT a, b FROM g MATCH (a:A) <~ (b:B) +SELECT a, b FROM (g MATCH (a:A) <~ (b:B)) ---------------------------------------- -SELECT a, b FROM g MATCH (a:A) <~ (b:B) +SELECT a, b +FROM (g MATCH (a:A) <~ (b:B)) ------------------------------ SELECT a, b -FROM g MATCH (a:A) <~ (b:B) +FROM (g MATCH (a:A) <~ (b:B)) -------------------- SELECT a, b -FROM g MATCH (a:A) <~ (b:B) +FROM (g MATCH (a:A) <~ (b:B)) ---------- SELECT a, b -FROM g MATCH (a:A) <~ (b:B) +FROM (g MATCH (a:A) <~ (b:B)) diff --git a/partiql/tests/snapshots/pretty__graph_edge_11.snap b/partiql/tests/snapshots/pretty__graph_edge_11.snap index 9593d28a..583b7152 100644 --- a/partiql/tests/snapshots/pretty__graph_edge_11.snap +++ b/partiql/tests/snapshots/pretty__graph_edge_11.snap @@ -3,31 +3,31 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b FROM g MATCH (a:A) <-[e:E]-> (b:B) +SELECT a,b FROM (g MATCH (a:A) <-[e:E]-> (b:B)) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) <-[e:E]-> (b:B) +SELECT a, b FROM (g MATCH (a:A) <-[e:E]-> (b:B)) ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) <-[e:E]-> (b:B) +SELECT a, b FROM (g MATCH (a:A) <-[e:E]-> (b:B)) -------------------------------------------------------------------------------- -SELECT a, b FROM g MATCH (a:A) <-[e:E]-> (b:B) +SELECT a, b FROM (g MATCH (a:A) <-[e:E]-> (b:B)) ---------------------------------------- SELECT a, b -FROM g MATCH (a:A) <-[e:E]-> (b:B) +FROM (g MATCH (a:A) <-[e:E]-> (b:B)) ------------------------------ SELECT a, b -FROM g MATCH (a:A) <-[e:E]-> (b:B) +FROM (g MATCH (a:A) <-[e:E]-> (b:B)) -------------------- SELECT a, b -FROM g MATCH (a:A) <-[e:E]-> (b:B) +FROM (g MATCH (a:A) <-[e:E]-> (b:B)) ---------- SELECT a, b -FROM g MATCH (a:A) <-[e:E]-> (b:B) +FROM (g MATCH (a:A) <-[e:E]-> (b:B)) diff --git a/partiql/tests/snapshots/pretty__graph_edge_12.snap b/partiql/tests/snapshots/pretty__graph_edge_12.snap index cb897a11..ded1371a 100644 --- a/partiql/tests/snapshots/pretty__graph_edge_12.snap +++ b/partiql/tests/snapshots/pretty__graph_edge_12.snap @@ -3,30 +3,31 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b FROM g MATCH (a:A) <-> (b:B) +SELECT a,b FROM (g MATCH (a:A) <-> (b:B)) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) <-> (b:B) +SELECT a, b FROM (g MATCH (a:A) <-> (b:B)) ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) <-> (b:B) +SELECT a, b FROM (g MATCH (a:A) <-> (b:B)) -------------------------------------------------------------------------------- -SELECT a, b FROM g MATCH (a:A) <-> (b:B) +SELECT a, b FROM (g MATCH (a:A) <-> (b:B)) ---------------------------------------- -SELECT a, b FROM g MATCH (a:A) <-> (b:B) +SELECT a, b +FROM (g MATCH (a:A) <-> (b:B)) ------------------------------ SELECT a, b -FROM g MATCH (a:A) <-> (b:B) +FROM (g MATCH (a:A) <-> (b:B)) -------------------- SELECT a, b -FROM g MATCH (a:A) <-> (b:B) +FROM (g MATCH (a:A) <-> (b:B)) ---------- SELECT a, b -FROM g MATCH (a:A) <-> (b:B) +FROM (g MATCH (a:A) <-> (b:B)) diff --git a/partiql/tests/snapshots/pretty__graph_edge_13.snap b/partiql/tests/snapshots/pretty__graph_edge_13.snap index a4256ceb..c4db580f 100644 --- a/partiql/tests/snapshots/pretty__graph_edge_13.snap +++ b/partiql/tests/snapshots/pretty__graph_edge_13.snap @@ -3,31 +3,31 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b FROM g MATCH (a:A) -[e:E]- (b:B) +SELECT a,b FROM (g MATCH (a:A) -[e:E]- (b:B)) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) -[e:E]- (b:B) +SELECT a, b FROM (g MATCH (a:A) -[e:E]- (b:B)) ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) -[e:E]- (b:B) +SELECT a, b FROM (g MATCH (a:A) -[e:E]- (b:B)) -------------------------------------------------------------------------------- -SELECT a, b FROM g MATCH (a:A) -[e:E]- (b:B) +SELECT a, b FROM (g MATCH (a:A) -[e:E]- (b:B)) ---------------------------------------- SELECT a, b -FROM g MATCH (a:A) -[e:E]- (b:B) +FROM (g MATCH (a:A) -[e:E]- (b:B)) ------------------------------ SELECT a, b -FROM g MATCH (a:A) -[e:E]- (b:B) +FROM (g MATCH (a:A) -[e:E]- (b:B)) -------------------- SELECT a, b -FROM g MATCH (a:A) -[e:E]- (b:B) +FROM (g MATCH (a:A) -[e:E]- (b:B)) ---------- SELECT a, b -FROM g MATCH (a:A) -[e:E]- (b:B) +FROM (g MATCH (a:A) -[e:E]- (b:B)) diff --git a/partiql/tests/snapshots/pretty__graph_edge_14.snap b/partiql/tests/snapshots/pretty__graph_edge_14.snap index 0320ba41..1e8dc105 100644 --- a/partiql/tests/snapshots/pretty__graph_edge_14.snap +++ b/partiql/tests/snapshots/pretty__graph_edge_14.snap @@ -3,30 +3,30 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b FROM g MATCH (a:A) - (b:B) +SELECT a,b FROM (g MATCH (a:A) - (b:B)) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) - (b:B) +SELECT a, b FROM (g MATCH (a:A) - (b:B)) ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) - (b:B) +SELECT a, b FROM (g MATCH (a:A) - (b:B)) -------------------------------------------------------------------------------- -SELECT a, b FROM g MATCH (a:A) - (b:B) +SELECT a, b FROM (g MATCH (a:A) - (b:B)) ---------------------------------------- -SELECT a, b FROM g MATCH (a:A) - (b:B) +SELECT a, b FROM (g MATCH (a:A) - (b:B)) ------------------------------ SELECT a, b -FROM g MATCH (a:A) - (b:B) +FROM (g MATCH (a:A) - (b:B)) -------------------- SELECT a, b -FROM g MATCH (a:A) - (b:B) +FROM (g MATCH (a:A) - (b:B)) ---------- SELECT a, b -FROM g MATCH (a:A) - (b:B) +FROM (g MATCH (a:A) - (b:B)) diff --git a/partiql/tests/snapshots/pretty__graph_edge_2.snap b/partiql/tests/snapshots/pretty__graph_edge_2.snap index 0583a7cf..4e8e8674 100644 --- a/partiql/tests/snapshots/pretty__graph_edge_2.snap +++ b/partiql/tests/snapshots/pretty__graph_edge_2.snap @@ -3,30 +3,31 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b FROM g MATCH (a:A) -> (b:B) +SELECT a,b FROM (g MATCH (a:A) -> (b:B)) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) -> (b:B) +SELECT a, b FROM (g MATCH (a:A) -> (b:B)) ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) -> (b:B) +SELECT a, b FROM (g MATCH (a:A) -> (b:B)) -------------------------------------------------------------------------------- -SELECT a, b FROM g MATCH (a:A) -> (b:B) +SELECT a, b FROM (g MATCH (a:A) -> (b:B)) ---------------------------------------- -SELECT a, b FROM g MATCH (a:A) -> (b:B) +SELECT a, b +FROM (g MATCH (a:A) -> (b:B)) ------------------------------ SELECT a, b -FROM g MATCH (a:A) -> (b:B) +FROM (g MATCH (a:A) -> (b:B)) -------------------- SELECT a, b -FROM g MATCH (a:A) -> (b:B) +FROM (g MATCH (a:A) -> (b:B)) ---------- SELECT a, b -FROM g MATCH (a:A) -> (b:B) +FROM (g MATCH (a:A) -> (b:B)) diff --git a/partiql/tests/snapshots/pretty__graph_edge_3.snap b/partiql/tests/snapshots/pretty__graph_edge_3.snap index a834241c..217e2306 100644 --- a/partiql/tests/snapshots/pretty__graph_edge_3.snap +++ b/partiql/tests/snapshots/pretty__graph_edge_3.snap @@ -3,31 +3,31 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b FROM g MATCH (a:A) ~[e:E]~ (b:B) +SELECT a,b FROM (g MATCH (a:A) ~[e:E]~ (b:B)) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) ~[e:E]~ (b:B) +SELECT a, b FROM (g MATCH (a:A) ~[e:E]~ (b:B)) ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) ~[e:E]~ (b:B) +SELECT a, b FROM (g MATCH (a:A) ~[e:E]~ (b:B)) -------------------------------------------------------------------------------- -SELECT a, b FROM g MATCH (a:A) ~[e:E]~ (b:B) +SELECT a, b FROM (g MATCH (a:A) ~[e:E]~ (b:B)) ---------------------------------------- SELECT a, b -FROM g MATCH (a:A) ~[e:E]~ (b:B) +FROM (g MATCH (a:A) ~[e:E]~ (b:B)) ------------------------------ SELECT a, b -FROM g MATCH (a:A) ~[e:E]~ (b:B) +FROM (g MATCH (a:A) ~[e:E]~ (b:B)) -------------------- SELECT a, b -FROM g MATCH (a:A) ~[e:E]~ (b:B) +FROM (g MATCH (a:A) ~[e:E]~ (b:B)) ---------- SELECT a, b -FROM g MATCH (a:A) ~[e:E]~ (b:B) +FROM (g MATCH (a:A) ~[e:E]~ (b:B)) diff --git a/partiql/tests/snapshots/pretty__graph_edge_4.snap b/partiql/tests/snapshots/pretty__graph_edge_4.snap index 8633bf06..91a0a2b5 100644 --- a/partiql/tests/snapshots/pretty__graph_edge_4.snap +++ b/partiql/tests/snapshots/pretty__graph_edge_4.snap @@ -3,30 +3,30 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b FROM g MATCH (a:A) ~ (b:B) +SELECT a,b FROM (g MATCH (a:A) ~ (b:B)) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) ~ (b:B) +SELECT a, b FROM (g MATCH (a:A) ~ (b:B)) ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) ~ (b:B) +SELECT a, b FROM (g MATCH (a:A) ~ (b:B)) -------------------------------------------------------------------------------- -SELECT a, b FROM g MATCH (a:A) ~ (b:B) +SELECT a, b FROM (g MATCH (a:A) ~ (b:B)) ---------------------------------------- -SELECT a, b FROM g MATCH (a:A) ~ (b:B) +SELECT a, b FROM (g MATCH (a:A) ~ (b:B)) ------------------------------ SELECT a, b -FROM g MATCH (a:A) ~ (b:B) +FROM (g MATCH (a:A) ~ (b:B)) -------------------- SELECT a, b -FROM g MATCH (a:A) ~ (b:B) +FROM (g MATCH (a:A) ~ (b:B)) ---------- SELECT a, b -FROM g MATCH (a:A) ~ (b:B) +FROM (g MATCH (a:A) ~ (b:B)) diff --git a/partiql/tests/snapshots/pretty__graph_edge_5.snap b/partiql/tests/snapshots/pretty__graph_edge_5.snap index a22ebbaf..f51dc8bd 100644 --- a/partiql/tests/snapshots/pretty__graph_edge_5.snap +++ b/partiql/tests/snapshots/pretty__graph_edge_5.snap @@ -3,31 +3,31 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b FROM g MATCH (a:A) <-[e:E]- (b:B) +SELECT a,b FROM (g MATCH (a:A) <-[e:E]- (b:B)) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) <-[e:E]- (b:B) +SELECT a, b FROM (g MATCH (a:A) <-[e:E]- (b:B)) ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) <-[e:E]- (b:B) +SELECT a, b FROM (g MATCH (a:A) <-[e:E]- (b:B)) -------------------------------------------------------------------------------- -SELECT a, b FROM g MATCH (a:A) <-[e:E]- (b:B) +SELECT a, b FROM (g MATCH (a:A) <-[e:E]- (b:B)) ---------------------------------------- SELECT a, b -FROM g MATCH (a:A) <-[e:E]- (b:B) +FROM (g MATCH (a:A) <-[e:E]- (b:B)) ------------------------------ SELECT a, b -FROM g MATCH (a:A) <-[e:E]- (b:B) +FROM (g MATCH (a:A) <-[e:E]- (b:B)) -------------------- SELECT a, b -FROM g MATCH (a:A) <-[e:E]- (b:B) +FROM (g MATCH (a:A) <-[e:E]- (b:B)) ---------- SELECT a, b -FROM g MATCH (a:A) <-[e:E]- (b:B) +FROM (g MATCH (a:A) <-[e:E]- (b:B)) diff --git a/partiql/tests/snapshots/pretty__graph_edge_6.snap b/partiql/tests/snapshots/pretty__graph_edge_6.snap index 0d745ef6..7f72f64f 100644 --- a/partiql/tests/snapshots/pretty__graph_edge_6.snap +++ b/partiql/tests/snapshots/pretty__graph_edge_6.snap @@ -3,30 +3,31 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b FROM g MATCH (a:A) <- (b:B) +SELECT a,b FROM (g MATCH (a:A) <- (b:B)) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) <- (b:B) +SELECT a, b FROM (g MATCH (a:A) <- (b:B)) ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) <- (b:B) +SELECT a, b FROM (g MATCH (a:A) <- (b:B)) -------------------------------------------------------------------------------- -SELECT a, b FROM g MATCH (a:A) <- (b:B) +SELECT a, b FROM (g MATCH (a:A) <- (b:B)) ---------------------------------------- -SELECT a, b FROM g MATCH (a:A) <- (b:B) +SELECT a, b +FROM (g MATCH (a:A) <- (b:B)) ------------------------------ SELECT a, b -FROM g MATCH (a:A) <- (b:B) +FROM (g MATCH (a:A) <- (b:B)) -------------------- SELECT a, b -FROM g MATCH (a:A) <- (b:B) +FROM (g MATCH (a:A) <- (b:B)) ---------- SELECT a, b -FROM g MATCH (a:A) <- (b:B) +FROM (g MATCH (a:A) <- (b:B)) diff --git a/partiql/tests/snapshots/pretty__graph_edge_7.snap b/partiql/tests/snapshots/pretty__graph_edge_7.snap index 6c8fa391..390b245e 100644 --- a/partiql/tests/snapshots/pretty__graph_edge_7.snap +++ b/partiql/tests/snapshots/pretty__graph_edge_7.snap @@ -3,31 +3,31 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b FROM g MATCH (a:A) ~[e:E]~> (b:B) +SELECT a,b FROM (g MATCH (a:A) ~[e:E]~> (b:B)) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) ~[e:E]~> (b:B) +SELECT a, b FROM (g MATCH (a:A) ~[e:E]~> (b:B)) ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) ~[e:E]~> (b:B) +SELECT a, b FROM (g MATCH (a:A) ~[e:E]~> (b:B)) -------------------------------------------------------------------------------- -SELECT a, b FROM g MATCH (a:A) ~[e:E]~> (b:B) +SELECT a, b FROM (g MATCH (a:A) ~[e:E]~> (b:B)) ---------------------------------------- SELECT a, b -FROM g MATCH (a:A) ~[e:E]~> (b:B) +FROM (g MATCH (a:A) ~[e:E]~> (b:B)) ------------------------------ SELECT a, b -FROM g MATCH (a:A) ~[e:E]~> (b:B) +FROM (g MATCH (a:A) ~[e:E]~> (b:B)) -------------------- SELECT a, b -FROM g MATCH (a:A) ~[e:E]~> (b:B) +FROM (g MATCH (a:A) ~[e:E]~> (b:B)) ---------- SELECT a, b -FROM g MATCH (a:A) ~[e:E]~> (b:B) +FROM (g MATCH (a:A) ~[e:E]~> (b:B)) diff --git a/partiql/tests/snapshots/pretty__graph_edge_8.snap b/partiql/tests/snapshots/pretty__graph_edge_8.snap index e078818c..097a860b 100644 --- a/partiql/tests/snapshots/pretty__graph_edge_8.snap +++ b/partiql/tests/snapshots/pretty__graph_edge_8.snap @@ -3,30 +3,31 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b FROM g MATCH (a:A) ~> (b:B) +SELECT a,b FROM (g MATCH (a:A) ~> (b:B)) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) ~> (b:B) +SELECT a, b FROM (g MATCH (a:A) ~> (b:B)) ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) ~> (b:B) +SELECT a, b FROM (g MATCH (a:A) ~> (b:B)) -------------------------------------------------------------------------------- -SELECT a, b FROM g MATCH (a:A) ~> (b:B) +SELECT a, b FROM (g MATCH (a:A) ~> (b:B)) ---------------------------------------- -SELECT a, b FROM g MATCH (a:A) ~> (b:B) +SELECT a, b +FROM (g MATCH (a:A) ~> (b:B)) ------------------------------ SELECT a, b -FROM g MATCH (a:A) ~> (b:B) +FROM (g MATCH (a:A) ~> (b:B)) -------------------- SELECT a, b -FROM g MATCH (a:A) ~> (b:B) +FROM (g MATCH (a:A) ~> (b:B)) ---------- SELECT a, b -FROM g MATCH (a:A) ~> (b:B) +FROM (g MATCH (a:A) ~> (b:B)) diff --git a/partiql/tests/snapshots/pretty__graph_edge_9.snap b/partiql/tests/snapshots/pretty__graph_edge_9.snap index 5daabb45..6148ffd8 100644 --- a/partiql/tests/snapshots/pretty__graph_edge_9.snap +++ b/partiql/tests/snapshots/pretty__graph_edge_9.snap @@ -3,31 +3,31 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b FROM g MATCH (a:A) <~[e:E]~ (b:B) +SELECT a,b FROM (g MATCH (a:A) <~[e:E]~ (b:B)) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) <~[e:E]~ (b:B) +SELECT a, b FROM (g MATCH (a:A) <~[e:E]~ (b:B)) ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) <~[e:E]~ (b:B) +SELECT a, b FROM (g MATCH (a:A) <~[e:E]~ (b:B)) -------------------------------------------------------------------------------- -SELECT a, b FROM g MATCH (a:A) <~[e:E]~ (b:B) +SELECT a, b FROM (g MATCH (a:A) <~[e:E]~ (b:B)) ---------------------------------------- SELECT a, b -FROM g MATCH (a:A) <~[e:E]~ (b:B) +FROM (g MATCH (a:A) <~[e:E]~ (b:B)) ------------------------------ SELECT a, b -FROM g MATCH (a:A) <~[e:E]~ (b:B) +FROM (g MATCH (a:A) <~[e:E]~ (b:B)) -------------------- SELECT a, b -FROM g MATCH (a:A) <~[e:E]~ (b:B) +FROM (g MATCH (a:A) <~[e:E]~ (b:B)) ---------- SELECT a, b -FROM g MATCH (a:A) <~[e:E]~ (b:B) +FROM (g MATCH (a:A) <~[e:E]~ (b:B)) diff --git a/partiql/tests/snapshots/pretty__graph_etc_1.snap b/partiql/tests/snapshots/pretty__graph_etc_1.snap index 9decaa51..6e328b8d 100644 --- a/partiql/tests/snapshots/pretty__graph_etc_1.snap +++ b/partiql/tests/snapshots/pretty__graph_etc_1.snap @@ -3,33 +3,30 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT * FROM g MATCH ALL SHORTEST [ (x)-[e]->*(y) ] +SELECT * FROM (g MATCH ALL SHORTEST ( (x)-[e]->*(y) )) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT * FROM g MATCH ALL SHORTEST (x) -[e]->* (y) +SELECT * FROM (g MATCH ALL SHORTEST ((x) -[e]->* (y))) ------------------------------------------------------------------------------------------------------------------------ -SELECT * FROM g MATCH ALL SHORTEST (x) -[e]->* (y) +SELECT * FROM (g MATCH ALL SHORTEST ((x) -[e]->* (y))) -------------------------------------------------------------------------------- -SELECT * FROM g MATCH ALL SHORTEST (x) -[e]->* (y) +SELECT * FROM (g MATCH ALL SHORTEST ((x) -[e]->* (y))) ---------------------------------------- -SELECT * FROM g MATCH ALL SHORTEST - (x) -[e]->* (y) +SELECT * +FROM (g MATCH ALL SHORTEST ((x) -[e]->* (y))) ------------------------------ SELECT * -FROM g MATCH ALL SHORTEST - (x) -[e]->* (y) +FROM (g MATCH ALL SHORTEST ((x) -[e]->* (y))) -------------------- SELECT * -FROM g MATCH ALL SHORTEST - (x) -[e]->* (y) +FROM (g MATCH ALL SHORTEST ((x) -[e]->* (y))) ---------- SELECT * -FROM g MATCH ALL SHORTEST - (x) -[e]->* (y) +FROM (g MATCH ALL SHORTEST ((x) -[e]->* (y))) diff --git a/partiql/tests/snapshots/pretty__graph_etc_2.snap b/partiql/tests/snapshots/pretty__graph_etc_2.snap index b6bf459b..260fb03e 100644 --- a/partiql/tests/snapshots/pretty__graph_etc_2.snap +++ b/partiql/tests/snapshots/pretty__graph_etc_2.snap @@ -3,33 +3,30 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT * FROM g MATCH ALL SHORTEST [ TRAIL (x)-[e]->*(y) ] +SELECT * FROM (g MATCH ALL SHORTEST ( TRAIL (x)-[e]->*(y) )) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT * FROM g MATCH ALL SHORTEST [TRAIL (x) -[e]->* (y)] +SELECT * FROM (g MATCH ALL SHORTEST (TRAIL (x) -[e]->* (y))) ------------------------------------------------------------------------------------------------------------------------ -SELECT * FROM g MATCH ALL SHORTEST [TRAIL (x) -[e]->* (y)] +SELECT * FROM (g MATCH ALL SHORTEST (TRAIL (x) -[e]->* (y))) -------------------------------------------------------------------------------- -SELECT * FROM g MATCH ALL SHORTEST [TRAIL (x) -[e]->* (y)] +SELECT * FROM (g MATCH ALL SHORTEST (TRAIL (x) -[e]->* (y))) ---------------------------------------- -SELECT * FROM g MATCH ALL SHORTEST - [TRAIL (x) -[e]->* (y)] +SELECT * +FROM (g MATCH ALL SHORTEST (TRAIL (x) -[e]->* (y))) ------------------------------ SELECT * -FROM g MATCH ALL SHORTEST - [TRAIL (x) -[e]->* (y)] +FROM (g MATCH ALL SHORTEST (TRAIL (x) -[e]->* (y))) -------------------- SELECT * -FROM g MATCH ALL SHORTEST - [TRAIL (x) -[e]->* (y)] +FROM (g MATCH ALL SHORTEST (TRAIL (x) -[e]->* (y))) ---------- SELECT * -FROM g MATCH ALL SHORTEST - [TRAIL (x) -[e]->* (y)] +FROM (g MATCH ALL SHORTEST (TRAIL (x) -[e]->* (y))) diff --git a/partiql/tests/snapshots/pretty__graph_parenthesized_1.snap b/partiql/tests/snapshots/pretty__graph_parenthesized_1.snap index 5511722c..1af83078 100644 --- a/partiql/tests/snapshots/pretty__graph_parenthesized_1.snap +++ b/partiql/tests/snapshots/pretty__graph_parenthesized_1.snap @@ -3,32 +3,32 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b FROM (g MATCH [(a:A)-[e:Edge]->(b:A) WHERE a.owner=b.owner]{2,5}) +SELECT a,b FROM GRAPH_TABLE (g MATCH ((a:A)-[e:Edge]->(b:A) WHERE a.owner=b.owner){2,5}) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH [(a:A) -[e:Edge]-> (b:A) WHERE (a.owner = b.owner)]{2,5} +SELECT a, b FROM GRAPH_TABLE (g MATCH ((a:A) -[e:Edge]-> (b:A) WHERE (a.owner = b.owner)){2,5}) ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH [(a:A) -[e:Edge]-> (b:A) WHERE (a.owner = b.owner)]{2,5} +SELECT a, b FROM GRAPH_TABLE (g MATCH ((a:A) -[e:Edge]-> (b:A) WHERE (a.owner = b.owner)){2,5}) -------------------------------------------------------------------------------- SELECT a, b -FROM g MATCH [(a:A) -[e:Edge]-> (b:A) WHERE (a.owner = b.owner)]{2,5} +FROM GRAPH_TABLE (g MATCH ((a:A) -[e:Edge]-> (b:A) WHERE (a.owner = b.owner)){2,5}) ---------------------------------------- SELECT a, b -FROM g MATCH [(a:A) -[e:Edge]-> (b:A) WHERE (a.owner = b.owner)]{2,5} +FROM GRAPH_TABLE (g MATCH ((a:A) -[e:Edge]-> (b:A) WHERE (a.owner = b.owner)){2,5}) ------------------------------ SELECT a, b -FROM g MATCH [(a:A) -[e:Edge]-> (b:A) WHERE (a.owner = b.owner)]{2,5} +FROM GRAPH_TABLE (g MATCH ((a:A) -[e:Edge]-> (b:A) WHERE (a.owner = b.owner)){2,5}) -------------------- SELECT a, b -FROM g MATCH [(a:A) -[e:Edge]-> (b:A) WHERE (a.owner = b.owner)]{2,5} +FROM GRAPH_TABLE (g MATCH ((a:A) -[e:Edge]-> (b:A) WHERE (a.owner = b.owner)){2,5}) ---------- SELECT a, b -FROM g MATCH [(a:A) -[e:Edge]-> (b:A) WHERE (a.owner = b.owner)]{2,5} +FROM GRAPH_TABLE (g MATCH ((a:A) -[e:Edge]-> (b:A) WHERE (a.owner = b.owner)){2,5}) diff --git a/partiql/tests/snapshots/pretty__graph_parenthesized_2.snap b/partiql/tests/snapshots/pretty__graph_parenthesized_2.snap index 6b78d858..571a6ee5 100644 --- a/partiql/tests/snapshots/pretty__graph_parenthesized_2.snap +++ b/partiql/tests/snapshots/pretty__graph_parenthesized_2.snap @@ -3,31 +3,32 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b FROM (g MATCH pathVar = (a:A)[()-[e:Edge]->()]{1,3}(b:B)) +SELECT a,b FROM GRAPH_TABLE (g MATCH pathVar = (a:A)(()-[e:Edge]->()){1,3}(b:B)) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH pathVar = (a:A) [() -[e:Edge]-> ()]{1,3} (b:B) +SELECT a, b FROM GRAPH_TABLE (g MATCH pathVar = (a:A) (() -[e:Edge]-> ()){1,3} (b:B)) ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH pathVar = (a:A) [() -[e:Edge]-> ()]{1,3} (b:B) +SELECT a, b FROM GRAPH_TABLE (g MATCH pathVar = (a:A) (() -[e:Edge]-> ()){1,3} (b:B)) -------------------------------------------------------------------------------- -SELECT a, b FROM g MATCH pathVar = (a:A) [() -[e:Edge]-> ()]{1,3} (b:B) +SELECT a, b +FROM GRAPH_TABLE (g MATCH pathVar = (a:A) (() -[e:Edge]-> ()){1,3} (b:B)) ---------------------------------------- SELECT a, b -FROM g MATCH pathVar = (a:A) [() -[e:Edge]-> ()]{1,3} (b:B) +FROM GRAPH_TABLE (g MATCH pathVar = (a:A) (() -[e:Edge]-> ()){1,3} (b:B)) ------------------------------ SELECT a, b -FROM g MATCH pathVar = (a:A) [() -[e:Edge]-> ()]{1,3} (b:B) +FROM GRAPH_TABLE (g MATCH pathVar = (a:A) (() -[e:Edge]-> ()){1,3} (b:B)) -------------------- SELECT a, b -FROM g MATCH pathVar = (a:A) [() -[e:Edge]-> ()]{1,3} (b:B) +FROM GRAPH_TABLE (g MATCH pathVar = (a:A) (() -[e:Edge]-> ()){1,3} (b:B)) ---------- SELECT a, b -FROM g MATCH pathVar = (a:A) [() -[e:Edge]-> ()]{1,3} (b:B) +FROM GRAPH_TABLE (g MATCH pathVar = (a:A) (() -[e:Edge]-> ()){1,3} (b:B)) diff --git a/partiql/tests/snapshots/pretty__graph_parenthesized_3.snap b/partiql/tests/snapshots/pretty__graph_parenthesized_3.snap index 2b735026..fb9277f1 100644 --- a/partiql/tests/snapshots/pretty__graph_parenthesized_3.snap +++ b/partiql/tests/snapshots/pretty__graph_parenthesized_3.snap @@ -3,31 +3,31 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b FROM (g MATCH pathVar = (a:A)[-[e:Edge]->]*(b:B)) +SELECT a,b FROM GRAPH_TABLE (g MATCH pathVar = (a:A)(-[e:Edge]->)*(b:B)) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH pathVar = (a:A) [-[e:Edge]->]* (b:B) +SELECT a, b FROM GRAPH_TABLE (g MATCH pathVar = (a:A) (-[e:Edge]->)* (b:B)) ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH pathVar = (a:A) [-[e:Edge]->]* (b:B) +SELECT a, b FROM GRAPH_TABLE (g MATCH pathVar = (a:A) (-[e:Edge]->)* (b:B)) -------------------------------------------------------------------------------- -SELECT a, b FROM g MATCH pathVar = (a:A) [-[e:Edge]->]* (b:B) +SELECT a, b FROM GRAPH_TABLE (g MATCH pathVar = (a:A) (-[e:Edge]->)* (b:B)) ---------------------------------------- SELECT a, b -FROM g MATCH pathVar = (a:A) [-[e:Edge]->]* (b:B) +FROM GRAPH_TABLE (g MATCH pathVar = (a:A) (-[e:Edge]->)* (b:B)) ------------------------------ SELECT a, b -FROM g MATCH pathVar = (a:A) [-[e:Edge]->]* (b:B) +FROM GRAPH_TABLE (g MATCH pathVar = (a:A) (-[e:Edge]->)* (b:B)) -------------------- SELECT a, b -FROM g MATCH pathVar = (a:A) [-[e:Edge]->]* (b:B) +FROM GRAPH_TABLE (g MATCH pathVar = (a:A) (-[e:Edge]->)* (b:B)) ---------- SELECT a, b -FROM g MATCH pathVar = (a:A) [-[e:Edge]->]* (b:B) +FROM GRAPH_TABLE (g MATCH pathVar = (a:A) (-[e:Edge]->)* (b:B)) diff --git a/partiql/tests/snapshots/pretty__graph_restrictors_2.snap b/partiql/tests/snapshots/pretty__graph_path_mode_1.snap similarity index 54% rename from partiql/tests/snapshots/pretty__graph_restrictors_2.snap rename to partiql/tests/snapshots/pretty__graph_path_mode_1.snap index 4d5757db..1372fe65 100644 --- a/partiql/tests/snapshots/pretty__graph_restrictors_2.snap +++ b/partiql/tests/snapshots/pretty__graph_path_mode_1.snap @@ -3,31 +3,31 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT p FROM g MATCH SIMPLE p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha') +SELECT p FROM (g MATCH p = TRAIL (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT p FROM g MATCH [SIMPLE p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))] +SELECT p FROM (g MATCH p = TRAIL (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) ------------------------------------------------------------------------------------------------------------------------ -SELECT p FROM g MATCH [SIMPLE p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))] +SELECT p FROM (g MATCH p = TRAIL (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) -------------------------------------------------------------------------------- SELECT p -FROM g MATCH [SIMPLE p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))] +FROM (g MATCH p = TRAIL (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) ---------------------------------------- SELECT p -FROM g MATCH [SIMPLE p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))] +FROM (g MATCH p = TRAIL (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) ------------------------------ SELECT p -FROM g MATCH [SIMPLE p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))] +FROM (g MATCH p = TRAIL (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) -------------------- SELECT p -FROM g MATCH [SIMPLE p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))] +FROM (g MATCH p = TRAIL (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) ---------- SELECT p -FROM g MATCH [SIMPLE p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))] +FROM (g MATCH p = TRAIL (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) diff --git a/partiql/tests/snapshots/pretty__graph_restrictors_3.snap b/partiql/tests/snapshots/pretty__graph_path_mode_2.snap similarity index 53% rename from partiql/tests/snapshots/pretty__graph_restrictors_3.snap rename to partiql/tests/snapshots/pretty__graph_path_mode_2.snap index 4005bf39..6a524cef 100644 --- a/partiql/tests/snapshots/pretty__graph_restrictors_3.snap +++ b/partiql/tests/snapshots/pretty__graph_path_mode_2.snap @@ -3,31 +3,31 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT p FROM g MATCH ACYCLIC p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha') +SELECT p FROM (g MATCH p = SIMPLE (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT p FROM g MATCH [ACYCLIC p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))] +SELECT p FROM (g MATCH p = SIMPLE (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) ------------------------------------------------------------------------------------------------------------------------ -SELECT p FROM g MATCH [ACYCLIC p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))] +SELECT p FROM (g MATCH p = SIMPLE (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) -------------------------------------------------------------------------------- SELECT p -FROM g MATCH [ACYCLIC p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))] +FROM (g MATCH p = SIMPLE (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) ---------------------------------------- SELECT p -FROM g MATCH [ACYCLIC p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))] +FROM (g MATCH p = SIMPLE (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) ------------------------------ SELECT p -FROM g MATCH [ACYCLIC p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))] +FROM (g MATCH p = SIMPLE (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) -------------------- SELECT p -FROM g MATCH [ACYCLIC p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))] +FROM (g MATCH p = SIMPLE (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) ---------- SELECT p -FROM g MATCH [ACYCLIC p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))] +FROM (g MATCH p = SIMPLE (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) diff --git a/partiql/tests/snapshots/pretty__graph_path_mode_3.snap b/partiql/tests/snapshots/pretty__graph_path_mode_3.snap new file mode 100644 index 00000000..39a3a94f --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_path_mode_3.snap @@ -0,0 +1,33 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT p FROM (g MATCH p = ACYCLIC (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT p FROM (g MATCH p = ACYCLIC (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +------------------------------------------------------------------------------------------------------------------------ +SELECT p FROM (g MATCH p = ACYCLIC (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +-------------------------------------------------------------------------------- +SELECT p +FROM (g MATCH p = ACYCLIC (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +---------------------------------------- +SELECT p +FROM (g MATCH p = ACYCLIC (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +------------------------------ +SELECT p +FROM (g MATCH p = ACYCLIC (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +-------------------- +SELECT p +FROM (g MATCH p = ACYCLIC (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +---------- +SELECT p +FROM (g MATCH p = ACYCLIC (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) diff --git a/partiql/tests/snapshots/pretty__graph_path_var_1.snap b/partiql/tests/snapshots/pretty__graph_path_var_1.snap index d87bdb1d..49b8f2e2 100644 --- a/partiql/tests/snapshots/pretty__graph_path_var_1.snap +++ b/partiql/tests/snapshots/pretty__graph_path_var_1.snap @@ -3,31 +3,31 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b FROM (g MATCH p = (a:A) -[e:E]-> (b:B)) +SELECT a,b FROM GRAPH_TABLE (g MATCH p = (a:A) -[e:E]-> (b:B)) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH p = (a:A) -[e:E]-> (b:B) +SELECT a, b FROM GRAPH_TABLE (g MATCH p = (a:A) -[e:E]-> (b:B)) ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH p = (a:A) -[e:E]-> (b:B) +SELECT a, b FROM GRAPH_TABLE (g MATCH p = (a:A) -[e:E]-> (b:B)) -------------------------------------------------------------------------------- -SELECT a, b FROM g MATCH p = (a:A) -[e:E]-> (b:B) +SELECT a, b FROM GRAPH_TABLE (g MATCH p = (a:A) -[e:E]-> (b:B)) ---------------------------------------- SELECT a, b -FROM g MATCH p = (a:A) -[e:E]-> (b:B) +FROM GRAPH_TABLE (g MATCH p = (a:A) -[e:E]-> (b:B)) ------------------------------ SELECT a, b -FROM g MATCH p = (a:A) -[e:E]-> (b:B) +FROM GRAPH_TABLE (g MATCH p = (a:A) -[e:E]-> (b:B)) -------------------- SELECT a, b -FROM g MATCH p = (a:A) -[e:E]-> (b:B) +FROM GRAPH_TABLE (g MATCH p = (a:A) -[e:E]-> (b:B)) ---------- SELECT a, b -FROM g MATCH p = (a:A) -[e:E]-> (b:B) +FROM GRAPH_TABLE (g MATCH p = (a:A) -[e:E]-> (b:B)) diff --git a/partiql/tests/snapshots/pretty__graph_patterns_2.snap b/partiql/tests/snapshots/pretty__graph_patterns_2.snap index cfd693da..4096a4d0 100644 --- a/partiql/tests/snapshots/pretty__graph_patterns_2.snap +++ b/partiql/tests/snapshots/pretty__graph_patterns_2.snap @@ -3,31 +3,26 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b FROM g MATCH (a)-[:has]->()-[:contains]->(b) +"SELECT a,b FROM (g MATCH (a)-[:has]->()-[:contains]->(b))" ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a) -[:has]-> () -[:contains]-> (b) +"SELECT a,b FROM (g MATCH (a)-[:has]->()-[:contains]->(b))" ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a) -[:has]-> () -[:contains]-> (b) +"SELECT a,b FROM (g MATCH (a)-[:has]->()-[:contains]->(b))" -------------------------------------------------------------------------------- -SELECT a, b FROM g MATCH (a) -[:has]-> () -[:contains]-> (b) +"SELECT a,b FROM (g MATCH (a)-[:has]->()-[:contains]->(b))" ---------------------------------------- -SELECT a, b -FROM g MATCH (a) -[:has]-> () -[:contains]-> (b) +"SELECT a,b FROM (g MATCH (a)-[:has]->()-[:contains]->(b))" ------------------------------ -SELECT a, b -FROM g MATCH (a) -[:has]-> () -[:contains]-> (b) +"SELECT a,b FROM (g MATCH (a)-[:has]->()-[:contains]->(b))" -------------------- -SELECT a, b -FROM g MATCH (a) -[:has]-> () -[:contains]-> (b) +"SELECT a,b FROM (g MATCH (a)-[:has]->()-[:contains]->(b))" ---------- -SELECT a, - b -FROM g MATCH (a) -[:has]-> () -[:contains]-> (b) +"SELECT a,b FROM (g MATCH (a)-[:has]->()-[:contains]->(b))" diff --git a/partiql/tests/snapshots/pretty__graph_patterns_3.snap b/partiql/tests/snapshots/pretty__graph_patterns_3.snap index 1f6fffa4..bb051c2b 100644 --- a/partiql/tests/snapshots/pretty__graph_patterns_3.snap +++ b/partiql/tests/snapshots/pretty__graph_patterns_3.snap @@ -3,35 +3,35 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b FROM (g MATCH (a) -[:has]-> (x), (x)-[:contains]->(b)) +SELECT a,b FROM GRAPH_TABLE (g MATCH (a) -[:has]-> (x), (x)-[:contains]->(b)) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM (g MATCH (a) -[:has]-> (x), (x) -[:contains]-> (b)) +SELECT a, b FROM GRAPH_TABLE (g MATCH (a) -[:has]-> (x), (x) -[:contains]-> (b)) ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM (g MATCH (a) -[:has]-> (x), (x) -[:contains]-> (b)) +SELECT a, b FROM GRAPH_TABLE (g MATCH (a) -[:has]-> (x), (x) -[:contains]-> (b)) -------------------------------------------------------------------------------- -SELECT a, b FROM (g MATCH (a) -[:has]-> (x), (x) -[:contains]-> (b)) +SELECT a, b FROM GRAPH_TABLE (g MATCH (a) -[:has]-> (x), (x) -[:contains]-> (b)) ---------------------------------------- SELECT a, b -FROM (g MATCH (a) -[:has]-> (x), +FROM GRAPH_TABLE (g MATCH (a) -[:has]-> (x), (x) -[:contains]-> (b)) ------------------------------ SELECT a, b -FROM (g MATCH (a) -[:has]-> (x), +FROM GRAPH_TABLE (g MATCH (a) -[:has]-> (x), (x) -[:contains]-> (b)) -------------------- SELECT a, b -FROM (g MATCH (a) -[:has]-> (x), +FROM GRAPH_TABLE (g MATCH (a) -[:has]-> (x), (x) -[:contains]-> (b)) ---------- SELECT a, b -FROM (g MATCH (a) -[:has]-> (x), +FROM GRAPH_TABLE (g MATCH (a) -[:has]-> (x), (x) -[:contains]-> (b)) diff --git a/partiql/tests/snapshots/pretty__graph_quantifiers_1.snap b/partiql/tests/snapshots/pretty__graph_quantifiers_1.snap index 6847fb0f..afcab2d7 100644 --- a/partiql/tests/snapshots/pretty__graph_quantifiers_1.snap +++ b/partiql/tests/snapshots/pretty__graph_quantifiers_1.snap @@ -3,31 +3,31 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b FROM g MATCH (a:A)-[:edge]->*(b:B) +SELECT a,b FROM (g MATCH (a:A)-[:edge]->*(b:B)) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) -[:edge]->* (b:B) +SELECT a, b FROM (g MATCH (a:A) -[:edge]->* (b:B)) ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) -[:edge]->* (b:B) +SELECT a, b FROM (g MATCH (a:A) -[:edge]->* (b:B)) -------------------------------------------------------------------------------- -SELECT a, b FROM g MATCH (a:A) -[:edge]->* (b:B) +SELECT a, b FROM (g MATCH (a:A) -[:edge]->* (b:B)) ---------------------------------------- SELECT a, b -FROM g MATCH (a:A) -[:edge]->* (b:B) +FROM (g MATCH (a:A) -[:edge]->* (b:B)) ------------------------------ SELECT a, b -FROM g MATCH (a:A) -[:edge]->* (b:B) +FROM (g MATCH (a:A) -[:edge]->* (b:B)) -------------------- SELECT a, b -FROM g MATCH (a:A) -[:edge]->* (b:B) +FROM (g MATCH (a:A) -[:edge]->* (b:B)) ---------- SELECT a, b -FROM g MATCH (a:A) -[:edge]->* (b:B) +FROM (g MATCH (a:A) -[:edge]->* (b:B)) diff --git a/partiql/tests/snapshots/pretty__graph_quantifiers_2.snap b/partiql/tests/snapshots/pretty__graph_quantifiers_2.snap index 21c67d7f..74325e5a 100644 --- a/partiql/tests/snapshots/pretty__graph_quantifiers_2.snap +++ b/partiql/tests/snapshots/pretty__graph_quantifiers_2.snap @@ -3,31 +3,31 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b FROM g MATCH (a:A)<-[:edge]-+(b:B) +SELECT a,b FROM (g MATCH (a:A)<-[:edge]-+(b:B)) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) <-[:edge]-+ (b:B) +SELECT a, b FROM (g MATCH (a:A) <-[:edge]-+ (b:B)) ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) <-[:edge]-+ (b:B) +SELECT a, b FROM (g MATCH (a:A) <-[:edge]-+ (b:B)) -------------------------------------------------------------------------------- -SELECT a, b FROM g MATCH (a:A) <-[:edge]-+ (b:B) +SELECT a, b FROM (g MATCH (a:A) <-[:edge]-+ (b:B)) ---------------------------------------- SELECT a, b -FROM g MATCH (a:A) <-[:edge]-+ (b:B) +FROM (g MATCH (a:A) <-[:edge]-+ (b:B)) ------------------------------ SELECT a, b -FROM g MATCH (a:A) <-[:edge]-+ (b:B) +FROM (g MATCH (a:A) <-[:edge]-+ (b:B)) -------------------- SELECT a, b -FROM g MATCH (a:A) <-[:edge]-+ (b:B) +FROM (g MATCH (a:A) <-[:edge]-+ (b:B)) ---------- SELECT a, b -FROM g MATCH (a:A) <-[:edge]-+ (b:B) +FROM (g MATCH (a:A) <-[:edge]-+ (b:B)) diff --git a/partiql/tests/snapshots/pretty__graph_quantifiers_3.snap b/partiql/tests/snapshots/pretty__graph_quantifiers_3.snap index 79c1a025..89c9d29c 100644 --- a/partiql/tests/snapshots/pretty__graph_quantifiers_3.snap +++ b/partiql/tests/snapshots/pretty__graph_quantifiers_3.snap @@ -3,31 +3,31 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b FROM g MATCH (a:A)~[:edge]~{5,}(b:B) +SELECT a,b FROM (g MATCH (a:A)~[:edge]~{5,}(b:B)) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) ~[:edge]~{5,} (b:B) +SELECT a, b FROM (g MATCH (a:A) ~[:edge]~{5,} (b:B)) ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) ~[:edge]~{5,} (b:B) +SELECT a, b FROM (g MATCH (a:A) ~[:edge]~{5,} (b:B)) -------------------------------------------------------------------------------- -SELECT a, b FROM g MATCH (a:A) ~[:edge]~{5,} (b:B) +SELECT a, b FROM (g MATCH (a:A) ~[:edge]~{5,} (b:B)) ---------------------------------------- SELECT a, b -FROM g MATCH (a:A) ~[:edge]~{5,} (b:B) +FROM (g MATCH (a:A) ~[:edge]~{5,} (b:B)) ------------------------------ SELECT a, b -FROM g MATCH (a:A) ~[:edge]~{5,} (b:B) +FROM (g MATCH (a:A) ~[:edge]~{5,} (b:B)) -------------------- SELECT a, b -FROM g MATCH (a:A) ~[:edge]~{5,} (b:B) +FROM (g MATCH (a:A) ~[:edge]~{5,} (b:B)) ---------- SELECT a, b -FROM g MATCH (a:A) ~[:edge]~{5,} (b:B) +FROM (g MATCH (a:A) ~[:edge]~{5,} (b:B)) diff --git a/partiql/tests/snapshots/pretty__graph_quantifiers_4.snap b/partiql/tests/snapshots/pretty__graph_quantifiers_4.snap index 47e8802c..ccf773e8 100644 --- a/partiql/tests/snapshots/pretty__graph_quantifiers_4.snap +++ b/partiql/tests/snapshots/pretty__graph_quantifiers_4.snap @@ -3,31 +3,31 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b FROM g MATCH (a:A)-[e:edge]-{2,6}(b:B) +SELECT a,b FROM (g MATCH (a:A)-[e:edge]-{2,6}(b:B)) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) -[e:edge]-{2,6} (b:B) +SELECT a, b FROM (g MATCH (a:A) -[e:edge]-{2,6} (b:B)) ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) -[e:edge]-{2,6} (b:B) +SELECT a, b FROM (g MATCH (a:A) -[e:edge]-{2,6} (b:B)) -------------------------------------------------------------------------------- -SELECT a, b FROM g MATCH (a:A) -[e:edge]-{2,6} (b:B) +SELECT a, b FROM (g MATCH (a:A) -[e:edge]-{2,6} (b:B)) ---------------------------------------- SELECT a, b -FROM g MATCH (a:A) -[e:edge]-{2,6} (b:B) +FROM (g MATCH (a:A) -[e:edge]-{2,6} (b:B)) ------------------------------ SELECT a, b -FROM g MATCH (a:A) -[e:edge]-{2,6} (b:B) +FROM (g MATCH (a:A) -[e:edge]-{2,6} (b:B)) -------------------- SELECT a, b -FROM g MATCH (a:A) -[e:edge]-{2,6} (b:B) +FROM (g MATCH (a:A) -[e:edge]-{2,6} (b:B)) ---------- SELECT a, b -FROM g MATCH (a:A) -[e:edge]-{2,6} (b:B) +FROM (g MATCH (a:A) -[e:edge]-{2,6} (b:B)) diff --git a/partiql/tests/snapshots/pretty__graph_quantifiers_5.snap b/partiql/tests/snapshots/pretty__graph_quantifiers_5.snap index 13dbbc8a..d451bad8 100644 --- a/partiql/tests/snapshots/pretty__graph_quantifiers_5.snap +++ b/partiql/tests/snapshots/pretty__graph_quantifiers_5.snap @@ -3,30 +3,31 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b FROM g MATCH (a:A)->*(b:B) +SELECT a,b FROM (g MATCH (a:A)->*(b:B)) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) ->* (b:B) +SELECT a, b FROM (g MATCH (a:A) ->* (b:B)) ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) ->* (b:B) +SELECT a, b FROM (g MATCH (a:A) ->* (b:B)) -------------------------------------------------------------------------------- -SELECT a, b FROM g MATCH (a:A) ->* (b:B) +SELECT a, b FROM (g MATCH (a:A) ->* (b:B)) ---------------------------------------- -SELECT a, b FROM g MATCH (a:A) ->* (b:B) +SELECT a, b +FROM (g MATCH (a:A) ->* (b:B)) ------------------------------ SELECT a, b -FROM g MATCH (a:A) ->* (b:B) +FROM (g MATCH (a:A) ->* (b:B)) -------------------- SELECT a, b -FROM g MATCH (a:A) ->* (b:B) +FROM (g MATCH (a:A) ->* (b:B)) ---------- SELECT a, b -FROM g MATCH (a:A) ->* (b:B) +FROM (g MATCH (a:A) ->* (b:B)) diff --git a/partiql/tests/snapshots/pretty__graph_quantifiers_6.snap b/partiql/tests/snapshots/pretty__graph_quantifiers_6.snap index 50da64d9..7b6ff3b5 100644 --- a/partiql/tests/snapshots/pretty__graph_quantifiers_6.snap +++ b/partiql/tests/snapshots/pretty__graph_quantifiers_6.snap @@ -3,30 +3,31 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b FROM g MATCH (a:A)<-+(b:B) +SELECT a,b FROM (g MATCH (a:A)<-+(b:B)) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) <-+ (b:B) +SELECT a, b FROM (g MATCH (a:A) <-+ (b:B)) ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) <-+ (b:B) +SELECT a, b FROM (g MATCH (a:A) <-+ (b:B)) -------------------------------------------------------------------------------- -SELECT a, b FROM g MATCH (a:A) <-+ (b:B) +SELECT a, b FROM (g MATCH (a:A) <-+ (b:B)) ---------------------------------------- -SELECT a, b FROM g MATCH (a:A) <-+ (b:B) +SELECT a, b +FROM (g MATCH (a:A) <-+ (b:B)) ------------------------------ SELECT a, b -FROM g MATCH (a:A) <-+ (b:B) +FROM (g MATCH (a:A) <-+ (b:B)) -------------------- SELECT a, b -FROM g MATCH (a:A) <-+ (b:B) +FROM (g MATCH (a:A) <-+ (b:B)) ---------- SELECT a, b -FROM g MATCH (a:A) <-+ (b:B) +FROM (g MATCH (a:A) <-+ (b:B)) diff --git a/partiql/tests/snapshots/pretty__graph_quantifiers_7.snap b/partiql/tests/snapshots/pretty__graph_quantifiers_7.snap index 78e9be73..7e906382 100644 --- a/partiql/tests/snapshots/pretty__graph_quantifiers_7.snap +++ b/partiql/tests/snapshots/pretty__graph_quantifiers_7.snap @@ -3,31 +3,31 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b FROM g MATCH (a:A)~{5,}(b:B) +SELECT a,b FROM (g MATCH (a:A)~{5,}(b:B)) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) ~{5,} (b:B) +SELECT a, b FROM (g MATCH (a:A) ~{5,} (b:B)) ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) ~{5,} (b:B) +SELECT a, b FROM (g MATCH (a:A) ~{5,} (b:B)) -------------------------------------------------------------------------------- -SELECT a, b FROM g MATCH (a:A) ~{5,} (b:B) +SELECT a, b FROM (g MATCH (a:A) ~{5,} (b:B)) ---------------------------------------- SELECT a, b -FROM g MATCH (a:A) ~{5,} (b:B) +FROM (g MATCH (a:A) ~{5,} (b:B)) ------------------------------ SELECT a, b -FROM g MATCH (a:A) ~{5,} (b:B) +FROM (g MATCH (a:A) ~{5,} (b:B)) -------------------- SELECT a, b -FROM g MATCH (a:A) ~{5,} (b:B) +FROM (g MATCH (a:A) ~{5,} (b:B)) ---------- SELECT a, b -FROM g MATCH (a:A) ~{5,} (b:B) +FROM (g MATCH (a:A) ~{5,} (b:B)) diff --git a/partiql/tests/snapshots/pretty__graph_quantifiers_8.snap b/partiql/tests/snapshots/pretty__graph_quantifiers_8.snap index b6dcfb89..34c9ead9 100644 --- a/partiql/tests/snapshots/pretty__graph_quantifiers_8.snap +++ b/partiql/tests/snapshots/pretty__graph_quantifiers_8.snap @@ -3,31 +3,31 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b FROM g MATCH (a:A)-{2,6}(b:B) +SELECT a,b FROM (g MATCH (a:A)-{2,6}(b:B)) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) -{2,6} (b:B) +SELECT a, b FROM (g MATCH (a:A) -{2,6} (b:B)) ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b FROM g MATCH (a:A) -{2,6} (b:B) +SELECT a, b FROM (g MATCH (a:A) -{2,6} (b:B)) -------------------------------------------------------------------------------- -SELECT a, b FROM g MATCH (a:A) -{2,6} (b:B) +SELECT a, b FROM (g MATCH (a:A) -{2,6} (b:B)) ---------------------------------------- SELECT a, b -FROM g MATCH (a:A) -{2,6} (b:B) +FROM (g MATCH (a:A) -{2,6} (b:B)) ------------------------------ SELECT a, b -FROM g MATCH (a:A) -{2,6} (b:B) +FROM (g MATCH (a:A) -{2,6} (b:B)) -------------------- SELECT a, b -FROM g MATCH (a:A) -{2,6} (b:B) +FROM (g MATCH (a:A) -{2,6} (b:B)) ---------- SELECT a, b -FROM g MATCH (a:A) -{2,6} (b:B) +FROM (g MATCH (a:A) -{2,6} (b:B)) diff --git a/partiql/tests/snapshots/pretty__graph_selectors_1.snap b/partiql/tests/snapshots/pretty__graph_search_prefix_1.snap similarity index 51% rename from partiql/tests/snapshots/pretty__graph_selectors_1.snap rename to partiql/tests/snapshots/pretty__graph_search_prefix_1.snap index 095dee2c..7d11558a 100644 --- a/partiql/tests/snapshots/pretty__graph_selectors_1.snap +++ b/partiql/tests/snapshots/pretty__graph_search_prefix_1.snap @@ -3,34 +3,31 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT p FROM g MATCH ANY SHORTEST p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha') +SELECT p FROM (g MATCH p = ANY SHORTEST (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT p FROM g MATCH ANY SHORTEST p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) +SELECT p FROM (g MATCH p = ANY SHORTEST (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) ------------------------------------------------------------------------------------------------------------------------ -SELECT p FROM g MATCH ANY SHORTEST p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) +SELECT p FROM (g MATCH p = ANY SHORTEST (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) -------------------------------------------------------------------------------- -SELECT p FROM g MATCH ANY SHORTEST - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) +SELECT p +FROM (g MATCH p = ANY SHORTEST (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) ---------------------------------------- -SELECT p FROM g MATCH ANY SHORTEST - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) +SELECT p +FROM (g MATCH p = ANY SHORTEST (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) ------------------------------ SELECT p -FROM g MATCH ANY SHORTEST - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) +FROM (g MATCH p = ANY SHORTEST (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) -------------------- SELECT p -FROM g MATCH ANY SHORTEST - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) +FROM (g MATCH p = ANY SHORTEST (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) ---------- SELECT p -FROM g MATCH ANY SHORTEST - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) +FROM (g MATCH p = ANY SHORTEST (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) diff --git a/partiql/tests/snapshots/pretty__graph_search_prefix_2.snap b/partiql/tests/snapshots/pretty__graph_search_prefix_2.snap new file mode 100644 index 00000000..664b65ed --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_search_prefix_2.snap @@ -0,0 +1,33 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT p FROM (g MATCH p = ALL SHORTEST (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT p FROM (g MATCH p = ALL SHORTEST (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +------------------------------------------------------------------------------------------------------------------------ +SELECT p FROM (g MATCH p = ALL SHORTEST (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +-------------------------------------------------------------------------------- +SELECT p +FROM (g MATCH p = ALL SHORTEST (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +---------------------------------------- +SELECT p +FROM (g MATCH p = ALL SHORTEST (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +------------------------------ +SELECT p +FROM (g MATCH p = ALL SHORTEST (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +-------------------- +SELECT p +FROM (g MATCH p = ALL SHORTEST (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +---------- +SELECT p +FROM (g MATCH p = ALL SHORTEST (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) diff --git a/partiql/tests/snapshots/pretty__graph_restrictors_1.snap b/partiql/tests/snapshots/pretty__graph_search_prefix_3.snap similarity index 54% rename from partiql/tests/snapshots/pretty__graph_restrictors_1.snap rename to partiql/tests/snapshots/pretty__graph_search_prefix_3.snap index b7210d36..ab789afe 100644 --- a/partiql/tests/snapshots/pretty__graph_restrictors_1.snap +++ b/partiql/tests/snapshots/pretty__graph_search_prefix_3.snap @@ -3,31 +3,31 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT p FROM g MATCH TRAIL p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha') +SELECT p FROM (g MATCH p = ANY (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT p FROM g MATCH [TRAIL p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))] +SELECT p FROM (g MATCH p = ANY (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) ------------------------------------------------------------------------------------------------------------------------ -SELECT p FROM g MATCH [TRAIL p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))] +SELECT p FROM (g MATCH p = ANY (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) -------------------------------------------------------------------------------- SELECT p -FROM g MATCH [TRAIL p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))] +FROM (g MATCH p = ANY (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) ---------------------------------------- SELECT p -FROM g MATCH [TRAIL p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))] +FROM (g MATCH p = ANY (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) ------------------------------ SELECT p -FROM g MATCH [TRAIL p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))] +FROM (g MATCH p = ANY (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) -------------------- SELECT p -FROM g MATCH [TRAIL p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))] +FROM (g MATCH p = ANY (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) ---------- SELECT p -FROM g MATCH [TRAIL p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))] +FROM (g MATCH p = ANY (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) diff --git a/partiql/tests/snapshots/pretty__graph_selectors_3.snap b/partiql/tests/snapshots/pretty__graph_search_prefix_4.snap similarity index 52% rename from partiql/tests/snapshots/pretty__graph_selectors_3.snap rename to partiql/tests/snapshots/pretty__graph_search_prefix_4.snap index 35bf53a8..0b5fb72f 100644 --- a/partiql/tests/snapshots/pretty__graph_selectors_3.snap +++ b/partiql/tests/snapshots/pretty__graph_search_prefix_4.snap @@ -3,33 +3,31 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT p FROM g MATCH ANY p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha') +SELECT p FROM (g MATCH p = ANY 5 (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT p FROM g MATCH ANY p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) +SELECT p FROM (g MATCH p = ANY 5 (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) ------------------------------------------------------------------------------------------------------------------------ -SELECT p FROM g MATCH ANY p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) +SELECT p FROM (g MATCH p = ANY 5 (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) -------------------------------------------------------------------------------- -SELECT p FROM g MATCH ANY - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) +SELECT p +FROM (g MATCH p = ANY 5 (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) ---------------------------------------- -SELECT p FROM g MATCH ANY - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) +SELECT p +FROM (g MATCH p = ANY 5 (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) ------------------------------ -SELECT p FROM g MATCH ANY - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) +SELECT p +FROM (g MATCH p = ANY 5 (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) -------------------- SELECT p -FROM g MATCH ANY - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) +FROM (g MATCH p = ANY 5 (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) ---------- SELECT p -FROM g MATCH ANY - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) +FROM (g MATCH p = ANY 5 (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) diff --git a/partiql/tests/snapshots/pretty__graph_selectors_5.snap b/partiql/tests/snapshots/pretty__graph_search_prefix_5.snap similarity index 52% rename from partiql/tests/snapshots/pretty__graph_selectors_5.snap rename to partiql/tests/snapshots/pretty__graph_search_prefix_5.snap index 907a697d..e2cb5a56 100644 --- a/partiql/tests/snapshots/pretty__graph_selectors_5.snap +++ b/partiql/tests/snapshots/pretty__graph_search_prefix_5.snap @@ -3,34 +3,31 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT p FROM g MATCH SHORTEST 5 p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha') +SELECT p FROM (g MATCH p = SHORTEST 5 (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT p FROM g MATCH SHORTEST 5 p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) +SELECT p FROM (g MATCH p = SHORTEST 5 (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) ------------------------------------------------------------------------------------------------------------------------ -SELECT p FROM g MATCH SHORTEST 5 p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) +SELECT p FROM (g MATCH p = SHORTEST 5 (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) -------------------------------------------------------------------------------- -SELECT p FROM g MATCH SHORTEST 5 - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) +SELECT p +FROM (g MATCH p = SHORTEST 5 (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) ---------------------------------------- -SELECT p FROM g MATCH SHORTEST 5 - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) +SELECT p +FROM (g MATCH p = SHORTEST 5 (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) ------------------------------ SELECT p -FROM g MATCH SHORTEST 5 - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) +FROM (g MATCH p = SHORTEST 5 (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) -------------------- SELECT p -FROM g MATCH SHORTEST 5 - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) +FROM (g MATCH p = SHORTEST 5 (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) ---------- SELECT p -FROM g MATCH SHORTEST 5 - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) +FROM (g MATCH p = SHORTEST 5 (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) diff --git a/partiql/tests/snapshots/pretty__graph_search_prefix_6.snap b/partiql/tests/snapshots/pretty__graph_search_prefix_6.snap new file mode 100644 index 00000000..aafba172 --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_search_prefix_6.snap @@ -0,0 +1,34 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT p FROM (g MATCH p = SHORTEST 5 GROUP (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT p FROM (g MATCH p = SHORTEST 5 GROUPS (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +------------------------------------------------------------------------------------------------------------------------ +SELECT p +FROM (g MATCH p = SHORTEST 5 GROUPS (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +-------------------------------------------------------------------------------- +SELECT p +FROM (g MATCH p = SHORTEST 5 GROUPS (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +---------------------------------------- +SELECT p +FROM (g MATCH p = SHORTEST 5 GROUPS (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +------------------------------ +SELECT p +FROM (g MATCH p = SHORTEST 5 GROUPS (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +-------------------- +SELECT p +FROM (g MATCH p = SHORTEST 5 GROUPS (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +---------- +SELECT p +FROM (g MATCH p = SHORTEST 5 GROUPS (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) diff --git a/partiql/tests/snapshots/pretty__graph_selectors_2.snap b/partiql/tests/snapshots/pretty__graph_selectors_2.snap deleted file mode 100644 index 72deb5ca..00000000 --- a/partiql/tests/snapshots/pretty__graph_selectors_2.snap +++ /dev/null @@ -1,36 +0,0 @@ ---- -source: partiql/tests/pretty.rs -expression: doc ---- -======================================================================================================================================================================================================== -SELECT p FROM g MATCH ALL SHORTEST p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha') -======================================================================================================================================================================================================== - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -SELECT p FROM g MATCH ALL SHORTEST p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) - ------------------------------------------------------------------------------------------------------------------------- -SELECT p FROM g MATCH ALL SHORTEST p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) - --------------------------------------------------------------------------------- -SELECT p FROM g MATCH ALL SHORTEST - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) - ----------------------------------------- -SELECT p FROM g MATCH ALL SHORTEST - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) - ------------------------------- -SELECT p -FROM g MATCH ALL SHORTEST - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) - --------------------- -SELECT p -FROM g MATCH ALL SHORTEST - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) - ----------- -SELECT p -FROM g MATCH ALL SHORTEST - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) diff --git a/partiql/tests/snapshots/pretty__graph_selectors_4.snap b/partiql/tests/snapshots/pretty__graph_selectors_4.snap deleted file mode 100644 index c67b0d9e..00000000 --- a/partiql/tests/snapshots/pretty__graph_selectors_4.snap +++ /dev/null @@ -1,35 +0,0 @@ ---- -source: partiql/tests/pretty.rs -expression: doc ---- -======================================================================================================================================================================================================== -SELECT p FROM g MATCH ANY 5 p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha') -======================================================================================================================================================================================================== - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -SELECT p FROM g MATCH ANY 5 p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) - ------------------------------------------------------------------------------------------------------------------------- -SELECT p FROM g MATCH ANY 5 p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) - --------------------------------------------------------------------------------- -SELECT p FROM g MATCH ANY 5 - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) - ----------------------------------------- -SELECT p FROM g MATCH ANY 5 - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) - ------------------------------- -SELECT p FROM g MATCH ANY 5 - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) - --------------------- -SELECT p -FROM g MATCH ANY 5 - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) - ----------- -SELECT p -FROM g MATCH ANY 5 - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) diff --git a/partiql/tests/snapshots/pretty__graph_selectors_6.snap b/partiql/tests/snapshots/pretty__graph_selectors_6.snap deleted file mode 100644 index e99309bd..00000000 --- a/partiql/tests/snapshots/pretty__graph_selectors_6.snap +++ /dev/null @@ -1,36 +0,0 @@ ---- -source: partiql/tests/pretty.rs -expression: doc ---- -======================================================================================================================================================================================================== -SELECT p FROM g MATCH SHORTEST 5 GROUP p = (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha') -======================================================================================================================================================================================================== - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -SELECT p FROM g MATCH SHORTEST 5 GROUP p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) - ------------------------------------------------------------------------------------------------------------------------- -SELECT p FROM g MATCH SHORTEST 5 GROUP p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) - --------------------------------------------------------------------------------- -SELECT p FROM g MATCH SHORTEST 5 GROUP - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) - ----------------------------------------- -SELECT p FROM g MATCH SHORTEST 5 GROUP - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) - ------------------------------- -SELECT p -FROM g MATCH SHORTEST 5 GROUP - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) - --------------------- -SELECT p -FROM g MATCH SHORTEST 5 GROUP - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) - ----------- -SELECT p -FROM g MATCH SHORTEST 5 GROUP - p = (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha')) diff --git a/partiql/tests/snapshots/pretty__graph_union_1.snap b/partiql/tests/snapshots/pretty__graph_union_1.snap index 2c2005a2..62f44037 100644 --- a/partiql/tests/snapshots/pretty__graph_union_1.snap +++ b/partiql/tests/snapshots/pretty__graph_union_1.snap @@ -7,37 +7,37 @@ expression: doc ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -(MyGraph MATCH (x)) +((MyGraph MATCH (x))) UNION (SELECT * FROM tbl1) ------------------------------------------------------------------------------------------------------------------------ -(MyGraph MATCH (x)) +((MyGraph MATCH (x))) UNION (SELECT * FROM tbl1) -------------------------------------------------------------------------------- -(MyGraph MATCH (x)) +((MyGraph MATCH (x))) UNION (SELECT * FROM tbl1) ---------------------------------------- -(MyGraph MATCH (x)) +((MyGraph MATCH (x))) UNION (SELECT * FROM tbl1) ------------------------------ -(MyGraph MATCH (x)) +((MyGraph MATCH (x))) UNION (SELECT * FROM tbl1) -------------------- -(MyGraph MATCH (x)) +((MyGraph MATCH (x))) UNION (SELECT * FROM tbl1) ---------- -(MyGraph MATCH (x)) +((MyGraph MATCH (x))) UNION (SELECT * FROM tbl1) diff --git a/partiql/tests/snapshots/pretty__graph_union_2.snap b/partiql/tests/snapshots/pretty__graph_union_2.snap index 1164ea58..6f8c3684 100644 --- a/partiql/tests/snapshots/pretty__graph_union_2.snap +++ b/partiql/tests/snapshots/pretty__graph_union_2.snap @@ -9,35 +9,35 @@ SELECT * FROM tbl1 UNION (MyGraph MATCH (x)) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ (SELECT * FROM tbl1) UNION -(MyGraph MATCH (x)) +((MyGraph MATCH (x))) ------------------------------------------------------------------------------------------------------------------------ (SELECT * FROM tbl1) UNION -(MyGraph MATCH (x)) +((MyGraph MATCH (x))) -------------------------------------------------------------------------------- (SELECT * FROM tbl1) UNION -(MyGraph MATCH (x)) +((MyGraph MATCH (x))) ---------------------------------------- (SELECT * FROM tbl1) UNION -(MyGraph MATCH (x)) +((MyGraph MATCH (x))) ------------------------------ (SELECT * FROM tbl1) UNION -(MyGraph MATCH (x)) +((MyGraph MATCH (x))) -------------------- (SELECT * FROM tbl1) UNION -(MyGraph MATCH (x)) +((MyGraph MATCH (x))) ---------- (SELECT * FROM tbl1) UNION -(MyGraph MATCH (x)) +((MyGraph MATCH (x))) From d5a86966c95652f65c2c608f05421b2f951fe5d4 Mon Sep 17 00:00:00 2001 From: Josh Pschorr Date: Fri, 4 Apr 2025 15:07:50 -0700 Subject: [PATCH 4/9] GRAPH_TABLE updates to logical plan lowering --- partiql-logical-planner/src/graph.rs | 150 ++++++++++----- partiql-logical-planner/src/lower.rs | 18 +- partiql/tests/graph.rs | 41 +++++ .../snapshots/graph__GPML L Triples.snap | 57 ++++++ .../snapshots/graph__GPML LUR Triples.snap | 172 ++++++++++++++++++ .../tests/snapshots/graph__GPML Nodes.snap | 20 ++ .../snapshots/graph__RFC0025 L Triples.snap | 5 + .../snapshots/graph__RFC0025 LUR Triples.snap | 5 + .../tests/snapshots/graph__RFC0025 Nodes.snap | 5 + .../snapshots/pretty__graph_filters_1.snap | 54 +++--- .../snapshots/pretty__graph_filters_2.snap | 61 +++++++ .../pretty__graph_match_and_join_1.snap | 18 +- .../pretty__graph_match_and_join_2.snap | 50 +++++ .../snapshots/pretty__graph_patterns_1.snap | 22 +-- 14 files changed, 581 insertions(+), 97 deletions(-) create mode 100644 partiql/tests/snapshots/graph__GPML L Triples.snap create mode 100644 partiql/tests/snapshots/graph__GPML LUR Triples.snap create mode 100644 partiql/tests/snapshots/graph__GPML Nodes.snap create mode 100644 partiql/tests/snapshots/graph__RFC0025 L Triples.snap create mode 100644 partiql/tests/snapshots/graph__RFC0025 LUR Triples.snap create mode 100644 partiql/tests/snapshots/graph__RFC0025 Nodes.snap create mode 100644 partiql/tests/snapshots/pretty__graph_filters_2.snap create mode 100644 partiql/tests/snapshots/pretty__graph_match_and_join_2.snap diff --git a/partiql-logical-planner/src/graph.rs b/partiql-logical-planner/src/graph.rs index d23c090d..0e8a61d9 100644 --- a/partiql-logical-planner/src/graph.rs +++ b/partiql-logical-planner/src/graph.rs @@ -1,11 +1,12 @@ use num::Integer; use partiql_ast::ast; -use partiql_ast::ast::{GraphMatchDirection, GraphPathPatternPart}; +use partiql_ast::ast::{GraphMatchDirection, GraphMatchLabel, GraphMatchPathPattern}; use partiql_logical::graph::bind_name::FreshBinder; use partiql_logical::graph::{ BindSpec, DirectionFilter, EdgeFilter, EdgeMatch, LabelFilter, NodeFilter, NodeMatch, PathMatch, PathPattern, PathPatternMatch, StepFilter, TripleFilter, ValueFilter, }; +use petgraph::visit::Walker; use std::mem::take; #[macro_export] @@ -165,76 +166,137 @@ impl GraphToLogical { } pub(crate) fn plan_graph_match( &self, - match_expr: &ast::GraphMatchExpr, + graph_match: &ast::GraphMatch, ) -> Result { - if match_expr.selector.is_some() { - not_yet_implemented_result!("MATCH expression selectors are not yet supported."); + if graph_match.shape.cols.is_some() { + not_yet_implemented_result!("MATCH expression COLUMNS are not yet supported."); + } + if graph_match.shape.export.is_some() { + not_yet_implemented_result!("MATCH expression EXPORT are not yet supported."); + } + if graph_match.shape.rows.is_some() { + not_yet_implemented_result!("MATCH expression ROWS are not yet supported."); + } + + let pattern = self.plan_graph_pattern(&graph_match.pattern)?; + let normalized = self.normalize(pattern)?; + let expanded = self.expand(normalized)?; + self.plan(expanded) + } + + fn plan_graph_pattern(&self, pattern: &ast::GraphPattern) -> Result, String> { + if pattern.mode.is_some() { + not_yet_implemented_result!("MATCH expression MATCH MODE is not yet supported."); + } + if pattern.keep.is_some() { + not_yet_implemented_result!("MATCH expression KEEP is not yet supported."); + } + if pattern.where_clause.is_some() { + not_yet_implemented_result!("MATCH expression WHERE is not yet supported."); } - if match_expr.patterns.len() != 1 { + if pattern.patterns.len() != 1 { not_yet_implemented_result!( "MATCH expression with multiple patterns are not yet supported." ); } - let first_pattern = &match_expr.patterns[0].node; - let pattern = self.plan_graph_pattern(first_pattern)?; - let normalized = self.normalize(pattern)?; - let expanded = self.expand(normalized)?; - self.plan(expanded) + let first_pattern = &pattern.patterns[0]; + self.plan_graph_path_pattern(first_pattern) } - fn plan_graph_pattern( + fn plan_graph_path_pattern( &self, pattern: &ast::GraphPathPattern, ) -> Result, String> { - if pattern.restrictor.is_some() { - not_yet_implemented_result!("MATCH pattern restrictors are not yet supported."); + if pattern.prefix.is_some() { + not_yet_implemented_result!("MATCH pattern SEARCH/MODE prefix are not yet supported."); } - if pattern.quantifier.is_some() { - not_yet_implemented_result!("MATCH pattern quantifiers are not yet supported."); + if pattern.variable.is_some() { + not_yet_implemented_result!("MATCH pattern path variables are not yet supported."); } - if pattern.prefilter.is_some() { - not_yet_implemented_result!("MATCH pattern prefilters are not yet supported."); + + self.plan_graph_match_path_pattern(&pattern.path) + } + + fn plan_graph_subpath_pattern( + &self, + pattern: &ast::GraphPathSubPattern, + ) -> Result, String> { + if pattern.mode.is_some() { + not_yet_implemented_result!("MATCH pattern MODE prefix are not yet supported."); } if pattern.variable.is_some() { not_yet_implemented_result!("MATCH pattern path variables are not yet supported."); } + if pattern.where_clause.is_some() { + not_yet_implemented_result!("MATCH expression WHERE is not yet supported."); + } - let parts = pattern - .parts - .iter() - .map(|p| self.plan_graph_pattern_part(p)); - let result: Result, _> = parts.collect(); - - result.map(|r| r.into_iter().flatten().collect()) + self.plan_graph_match_path_pattern(&pattern.path) } - fn plan_graph_pattern_part( + fn plan_graph_match_path_pattern( &self, - part: &ast::GraphPathPatternPart, + pattern: &ast::GraphMatchPathPattern, ) -> Result, String> { - match part { - GraphPathPatternPart::Node(n) => self.plan_graph_pattern_part_node(&n.node), - GraphPathPatternPart::Edge(e) => self.plan_graph_pattern_part_edge(&e.node), - GraphPathPatternPart::Pattern(pattern) => self.plan_graph_pattern(&pattern.node), + match pattern { + GraphMatchPathPattern::Path(path) => { + let path: Result>, _> = path + .iter() + .map(|elt| self.plan_graph_match_path_pattern(elt)) + .collect(); + Ok(path?.into_iter().flatten().collect()) + } + GraphMatchPathPattern::Union(_) => { + not_yet_implemented_result!("MATCH expression UNION is not yet supported.") + } + GraphMatchPathPattern::Multiset(_) => { + not_yet_implemented_result!("MATCH expression MULTISET is not yet supported.") + } + GraphMatchPathPattern::Questioned(_) => { + not_yet_implemented_result!("MATCH expression QUESTIONED is not yet supported.") + } + GraphMatchPathPattern::Quantified(_, _) => { + not_yet_implemented_result!("MATCH expression QUANTIFIED is not yet supported.") + } + GraphMatchPathPattern::Sub(subpath) => self.plan_graph_subpath_pattern(subpath), + GraphMatchPathPattern::Node(n) => self.plan_graph_pattern_part_node(&n), + GraphMatchPathPattern::Edge(e) => self.plan_graph_pattern_part_edge(&e), + GraphMatchPathPattern::Simplified(_) => { + not_yet_implemented_result!( + "MATCH expression Simplified Edge Expressions are not yet supported." + ) + } } } fn plan_graph_pattern_label( &self, - label: &Option>, + label: Option<&GraphMatchLabel>, ) -> Result { - match label { - None => Ok(LabelFilter::Always), - Some(labels) => { - if labels.len() != 1 { + if let Some(label) = label { + match label { + GraphMatchLabel::Name(n) => Ok(LabelFilter::Named(n.value.clone())), + GraphMatchLabel::Wildcard => Ok(LabelFilter::Always), + GraphMatchLabel::Negated(_) => { + not_yet_implemented_result!( + "MATCH expression label negation is not yet supported." + ); + } + GraphMatchLabel::Conjunction(_) => { not_yet_implemented_result!( - "MATCH expression with multiple patterns are not yet supported." + "MATCH expression label conjunction is not yet supported." + ); + } + GraphMatchLabel::Disjunction(_) => { + not_yet_implemented_result!( + "MATCH expression label disjunction is not yet supported." ); } - Ok(LabelFilter::Named(labels[0].value.clone())) } + } else { + Ok(LabelFilter::Always) } } @@ -242,17 +304,18 @@ impl GraphToLogical { &self, node: &ast::GraphMatchNode, ) -> Result, String> { - if node.prefilter.is_some() { - not_yet_implemented_result!("MATCH node prefilters are not yet supported."); + if node.where_clause.is_some() { + not_yet_implemented_result!("MATCH node where_clauses are not yet supported."); } let binder = match &node.variable { None => self.graph_id.node(), Some(v) => v.value.clone(), }; + let label = self.plan_graph_pattern_label(node.label.as_deref())?; let node_match = NodeMatch { binder: BindSpec(binder), spec: NodeFilter { - label: self.plan_graph_pattern_label(&node.label)?, + label, filter: ValueFilter::Always, }, }; @@ -266,8 +329,8 @@ impl GraphToLogical { if edge.quantifier.is_some() { not_yet_implemented_result!("MATCH edge quantifiers are not yet supported."); } - if edge.prefilter.is_some() { - not_yet_implemented_result!("MATCH edge prefilters are not yet supported."); + if edge.where_clause.is_some() { + not_yet_implemented_result!("MATCH edge where_clauses are not yet supported."); } let direction = match &edge.direction { GraphMatchDirection::Left => DirectionFilter::L, @@ -282,10 +345,11 @@ impl GraphToLogical { None => self.graph_id.node(), Some(v) => v.value.clone(), }; + let label = self.plan_graph_pattern_label(edge.label.as_deref())?; let edge_match = EdgeMatch { binder: BindSpec(binder), spec: EdgeFilter { - label: self.plan_graph_pattern_label(&edge.label)?, + label, filter: ValueFilter::Always, }, }; diff --git a/partiql-logical-planner/src/lower.rs b/partiql-logical-planner/src/lower.rs index ab9dd8a4..7335b605 100644 --- a/partiql-logical-planner/src/lower.rs +++ b/partiql-logical-planner/src/lower.rs @@ -1512,6 +1512,11 @@ impl<'ast> Visitor<'ast> for AstToLogical<'_> { as_key, at_key, }), + FromLetKind::GraphTable => logical::BindingsOp::Scan(logical::Scan { + expr, + as_key, + at_key, + }), }; let id = self.plan.add_operator(bexpr); self.push_bexpr(id); @@ -1899,16 +1904,19 @@ impl<'ast> Visitor<'ast> for AstToLogical<'_> { self.enter_env(); Traverse::Continue } - fn exit_graph_match(&mut self, graph_pattern: &'ast GraphMatch) -> Traverse { + fn exit_graph_match(&mut self, graph_match: &'ast GraphMatch) -> Traverse { let mut env = self.exit_env(); true_or_fault!(self, env.len() == 1, "env.len() is not 1"); - let value = Box::new(env.pop().unwrap()); + let graph_reference = Box::new(env.pop().unwrap()); let graph_planner = crate::graph::GraphToLogical::default(); - match graph_planner.plan_graph_match(&graph_pattern.graph_expr.node) { - Ok(pattern) => { - self.push_vexpr(ValueExpr::GraphMatch(GraphMatchExpr { value, pattern })); + match graph_planner.plan_graph_match(&graph_match) { + Ok(pattern) => { + self.push_vexpr(ValueExpr::GraphMatch(GraphMatchExpr { + value: graph_reference, + pattern, + })); Traverse::Continue } Err(e) => { diff --git a/partiql/tests/graph.rs b/partiql/tests/graph.rs index 3ac57b25..7e3a71d7 100644 --- a/partiql/tests/graph.rs +++ b/partiql/tests/graph.rs @@ -1,6 +1,16 @@ +use insta::assert_snapshot; +use partiql_catalog::catalog::PartiqlCatalog; +use partiql_catalog::context::SystemContext; +use partiql_common::pretty::ToPretty; +use partiql_eval::eval::graph::string_graph::StringGraphTypes; +use partiql_eval::eval::BasicContext; +use partiql_eval::plan::EvaluationMode; use partiql_extension_ion::decode::{IonDecodeResult, IonDecoderBuilder, IonDecoderConfig}; +use crate::common::{compile, evaluate, lower, parse}; use partiql_extension_ion::Encoding; +use partiql_value::{tuple, DateTime, Value}; + mod common; #[track_caller] @@ -155,3 +165,34 @@ fn repeated_identifers() { {id:e1, ends: (n1 -- n1)} ] }"##, ) } + +#[track_caller] +fn snapshot_test_graph_eval(name: &'static str, contents: &'static str, query: &'static str) { + let graph = decode_ion_text(contents, Encoding::PartiqlEncodedAsIon).expect("graph decode"); + let bindings = tuple![("g", graph)]; + + let parsed = parse(query).expect("parse"); + let catalog = PartiqlCatalog::default(); + let lowered = lower(&catalog, &parsed).expect("lower"); + let plan = compile(EvaluationMode::Permissive, &catalog, lowered).expect("compile"); + let res = evaluate(plan, bindings.into()); + assert!(res.is_ok()); + assert_snapshot!(name, res.unwrap().result); +} + +#[test] +fn select_star_rfc_0025() { + // The RFC 0025 graph has no payloads so these just return an appropriate number of `{}` + let contents = include_str!("resources/rfc0025-example.ion"); + snapshot_test_graph_eval("RFC0025 Nodes", contents, "(g MATCH (x))"); + snapshot_test_graph_eval("RFC0025 L Triples", contents, "(g MATCH (x) -> (y))"); + snapshot_test_graph_eval("RFC0025 LUR Triples", contents, "(g MATCH (x) - (y))"); +} + +#[test] +fn select_star_gpml_paper() { + let contents = include_str!("resources/gpml-paper-example.ion"); + snapshot_test_graph_eval("GPML Nodes", contents, "(g MATCH (x))"); + snapshot_test_graph_eval("GPML L Triples", contents, "(g MATCH (x) -[e]-> (y))"); + snapshot_test_graph_eval("GPML LUR Triples", contents, "(g MATCH (x) -[e]- (y))"); +} diff --git a/partiql/tests/snapshots/graph__GPML L Triples.snap b/partiql/tests/snapshots/graph__GPML L Triples.snap new file mode 100644 index 00000000..55c12b14 --- /dev/null +++ b/partiql/tests/snapshots/graph__GPML L Triples.snap @@ -0,0 +1,57 @@ +--- +source: partiql/tests/graph.rs +expression: res.unwrap().result +--- +<< + { 'x': { 'owner': 'Scott', 'isBlocked': 'no' }, 'y': { 'name': 'Zembla' } }, + { + 'x': { 'owner': 'Aretha', 'isBlocked': 'no' }, + 'y': { 'name': 'Ankh-Morpork' } + }, + { 'x': { 'owner': 'Mike', 'isBlocked': 'no' }, 'y': { 'name': 'Zembla' } }, + { + 'x': { 'owner': 'Jay', 'isBlocked': 'yes' }, + 'y': { 'name': 'Ankh-Morpork' } + }, + { 'x': { 'owner': 'Charles', 'isBlocked': 'no' }, 'y': { 'name': 'Zembla' } }, + { + 'x': { 'owner': 'Scott', 'isBlocked': 'no' }, + 'e': { 'date': TIMESTAMP '2020-01-01 0:00:00.0', 'amount': 8000000.00 }, + 'y': { 'owner': 'Mike', 'isBlocked': 'no' } + }, + { + 'x': { 'owner': 'Mike', 'isBlocked': 'no' }, + 'e': { 'date': TIMESTAMP '2020-02-01 0:00:00.0', 'amount': 10000000.00 }, + 'y': { 'owner': 'Aretha', 'isBlocked': 'no' } + }, + { + 'x': { 'owner': 'Aretha', 'isBlocked': 'no' }, + 'e': { 'date': TIMESTAMP '2020-03-01 0:00:00.0', 'amount': 10000000.00 }, + 'y': { 'owner': 'Jay', 'isBlocked': 'yes' } + }, + { + 'x': { 'owner': 'Jay', 'isBlocked': 'yes' }, + 'e': { 'date': TIMESTAMP '2020-04-01 0:00:00.0', 'amount': 10000000.00 }, + 'y': { 'owner': 'Dave', 'isBlocked': 'no' } + }, + { + 'x': { 'owner': 'Dave', 'isBlocked': 'no' }, + 'e': { 'date': TIMESTAMP '2020-06-01 0:00:00.0', 'amount': 10000000.00 }, + 'y': { 'owner': 'Mike', 'isBlocked': 'no' } + }, + { + 'x': { 'owner': 'Dave', 'isBlocked': 'no' }, + 'e': { 'date': TIMESTAMP '2020-07-01 0:00:00.0', 'amount': 4000000.00 }, + 'y': { 'owner': 'Charles', 'isBlocked': 'no' } + }, + { + 'x': { 'owner': 'Mike', 'isBlocked': 'no' }, + 'e': { 'date': TIMESTAMP '2020-08-01 0:00:00.0', 'amount': 6000000.00 }, + 'y': { 'owner': 'Charles', 'isBlocked': 'no' } + }, + { + 'x': { 'owner': 'Charles', 'isBlocked': 'no' }, + 'e': { 'date': TIMESTAMP '2020-09-01 0:00:00.0', 'amount': 9000000.00 }, + 'y': { 'owner': 'Scott', 'isBlocked': 'no' } + } +>> diff --git a/partiql/tests/snapshots/graph__GPML LUR Triples.snap b/partiql/tests/snapshots/graph__GPML LUR Triples.snap new file mode 100644 index 00000000..ca2aafe2 --- /dev/null +++ b/partiql/tests/snapshots/graph__GPML LUR Triples.snap @@ -0,0 +1,172 @@ +--- +source: partiql/tests/graph.rs +expression: res.unwrap().result +--- +<< + { + 'x': { 'owner': 'Scott', 'isBlocked': 'no' }, + 'y': { 'number': '123.111', 'isBlocked': 'no' } + }, + { + 'x': { 'number': '123.111', 'isBlocked': 'no' }, + 'y': { 'owner': 'Scott', 'isBlocked': 'no' } + }, + { + 'x': { 'number': '123.222', 'isBlocked': 'no' }, + 'y': { 'owner': 'Charles', 'isBlocked': 'no' } + }, + { + 'x': { 'owner': 'Charles', 'isBlocked': 'no' }, + 'y': { 'number': '123.222', 'isBlocked': 'no' } + }, + { + 'x': { 'number': '111', 'isBlocked': 'no' }, + 'y': { 'owner': 'Scott', 'isBlocked': 'no' } + }, + { + 'x': { 'owner': 'Scott', 'isBlocked': 'no' }, + 'y': { 'number': '111', 'isBlocked': 'no' } + }, + { + 'x': { 'number': '222', 'isBlocked': 'no' }, + 'y': { 'owner': 'Aretha', 'isBlocked': 'no' } + }, + { + 'x': { 'owner': 'Aretha', 'isBlocked': 'no' }, + 'y': { 'number': '222', 'isBlocked': 'no' } + }, + { + 'x': { 'owner': 'Mike', 'isBlocked': 'no' }, + 'y': { 'number': '222', 'isBlocked': 'no' } + }, + { + 'x': { 'number': '222', 'isBlocked': 'no' }, + 'y': { 'owner': 'Mike', 'isBlocked': 'no' } + }, + { + 'x': { 'owner': 'Jay', 'isBlocked': 'yes' }, + 'y': { 'number': '333', 'isBlocked': 'no' } + }, + { + 'x': { 'number': '333', 'isBlocked': 'no' }, + 'y': { 'owner': 'Jay', 'isBlocked': 'yes' } + }, + { + 'x': { 'number': '111', 'isBlocked': 'no' }, + 'y': { 'owner': 'Charles', 'isBlocked': 'no' } + }, + { + 'x': { 'owner': 'Charles', 'isBlocked': 'no' }, + 'y': { 'number': '111', 'isBlocked': 'no' } + }, + { + 'x': { 'number': '444', 'isBlocked': 'no' }, + 'y': { 'owner': 'Dave', 'isBlocked': 'no' } + }, + { + 'x': { 'owner': 'Dave', 'isBlocked': 'no' }, + 'y': { 'number': '444', 'isBlocked': 'no' } + }, + { 'x': { 'owner': 'Scott', 'isBlocked': 'no' }, 'y': { 'name': 'Zembla' } }, + { 'x': { 'name': 'Zembla' }, 'y': { 'owner': 'Scott', 'isBlocked': 'no' } }, + { + 'x': { 'owner': 'Aretha', 'isBlocked': 'no' }, + 'y': { 'name': 'Ankh-Morpork' } + }, + { + 'x': { 'name': 'Ankh-Morpork' }, + 'y': { 'owner': 'Aretha', 'isBlocked': 'no' } + }, + { 'x': { 'owner': 'Mike', 'isBlocked': 'no' }, 'y': { 'name': 'Zembla' } }, + { 'x': { 'name': 'Zembla' }, 'y': { 'owner': 'Mike', 'isBlocked': 'no' } }, + { + 'x': { 'owner': 'Jay', 'isBlocked': 'yes' }, + 'y': { 'name': 'Ankh-Morpork' } + }, + { + 'x': { 'name': 'Ankh-Morpork' }, + 'y': { 'owner': 'Jay', 'isBlocked': 'yes' } + }, + { 'x': { 'owner': 'Charles', 'isBlocked': 'no' }, 'y': { 'name': 'Zembla' } }, + { 'x': { 'name': 'Zembla' }, 'y': { 'owner': 'Charles', 'isBlocked': 'no' } }, + { + 'x': { 'owner': 'Scott', 'isBlocked': 'no' }, + 'e': { 'date': TIMESTAMP '2020-01-01 0:00:00.0', 'amount': 8000000.00 }, + 'y': { 'owner': 'Mike', 'isBlocked': 'no' } + }, + { + 'x': { 'owner': 'Mike', 'isBlocked': 'no' }, + 'e': { 'date': TIMESTAMP '2020-01-01 0:00:00.0', 'amount': 8000000.00 }, + 'y': { 'owner': 'Scott', 'isBlocked': 'no' } + }, + { + 'x': { 'owner': 'Mike', 'isBlocked': 'no' }, + 'e': { 'date': TIMESTAMP '2020-02-01 0:00:00.0', 'amount': 10000000.00 }, + 'y': { 'owner': 'Aretha', 'isBlocked': 'no' } + }, + { + 'x': { 'owner': 'Aretha', 'isBlocked': 'no' }, + 'e': { 'date': TIMESTAMP '2020-02-01 0:00:00.0', 'amount': 10000000.00 }, + 'y': { 'owner': 'Mike', 'isBlocked': 'no' } + }, + { + 'x': { 'owner': 'Aretha', 'isBlocked': 'no' }, + 'e': { 'date': TIMESTAMP '2020-03-01 0:00:00.0', 'amount': 10000000.00 }, + 'y': { 'owner': 'Jay', 'isBlocked': 'yes' } + }, + { + 'x': { 'owner': 'Jay', 'isBlocked': 'yes' }, + 'e': { 'date': TIMESTAMP '2020-03-01 0:00:00.0', 'amount': 10000000.00 }, + 'y': { 'owner': 'Aretha', 'isBlocked': 'no' } + }, + { + 'x': { 'owner': 'Jay', 'isBlocked': 'yes' }, + 'e': { 'date': TIMESTAMP '2020-04-01 0:00:00.0', 'amount': 10000000.00 }, + 'y': { 'owner': 'Dave', 'isBlocked': 'no' } + }, + { + 'x': { 'owner': 'Dave', 'isBlocked': 'no' }, + 'e': { 'date': TIMESTAMP '2020-04-01 0:00:00.0', 'amount': 10000000.00 }, + 'y': { 'owner': 'Jay', 'isBlocked': 'yes' } + }, + { + 'x': { 'owner': 'Dave', 'isBlocked': 'no' }, + 'e': { 'date': TIMESTAMP '2020-06-01 0:00:00.0', 'amount': 10000000.00 }, + 'y': { 'owner': 'Mike', 'isBlocked': 'no' } + }, + { + 'x': { 'owner': 'Mike', 'isBlocked': 'no' }, + 'e': { 'date': TIMESTAMP '2020-06-01 0:00:00.0', 'amount': 10000000.00 }, + 'y': { 'owner': 'Dave', 'isBlocked': 'no' } + }, + { + 'x': { 'owner': 'Dave', 'isBlocked': 'no' }, + 'e': { 'date': TIMESTAMP '2020-07-01 0:00:00.0', 'amount': 4000000.00 }, + 'y': { 'owner': 'Charles', 'isBlocked': 'no' } + }, + { + 'x': { 'owner': 'Charles', 'isBlocked': 'no' }, + 'e': { 'date': TIMESTAMP '2020-07-01 0:00:00.0', 'amount': 4000000.00 }, + 'y': { 'owner': 'Dave', 'isBlocked': 'no' } + }, + { + 'x': { 'owner': 'Mike', 'isBlocked': 'no' }, + 'e': { 'date': TIMESTAMP '2020-08-01 0:00:00.0', 'amount': 6000000.00 }, + 'y': { 'owner': 'Charles', 'isBlocked': 'no' } + }, + { + 'x': { 'owner': 'Charles', 'isBlocked': 'no' }, + 'e': { 'date': TIMESTAMP '2020-08-01 0:00:00.0', 'amount': 6000000.00 }, + 'y': { 'owner': 'Mike', 'isBlocked': 'no' } + }, + { + 'x': { 'owner': 'Charles', 'isBlocked': 'no' }, + 'e': { 'date': TIMESTAMP '2020-09-01 0:00:00.0', 'amount': 9000000.00 }, + 'y': { 'owner': 'Scott', 'isBlocked': 'no' } + }, + { + 'x': { 'owner': 'Scott', 'isBlocked': 'no' }, + 'e': { 'date': TIMESTAMP '2020-09-01 0:00:00.0', 'amount': 9000000.00 }, + 'y': { 'owner': 'Charles', 'isBlocked': 'no' } + } +>> diff --git a/partiql/tests/snapshots/graph__GPML Nodes.snap b/partiql/tests/snapshots/graph__GPML Nodes.snap new file mode 100644 index 00000000..422aaff7 --- /dev/null +++ b/partiql/tests/snapshots/graph__GPML Nodes.snap @@ -0,0 +1,20 @@ +--- +source: partiql/tests/graph.rs +expression: res.unwrap().result +--- +<< + { 'x': { 'owner': 'Scott', 'isBlocked': 'no' } }, + { 'x': { 'owner': 'Aretha', 'isBlocked': 'no' } }, + { 'x': { 'owner': 'Mike', 'isBlocked': 'no' } }, + { 'x': { 'owner': 'Jay', 'isBlocked': 'yes' } }, + { 'x': { 'owner': 'Charles', 'isBlocked': 'no' } }, + { 'x': { 'owner': 'Dave', 'isBlocked': 'no' } }, + { 'x': { 'name': 'Zembla' } }, + { 'x': { 'name': 'Ankh-Morpork' } }, + { 'x': { 'number': '123.111', 'isBlocked': 'no' } }, + { 'x': { 'number': '123.222', 'isBlocked': 'no' } }, + { 'x': { 'number': '111', 'isBlocked': 'no' } }, + { 'x': { 'number': '222', 'isBlocked': 'no' } }, + { 'x': { 'number': '333', 'isBlocked': 'no' } }, + { 'x': { 'number': '444', 'isBlocked': 'no' } } +>> diff --git a/partiql/tests/snapshots/graph__RFC0025 L Triples.snap b/partiql/tests/snapshots/graph__RFC0025 L Triples.snap new file mode 100644 index 00000000..c4de43b2 --- /dev/null +++ b/partiql/tests/snapshots/graph__RFC0025 L Triples.snap @@ -0,0 +1,5 @@ +--- +source: partiql/tests/graph.rs +expression: res.unwrap().result +--- +<< { }, { }, { } >> diff --git a/partiql/tests/snapshots/graph__RFC0025 LUR Triples.snap b/partiql/tests/snapshots/graph__RFC0025 LUR Triples.snap new file mode 100644 index 00000000..fe1c0093 --- /dev/null +++ b/partiql/tests/snapshots/graph__RFC0025 LUR Triples.snap @@ -0,0 +1,5 @@ +--- +source: partiql/tests/graph.rs +expression: res.unwrap().result +--- +<< { }, { }, { }, { }, { }, { } >> diff --git a/partiql/tests/snapshots/graph__RFC0025 Nodes.snap b/partiql/tests/snapshots/graph__RFC0025 Nodes.snap new file mode 100644 index 00000000..c4de43b2 --- /dev/null +++ b/partiql/tests/snapshots/graph__RFC0025 Nodes.snap @@ -0,0 +1,5 @@ +--- +source: partiql/tests/graph.rs +expression: res.unwrap().result +--- +<< { }, { }, { } >> diff --git a/partiql/tests/snapshots/pretty__graph_filters_1.snap b/partiql/tests/snapshots/pretty__graph_filters_1.snap index 1728cc9b..552861a1 100644 --- a/partiql/tests/snapshots/pretty__graph_filters_1.snap +++ b/partiql/tests/snapshots/pretty__graph_filters_1.snap @@ -3,51 +3,51 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT u as banCandidate FROM g MATCH (p:Post Where p.isFlagged = true) <-[:createdPost]- (u:User WHERE u.isBanned = false AND u.karma < 20) -[:createdComment]->(c:Comment WHERE c.isFlagged = true) WHERE p.title LIKE '%considered harmful%' +SELECT u as banCandidate + FROM (g MATCH + (p:Post Where p.isFlagged = true) + <-[:createdPost]- + (u:User WHERE u.isBanned = false AND u.karma < 20) + -[:createdComment]-> + (c:Comment WHERE c.isFlagged = true) + WHERE p.title LIKE '%considered harmful%') ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT u AS banCandidate FROM g MATCH (p:Post WHERE (p.isFlagged = true)) <-[:createdPost]- (u:User WHERE ((u.isBanned = false) AND - (u.karma < 20))) -[:createdComment]-> (c:Comment WHERE (c.isFlagged = true)) -WHERE p.title LIKE '%considered harmful%' +SELECT u AS banCandidate FROM (g MATCH (p:Post WHERE (p.isFlagged = true)) <-[:createdPost]- (u:User WHERE ((u.isBanned = false) AND + (u.karma < 20))) -[:createdComment]-> (c:Comment WHERE (c.isFlagged = true)) WHERE p.title LIKE '%considered harmful%') ------------------------------------------------------------------------------------------------------------------------ SELECT u AS banCandidate -FROM g MATCH (p:Post WHERE (p.isFlagged = true)) <-[:createdPost]- (u:User WHERE ((u.isBanned = false) AND - (u.karma < 20))) -[:createdComment]-> (c:Comment WHERE (c.isFlagged = true)) -WHERE p.title LIKE '%considered harmful%' +FROM (g MATCH (p:Post WHERE (p.isFlagged = true)) <-[:createdPost]- (u:User WHERE ((u.isBanned = false) AND + (u.karma < 20))) -[:createdComment]-> (c:Comment WHERE (c.isFlagged = true)) WHERE p.title LIKE '%considered harmful%') -------------------------------------------------------------------------------- SELECT u AS banCandidate -FROM g MATCH (p:Post WHERE (p.isFlagged = true)) <-[:createdPost]- (u:User WHERE ((u.isBanned = false) - AND - (u.karma < 20))) -[:createdComment]-> (c:Comment WHERE (c.isFlagged = true)) -WHERE p.title LIKE '%considered harmful%' +FROM (g MATCH (p:Post WHERE (p.isFlagged = true)) <-[:createdPost]- (u:User WHERE ((u.isBanned = false) + AND + (u.karma < 20))) -[:createdComment]-> (c:Comment WHERE (c.isFlagged = true)) WHERE p.title LIKE '%considered harmful%') ---------------------------------------- SELECT u AS banCandidate -FROM g MATCH (p:Post WHERE (p.isFlagged = true)) <-[:createdPost]- (u:User WHERE ((u.isBanned = false) - AND - (u.karma < 20))) -[:createdComment]-> (c:Comment WHERE (c.isFlagged = true)) -WHERE p.title LIKE '%considered harmful%' +FROM (g MATCH (p:Post WHERE (p.isFlagged = true)) <-[:createdPost]- (u:User WHERE ((u.isBanned = false) + AND + (u.karma < 20))) -[:createdComment]-> (c:Comment WHERE (c.isFlagged = true)) WHERE p.title LIKE '%considered harmful%') ------------------------------ SELECT u AS banCandidate -FROM g MATCH (p:Post WHERE (p.isFlagged = true)) <-[:createdPost]- (u:User WHERE ((u.isBanned = false) - AND - (u.karma < 20))) -[:createdComment]-> (c:Comment WHERE (c.isFlagged = true)) -WHERE p.title LIKE '%considered harmful%' +FROM (g MATCH (p:Post WHERE (p.isFlagged = true)) <-[:createdPost]- (u:User WHERE ((u.isBanned = false) + AND + (u.karma < 20))) -[:createdComment]-> (c:Comment WHERE (c.isFlagged = true)) WHERE p.title LIKE '%considered harmful%') -------------------- SELECT u AS banCandidate -FROM g MATCH (p:Post WHERE (p.isFlagged = true)) <-[:createdPost]- (u:User WHERE ((u.isBanned = false) - AND - (u.karma < 20))) -[:createdComment]-> (c:Comment WHERE (c.isFlagged = true)) -WHERE p.title LIKE '%considered harmful%' +FROM (g MATCH (p:Post WHERE (p.isFlagged = true)) <-[:createdPost]- (u:User WHERE ((u.isBanned = false) + AND + (u.karma < 20))) -[:createdComment]-> (c:Comment WHERE (c.isFlagged = true)) WHERE p.title LIKE '%considered harmful%') ---------- SELECT u AS banCandidate -FROM g MATCH (p:Post WHERE (p.isFlagged = true)) <-[:createdPost]- (u:User WHERE ((u.isBanned = false) - AND - (u.karma < 20))) -[:createdComment]-> (c:Comment WHERE (c.isFlagged = true)) -WHERE p.title LIKE '%considered harmful%' +FROM (g MATCH (p:Post WHERE (p.isFlagged = true)) <-[:createdPost]- (u:User WHERE ((u.isBanned = false) + AND + (u.karma < 20))) -[:createdComment]-> (c:Comment WHERE (c.isFlagged = true)) WHERE p.title LIKE '%considered harmful%') diff --git a/partiql/tests/snapshots/pretty__graph_filters_2.snap b/partiql/tests/snapshots/pretty__graph_filters_2.snap new file mode 100644 index 00000000..8255712d --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_filters_2.snap @@ -0,0 +1,61 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT u as banCandidate + FROM (g MATCH + (p:Post Where p.isFlagged = true) + <-[:createdPost]- + (u:User WHERE u.isBanned = false AND u.karma < 20) + -[:createdComment]-> + (c:Comment WHERE c.isFlagged = true) + ) + WHERE p.title LIKE '%considered harmful%' +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT u AS banCandidate FROM (g MATCH (p:Post WHERE (p.isFlagged = true)) <-[:createdPost]- (u:User WHERE ((u.isBanned = false) AND + (u.karma < 20))) -[:createdComment]-> (c:Comment WHERE (c.isFlagged = true))) +WHERE p.title LIKE '%considered harmful%' + +------------------------------------------------------------------------------------------------------------------------ +SELECT u AS banCandidate +FROM (g MATCH (p:Post WHERE (p.isFlagged = true)) <-[:createdPost]- (u:User WHERE ((u.isBanned = false) AND + (u.karma < 20))) -[:createdComment]-> (c:Comment WHERE (c.isFlagged = true))) +WHERE p.title LIKE '%considered harmful%' + +-------------------------------------------------------------------------------- +SELECT u AS banCandidate +FROM (g MATCH (p:Post WHERE (p.isFlagged = true)) <-[:createdPost]- (u:User WHERE ((u.isBanned = false) + AND + (u.karma < 20))) -[:createdComment]-> (c:Comment WHERE (c.isFlagged = true))) +WHERE p.title LIKE '%considered harmful%' + +---------------------------------------- +SELECT u AS banCandidate +FROM (g MATCH (p:Post WHERE (p.isFlagged = true)) <-[:createdPost]- (u:User WHERE ((u.isBanned = false) + AND + (u.karma < 20))) -[:createdComment]-> (c:Comment WHERE (c.isFlagged = true))) +WHERE p.title LIKE '%considered harmful%' + +------------------------------ +SELECT u AS banCandidate +FROM (g MATCH (p:Post WHERE (p.isFlagged = true)) <-[:createdPost]- (u:User WHERE ((u.isBanned = false) + AND + (u.karma < 20))) -[:createdComment]-> (c:Comment WHERE (c.isFlagged = true))) +WHERE p.title LIKE '%considered harmful%' + +-------------------- +SELECT u AS banCandidate +FROM (g MATCH (p:Post WHERE (p.isFlagged = true)) <-[:createdPost]- (u:User WHERE ((u.isBanned = false) + AND + (u.karma < 20))) -[:createdComment]-> (c:Comment WHERE (c.isFlagged = true))) +WHERE p.title LIKE '%considered harmful%' + +---------- +SELECT u AS banCandidate +FROM (g MATCH (p:Post WHERE (p.isFlagged = true)) <-[:createdPost]- (u:User WHERE ((u.isBanned = false) + AND + (u.karma < 20))) -[:createdComment]-> (c:Comment WHERE (c.isFlagged = true))) +WHERE p.title LIKE '%considered harmful%' diff --git a/partiql/tests/snapshots/pretty__graph_match_and_join_1.snap b/partiql/tests/snapshots/pretty__graph_match_and_join_1.snap index 6941298e..93b24722 100644 --- a/partiql/tests/snapshots/pretty__graph_match_and_join_1.snap +++ b/partiql/tests/snapshots/pretty__graph_match_and_join_1.snap @@ -3,29 +3,29 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT a,b,c, t1.x as x, t2.y as y FROM (graph MATCH (a) -> (b), (a) -> (c)), table1 as t1, table2 as t2 +SELECT a,b,c, t1.x as x, t2.y as y FROM GRAPH_TABLE (g MATCH (a) -> (b), (a) -> (c)), table1 as t1, table2 as t2 ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT a, b, c, t1.x AS x, t2.y AS y FROM (graph MATCH (a) -> (b), (a) -> (c)), table1 AS t1, table2 AS t2 +SELECT a, b, c, t1.x AS x, t2.y AS y FROM GRAPH_TABLE (g MATCH (a) -> (b), (a) -> (c)), table1 AS t1, table2 AS t2 ------------------------------------------------------------------------------------------------------------------------ -SELECT a, b, c, t1.x AS x, t2.y AS y FROM (graph MATCH (a) -> (b), (a) -> (c)), table1 AS t1, table2 AS t2 +SELECT a, b, c, t1.x AS x, t2.y AS y FROM GRAPH_TABLE (g MATCH (a) -> (b), (a) -> (c)), table1 AS t1, table2 AS t2 -------------------------------------------------------------------------------- -SELECT a, b, c, t1.x AS x, t2.y AS y FROM (graph MATCH (a) -> (b), (a) -> (c)), - table1 AS t1, table2 AS t2 +SELECT a, b, c, t1.x AS x, t2.y AS y FROM GRAPH_TABLE (g MATCH (a) -> (b), + (a) -> (c)), table1 AS t1, table2 AS t2 ---------------------------------------- SELECT a, b, c, t1.x AS x, t2.y AS y -FROM (graph MATCH (a) -> (b), +FROM GRAPH_TABLE (g MATCH (a) -> (b), (a) -> (c)), table1 AS t1, table2 AS t2 ------------------------------ SELECT a, b, c, t1.x AS x, t2.y AS y -FROM (graph MATCH (a) -> (b), +FROM GRAPH_TABLE (g MATCH (a) -> (b), (a) -> (c)), table1 AS t1, table2 AS t2 @@ -33,7 +33,7 @@ FROM (graph MATCH (a) -> (b), SELECT a, b, c, t1.x AS x, t2.y AS y -FROM (graph MATCH (a) -> (b), +FROM GRAPH_TABLE (g MATCH (a) -> (b), (a) -> (c)), table1 AS t1, table2 AS t2 @@ -43,7 +43,7 @@ SELECT a, b, c, t1.x AS x, t2.y AS y -FROM (graph MATCH (a) -> (b), +FROM GRAPH_TABLE (g MATCH (a) -> (b), (a) -> (c)), table1 AS t1, table2 AS t2 diff --git a/partiql/tests/snapshots/pretty__graph_match_and_join_2.snap b/partiql/tests/snapshots/pretty__graph_match_and_join_2.snap new file mode 100644 index 00000000..4daa55c3 --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_match_and_join_2.snap @@ -0,0 +1,50 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT a,b,c, t1.x as x, t2.y as y FROM table1 as t1, table2 as t2, GRAPH_TABLE (g MATCH (a) -> (b), (a) -> (c)) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT a, b, c, t1.x AS x, t2.y AS y FROM table1 AS t1, table2 AS t2, GRAPH_TABLE (g MATCH (a) -> (b), (a) -> (c)) + +------------------------------------------------------------------------------------------------------------------------ +SELECT a, b, c, t1.x AS x, t2.y AS y FROM table1 AS t1, table2 AS t2, GRAPH_TABLE (g MATCH (a) -> (b), (a) -> (c)) + +-------------------------------------------------------------------------------- +SELECT a, b, c, t1.x AS x, t2.y AS y FROM table1 AS t1, table2 AS t2, + GRAPH_TABLE (g MATCH (a) -> (b), (a) -> (c)) + +---------------------------------------- +SELECT a, b, c, t1.x AS x, t2.y AS y +FROM table1 AS t1, table2 AS t2, + GRAPH_TABLE (g MATCH (a) -> (b), + (a) -> (c)) + +------------------------------ +SELECT a, b, c, t1.x AS x, + t2.y AS y +FROM table1 AS t1, + table2 AS t2, + GRAPH_TABLE (g MATCH (a) -> (b), + (a) -> (c)) + +-------------------- +SELECT a, b, c, + t1.x AS x, + t2.y AS y +FROM table1 AS t1, + table2 AS t2, + GRAPH_TABLE (g MATCH (a) -> (b), + (a) -> (c)) + +---------- +SELECT a, + b, c, + t1.x AS x, + t2.y AS y +FROM table1 AS t1, + table2 AS t2, + GRAPH_TABLE (g MATCH (a) -> (b), + (a) -> (c)) diff --git a/partiql/tests/snapshots/pretty__graph_patterns_1.snap b/partiql/tests/snapshots/pretty__graph_patterns_1.snap index ad81d1f6..80e789d7 100644 --- a/partiql/tests/snapshots/pretty__graph_patterns_1.snap +++ b/partiql/tests/snapshots/pretty__graph_patterns_1.snap @@ -3,40 +3,36 @@ source: partiql/tests/pretty.rs expression: doc --- ======================================================================================================================================================================================================== -SELECT the_a.name AS src, the_b.name AS dest FROM my_graph MATCH (the_a:a) -[the_y:y]-> (the_b:b) WHERE the_y.score > 10 +SELECT the_a.name AS src, the_b.name AS dest FROM (my_graph MATCH (the_a:a) -[the_y:y]-> (the_b:b) WHERE the_y.score > 10) ======================================================================================================================================================================================================== ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -SELECT the_a.name AS src, the_b.name AS dest FROM my_graph MATCH (the_a:a) -[the_y:y]-> (the_b:b) WHERE (the_y.score > 10) +SELECT the_a.name AS src, the_b.name AS dest FROM (my_graph MATCH (the_a:a) -[the_y:y]-> (the_b:b) WHERE (the_y.score > 10)) ------------------------------------------------------------------------------------------------------------------------ -SELECT the_a.name AS src, the_b.name AS dest FROM my_graph MATCH (the_a:a) -[the_y:y]-> (the_b:b) -WHERE (the_y.score > 10) +SELECT the_a.name AS src, the_b.name AS dest +FROM (my_graph MATCH (the_a:a) -[the_y:y]-> (the_b:b) WHERE (the_y.score > 10)) -------------------------------------------------------------------------------- SELECT the_a.name AS src, the_b.name AS dest -FROM my_graph MATCH (the_a:a) -[the_y:y]-> (the_b:b) WHERE (the_y.score > 10) +FROM (my_graph MATCH (the_a:a) -[the_y:y]-> (the_b:b) WHERE (the_y.score > 10)) ---------------------------------------- SELECT the_a.name AS src, the_b.name AS dest -FROM my_graph MATCH (the_a:a) -[the_y:y]-> (the_b:b) -WHERE (the_y.score > 10) +FROM (my_graph MATCH (the_a:a) -[the_y:y]-> (the_b:b) WHERE (the_y.score > 10)) ------------------------------ SELECT the_a.name AS src, the_b.name AS dest -FROM my_graph MATCH (the_a:a) -[the_y:y]-> (the_b:b) -WHERE (the_y.score > 10) +FROM (my_graph MATCH (the_a:a) -[the_y:y]-> (the_b:b) WHERE (the_y.score > 10)) -------------------- SELECT the_a.name AS src, the_b.name AS dest -FROM my_graph MATCH (the_a:a) -[the_y:y]-> (the_b:b) -WHERE (the_y.score > 10) +FROM (my_graph MATCH (the_a:a) -[the_y:y]-> (the_b:b) WHERE (the_y.score > 10)) ---------- SELECT the_a.name AS src, the_b.name AS dest -FROM my_graph MATCH (the_a:a) -[the_y:y]-> (the_b:b) -WHERE (the_y.score > 10) +FROM (my_graph MATCH (the_a:a) -[the_y:y]-> (the_b:b) WHERE (the_y.score > 10)) From cced91036759fa30a2b264d71c057f69bf0946f0 Mon Sep 17 00:00:00 2001 From: Josh Pschorr Date: Fri, 4 Apr 2025 15:18:08 -0700 Subject: [PATCH 5/9] Clippy & cleanup --- partiql-ast/src/pretty.rs | 27 ++++++++++++------------ partiql-logical-planner/src/graph.rs | 15 ++++++------- partiql-logical-planner/src/lower.rs | 2 +- partiql-parser/src/parse/partiql.lalrpop | 4 ++-- partiql/tests/graph.rs | 6 +----- 5 files changed, 24 insertions(+), 30 deletions(-) diff --git a/partiql-ast/src/pretty.rs b/partiql-ast/src/pretty.rs index 0d80c6ea..674bc72b 100644 --- a/partiql-ast/src/pretty.rs +++ b/partiql-ast/src/pretty.rs @@ -6,7 +6,6 @@ use partiql_common::pretty::{ }; use pretty::{DocAllocator, DocBuilder}; use std::borrow::Cow; -use std::num::NonZeroU32; impl PrettyDoc for AstNode where @@ -1021,7 +1020,7 @@ impl PrettyDoc for GraphMatch { shape.export.as_ref().map(|d| d.pretty_doc(arena)), ] .into_iter() - .filter_map(|x| x) + .flatten() .collect::>(); if !shapes.is_empty() { let docs = std::iter::once(match_expr).chain(shapes); @@ -1033,7 +1032,7 @@ impl PrettyDoc for GraphMatch { } impl PrettyDoc for GraphTableRows { - fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + fn pretty_doc<'b, D, A>(&'b self, _arena: &'b D) -> DocBuilder<'b, D, A> where D: DocAllocator<'b, A>, D::Doc: Clone, @@ -1044,7 +1043,7 @@ impl PrettyDoc for GraphTableRows { } impl PrettyDoc for GraphTableColumns { - fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + fn pretty_doc<'b, D, A>(&'b self, _arena: &'b D) -> DocBuilder<'b, D, A> where D: DocAllocator<'b, A>, D::Doc: Clone, @@ -1055,7 +1054,7 @@ impl PrettyDoc for GraphTableColumns { } impl PrettyDoc for GraphTableExport { - fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + fn pretty_doc<'b, D, A>(&'b self, _arena: &'b D) -> DocBuilder<'b, D, A> where D: DocAllocator<'b, A>, D::Doc: Clone, @@ -1083,7 +1082,7 @@ impl PrettyDoc for GraphPattern { mode.as_ref().map(|inner| inner.pretty_doc(arena)), Some(patterns), keep.as_ref().map(|inner| inner.pretty_doc(arena)), - self.where_clause.as_ref().map(|clause| { + where_clause.as_ref().map(|clause| { arena.intersperse( [arena.text("WHERE"), clause.pretty_doc(arena)], arena.space(), @@ -1091,7 +1090,7 @@ impl PrettyDoc for GraphPattern { }), ] .into_iter() - .filter_map(|x| x); + .flatten(); arena.intersperse(parts, arena.space()) } @@ -1149,7 +1148,7 @@ impl PrettyDoc for GraphPathPrefix { ], } .into_iter() - .filter_map(|x| x) + .flatten() .collect::>(); arena.intersperse(parts, arena.space()) } @@ -1190,7 +1189,7 @@ impl PrettyDoc for GraphPathPattern { let parts = [var, prefix, Some(self.path.pretty_doc(arena))] .into_iter() - .filter_map(|x| x); + .flatten(); arena.intersperse(parts, arena.space()) } } @@ -1217,7 +1216,7 @@ impl PrettyDoc for GraphPathSubPattern { let parts = [var, mode, Some(self.path.pretty_doc(arena)), where_clause] .into_iter() - .filter_map(|x| x); + .flatten(); arena.intersperse(parts, arena.space()) } } @@ -1258,7 +1257,7 @@ impl PrettyDoc for GraphMatchNode { .map(|label| arena.concat([arena.text(":"), label.pretty_doc(arena)])), ] .into_iter() - .filter_map(|x| x) + .flatten() .collect(); let name = if name.is_empty() { None @@ -1271,7 +1270,7 @@ impl PrettyDoc for GraphMatchNode { arena.space(), ) }); - let parts = [name, where_clause].into_iter().filter_map(|x| x); + let parts = [name, where_clause].into_iter().flatten(); let spec = arena.intersperse(parts, arena.space()); pretty_surrounded_doc(spec, "(", ")", arena).group() @@ -1292,7 +1291,7 @@ impl PrettyDoc for GraphMatchEdge { .map(|label| arena.concat([arena.text(":"), label.pretty_doc(arena)])), ] .into_iter() - .filter_map(|x| x) + .flatten() .collect(); let name = if name.is_empty() { None @@ -1307,7 +1306,7 @@ impl PrettyDoc for GraphMatchEdge { }); let parts = [name, where_clause] .into_iter() - .filter_map(|x| x) + .flatten() .collect::>(); let mut edge = if !parts.is_empty() { diff --git a/partiql-logical-planner/src/graph.rs b/partiql-logical-planner/src/graph.rs index 0e8a61d9..a6207bd4 100644 --- a/partiql-logical-planner/src/graph.rs +++ b/partiql-logical-planner/src/graph.rs @@ -6,7 +6,6 @@ use partiql_logical::graph::{ BindSpec, DirectionFilter, EdgeFilter, EdgeMatch, LabelFilter, NodeFilter, NodeMatch, PathMatch, PathPattern, PathPatternMatch, StepFilter, TripleFilter, ValueFilter, }; -use petgraph::visit::Walker; use std::mem::take; #[macro_export] @@ -249,24 +248,24 @@ impl GraphToLogical { Ok(path?.into_iter().flatten().collect()) } GraphMatchPathPattern::Union(_) => { - not_yet_implemented_result!("MATCH expression UNION is not yet supported.") + not_yet_implemented_result!("MATCH expression UNION is not yet supported."); } GraphMatchPathPattern::Multiset(_) => { - not_yet_implemented_result!("MATCH expression MULTISET is not yet supported.") + not_yet_implemented_result!("MATCH expression MULTISET is not yet supported."); } GraphMatchPathPattern::Questioned(_) => { - not_yet_implemented_result!("MATCH expression QUESTIONED is not yet supported.") + not_yet_implemented_result!("MATCH expression QUESTIONED is not yet supported."); } GraphMatchPathPattern::Quantified(_, _) => { - not_yet_implemented_result!("MATCH expression QUANTIFIED is not yet supported.") + not_yet_implemented_result!("MATCH expression QUANTIFIED is not yet supported."); } GraphMatchPathPattern::Sub(subpath) => self.plan_graph_subpath_pattern(subpath), - GraphMatchPathPattern::Node(n) => self.plan_graph_pattern_part_node(&n), - GraphMatchPathPattern::Edge(e) => self.plan_graph_pattern_part_edge(&e), + GraphMatchPathPattern::Node(n) => self.plan_graph_pattern_part_node(n), + GraphMatchPathPattern::Edge(e) => self.plan_graph_pattern_part_edge(e), GraphMatchPathPattern::Simplified(_) => { not_yet_implemented_result!( "MATCH expression Simplified Edge Expressions are not yet supported." - ) + ); } } } diff --git a/partiql-logical-planner/src/lower.rs b/partiql-logical-planner/src/lower.rs index 7335b605..8ee0b42e 100644 --- a/partiql-logical-planner/src/lower.rs +++ b/partiql-logical-planner/src/lower.rs @@ -1911,7 +1911,7 @@ impl<'ast> Visitor<'ast> for AstToLogical<'_> { let graph_reference = Box::new(env.pop().unwrap()); let graph_planner = crate::graph::GraphToLogical::default(); - match graph_planner.plan_graph_match(&graph_match) { + match graph_planner.plan_graph_match(graph_match) { Ok(pattern) => { self.push_vexpr(ValueExpr::GraphMatch(GraphMatchExpr { value: graph_reference, diff --git a/partiql-parser/src/parse/partiql.lalrpop b/partiql-parser/src/parse/partiql.lalrpop index 56e74dac..a29bf4fc 100644 --- a/partiql-parser/src/parse/partiql.lalrpop +++ b/partiql-parser/src/parse/partiql.lalrpop @@ -1924,14 +1924,14 @@ PuncSepStar: Vec = { // This is a macro; see http://lalrpop.github.io/lalrpop/tutorial/006_macros.html PuncSepPlus: Vec = { -

)*> => { +

)*> => { v.into_iter().map(|t|t.0).chain(std::iter::once(e)).collect() } } // This is a macro; see http://lalrpop.github.io/lalrpop/tutorial/006_macros.html PuncSep2Plus: Vec = { -

)+> => { +

)+> => { v.into_iter().map(|t|t.0).chain(std::iter::once(e)).collect() } } diff --git a/partiql/tests/graph.rs b/partiql/tests/graph.rs index 7e3a71d7..d8d2c5e7 100644 --- a/partiql/tests/graph.rs +++ b/partiql/tests/graph.rs @@ -1,15 +1,11 @@ use insta::assert_snapshot; use partiql_catalog::catalog::PartiqlCatalog; -use partiql_catalog::context::SystemContext; -use partiql_common::pretty::ToPretty; -use partiql_eval::eval::graph::string_graph::StringGraphTypes; -use partiql_eval::eval::BasicContext; use partiql_eval::plan::EvaluationMode; use partiql_extension_ion::decode::{IonDecodeResult, IonDecoderBuilder, IonDecoderConfig}; use crate::common::{compile, evaluate, lower, parse}; use partiql_extension_ion::Encoding; -use partiql_value::{tuple, DateTime, Value}; +use partiql_value::{tuple, Value}; mod common; From 6090fb9f7b9110eae72691ade4c1d467c2dc8133 Mon Sep 17 00:00:00 2001 From: Josh Pschorr Date: Fri, 4 Apr 2025 13:51:28 -0700 Subject: [PATCH 6/9] Update to latest conformance tests --- partiql-conformance-tests/partiql-tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/partiql-conformance-tests/partiql-tests b/partiql-conformance-tests/partiql-tests index f7d42c41..25cad011 160000 --- a/partiql-conformance-tests/partiql-tests +++ b/partiql-conformance-tests/partiql-tests @@ -1 +1 @@ -Subproject commit f7d42c414f14f9efe23047883e8c3856d926f8e0 +Subproject commit 25cad011aa27e1439b48e6df6b213f1157760225 From e29316fadc9a40f69003e9914f9928e00124f936 Mon Sep 17 00:00:00 2001 From: Josh Pschorr Date: Fri, 4 Apr 2025 22:15:55 -0700 Subject: [PATCH 7/9] Additional parse/lex tests --- partiql-ast/src/pretty.rs | 118 +++++++++++++- partiql-common/src/pretty.rs | 12 ++ partiql-parser/Cargo.toml | 1 + partiql-parser/src/lexer/mod.rs | 40 +++-- ...partiql_parser__lexer__tests__display.snap | 150 ++++++++++++++++++ partiql/tests/graph.rs | 36 ++++- partiql/tests/pretty.rs | 92 +++++++++++ .../snapshots/pretty__graph_shapes_1.snap | 38 +++++ .../snapshots/pretty__graph_shapes_10.snap | 38 +++++ .../snapshots/pretty__graph_shapes_11.snap | 54 +++++++ .../snapshots/pretty__graph_shapes_12.snap | 54 +++++++ .../snapshots/pretty__graph_shapes_13.snap | 53 +++++++ .../snapshots/pretty__graph_shapes_2.snap | 40 +++++ .../snapshots/pretty__graph_shapes_3.snap | 42 +++++ .../snapshots/pretty__graph_shapes_4.snap | 41 +++++ .../snapshots/pretty__graph_shapes_5.snap | 44 +++++ .../snapshots/pretty__graph_shapes_6.snap | 41 +++++ .../snapshots/pretty__graph_shapes_7.snap | 38 +++++ .../snapshots/pretty__graph_shapes_8.snap | 41 +++++ .../snapshots/pretty__graph_shapes_9.snap | 40 +++++ 20 files changed, 979 insertions(+), 34 deletions(-) create mode 100644 partiql-parser/src/lexer/snapshots/partiql_parser__lexer__tests__display.snap create mode 100644 partiql/tests/snapshots/pretty__graph_shapes_1.snap create mode 100644 partiql/tests/snapshots/pretty__graph_shapes_10.snap create mode 100644 partiql/tests/snapshots/pretty__graph_shapes_11.snap create mode 100644 partiql/tests/snapshots/pretty__graph_shapes_12.snap create mode 100644 partiql/tests/snapshots/pretty__graph_shapes_13.snap create mode 100644 partiql/tests/snapshots/pretty__graph_shapes_2.snap create mode 100644 partiql/tests/snapshots/pretty__graph_shapes_3.snap create mode 100644 partiql/tests/snapshots/pretty__graph_shapes_4.snap create mode 100644 partiql/tests/snapshots/pretty__graph_shapes_5.snap create mode 100644 partiql/tests/snapshots/pretty__graph_shapes_6.snap create mode 100644 partiql/tests/snapshots/pretty__graph_shapes_7.snap create mode 100644 partiql/tests/snapshots/pretty__graph_shapes_8.snap create mode 100644 partiql/tests/snapshots/pretty__graph_shapes_9.snap diff --git a/partiql-ast/src/pretty.rs b/partiql-ast/src/pretty.rs index 674bc72b..fd9ab54f 100644 --- a/partiql-ast/src/pretty.rs +++ b/partiql-ast/src/pretty.rs @@ -1,6 +1,6 @@ use crate::ast::*; use partiql_common::pretty::{ - pretty_list, pretty_parenthesized_doc, pretty_prefixed_doc, pretty_seperated, + pretty_doc_list, pretty_list, pretty_parenthesized_doc, pretty_prefixed_doc, pretty_seperated, pretty_seperated_doc, pretty_seq, pretty_seq_doc, pretty_surrounded, pretty_surrounded_doc, PrettyDoc, PRETTY_INDENT_MINOR_NEST, PRETTY_INDENT_SUBORDINATE_CLAUSE_NEST, }; @@ -1032,35 +1032,139 @@ impl PrettyDoc for GraphMatch { } impl PrettyDoc for GraphTableRows { - fn pretty_doc<'b, D, A>(&'b self, _arena: &'b D) -> DocBuilder<'b, D, A> + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> where D: DocAllocator<'b, A>, D::Doc: Clone, A: Clone, { - todo!() + match self { + GraphTableRows::OneRowPerMatch => arena.text("ONE ROW PER MATCH"), + GraphTableRows::OneRowPerVertex { v, in_paths } => { + let prefix = arena.text("ONE ROW PER NODE"); + let spec = pretty_parenthesized_doc(arena.text(&v.value), arena); + let in_paths = in_paths.as_ref().map(|paths| { + let paths = pretty_parenthesized_doc( + pretty_doc_list(paths.iter().map(|p| arena.text(&p.value)), 0, arena), + arena, + ); + [arena.text("IN"), paths] + }); + arena.intersperse( + [Some([prefix, spec]), in_paths] + .into_iter() + .flatten() + .flatten(), + arena.softline(), + ) + } + GraphTableRows::OneRowPerStep { + v1, + e, + v2, + in_paths, + } => { + let prefix = arena.text("ONE ROW PER STEP"); + let step = pretty_doc_list( + [v1, e, v2].into_iter().map(|n| arena.text(&n.value)), + 0, + arena, + ); + let spec = pretty_parenthesized_doc(step, arena); + let in_paths = in_paths.as_ref().map(|paths| { + let paths = + pretty_doc_list(paths.iter().map(|p| arena.text(&p.value)), 0, arena); + [arena.text("IN"), pretty_parenthesized_doc(paths, arena)] + }); + arena.intersperse( + [Some([prefix, spec]), in_paths] + .into_iter() + .flatten() + .flatten(), + arena.softline(), + ) + } + } } } impl PrettyDoc for GraphTableColumns { - fn pretty_doc<'b, D, A>(&'b self, _arena: &'b D) -> DocBuilder<'b, D, A> + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> where D: DocAllocator<'b, A>, D::Doc: Clone, A: Clone, { - todo!() + let col_defs = pretty_list(&self.columns, 0, arena); + arena.intersperse( + [ + arena.text("COLUMNS"), + pretty_parenthesized_doc(col_defs, arena), + ], + arena.space(), + ) + } +} + +impl PrettyDoc for GraphTableColumnDef { + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + match self { + GraphTableColumnDef::Expr(expr, as_ident) => { + let parts = if let Some(as_ident) = as_ident { + vec![ + expr.pretty_doc(arena), + arena.text("AS"), + arena.text(&as_ident.value), + ] + } else { + vec![expr.pretty_doc(arena)] + }; + arena.intersperse(parts, arena.space()) + } + GraphTableColumnDef::AllProperties(_) => { + unreachable!() + } + } } } impl PrettyDoc for GraphTableExport { - fn pretty_doc<'b, D, A>(&'b self, _arena: &'b D) -> DocBuilder<'b, D, A> + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> where D: DocAllocator<'b, A>, D::Doc: Clone, A: Clone, { - todo!() + match self { + GraphTableExport::AllSingletons { except } => { + let prefix = arena.text("EXPORT ALL SINGLETONS"); + if let Some(except) = except { + let except = + pretty_doc_list(except.iter().map(|s| arena.text(&s.value)), 0, arena); + let parts = [ + prefix, + arena.text("EXCEPT"), + pretty_parenthesized_doc(except, arena), + ]; + arena.intersperse(parts, arena.space()) + } else { + prefix + } + } + GraphTableExport::Singletons { exports } => { + let prefix = arena.text("EXPORT SINGLETONS"); + let exports = + pretty_doc_list(exports.iter().map(|e| arena.text(&e.value)), 0, arena); + let parts = [prefix, pretty_parenthesized_doc(exports, arena)]; + arena.intersperse(parts, arena.space()) + } + GraphTableExport::NoSingletons => arena.text("EXPORT NO SINGLETONS"), + } } } diff --git a/partiql-common/src/pretty.rs b/partiql-common/src/pretty.rs index ecdf2f06..d349fd85 100644 --- a/partiql-common/src/pretty.rs +++ b/partiql-common/src/pretty.rs @@ -262,6 +262,18 @@ where pretty_seperated(sep, list, nest, arena) } +#[inline] +pub fn pretty_doc_list<'b, I, D, A>(list: I, nest: isize, arena: &'b D) -> DocBuilder<'b, D, A> +where + I: IntoIterator>, + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, +{ + let sep = arena.text(",").append(arena.softline()); + pretty_seperated_doc(sep, list, nest, arena) +} + #[inline] pub fn pretty_seperated<'b, I, E, P, D, A>( sep: E, diff --git a/partiql-parser/Cargo.toml b/partiql-parser/Cargo.toml index 1e74065f..e0189d93 100644 --- a/partiql-parser/Cargo.toml +++ b/partiql-parser/Cargo.toml @@ -48,6 +48,7 @@ serde = { version = "1", features = ["derive"], optional = true } [dev-dependencies] criterion = "0.5" assert_matches = "1" +insta = "1" [features] default = [] diff --git a/partiql-parser/src/lexer/mod.rs b/partiql-parser/src/lexer/mod.rs index c16de096..3ff81319 100644 --- a/partiql-parser/src/lexer/mod.rs +++ b/partiql-parser/src/lexer/mod.rs @@ -84,41 +84,39 @@ mod tests { fn display() -> Result<(), ParseError<'static, BytePosition>> { let symbols = "( [ { } ] ) << >> ; , < > <= >= != <> = == - + * ? % / ^ . || : --foo /*block*/"; + let graph_symbols = + "|+| <- ~ -> <~ ~> <-[ ]- ~[ ]~ -[ ]-> <~[ ]~> <-> <-/ /- ~/ /~ -/ /-> <~/ /~>"; let primitives = r#"unquoted_ident "quoted_ident" @unquoted_atident @"quoted_atident""#; let keywords = "WiTH Where Value uSiNg Unpivot UNION True Select right Preserve pivoT Outer Order Or \ On Offset Nulls Null Not Natural Missing Limit Like Left Lateral Last Join \ Intersect Is Inner In Having Group From For Full First False Except Escape Desc \ Cross Table Time Timestamp Date By Between At As And Asc All Values Case When Then Else End \ - Match Any Shortest Trail Acyclic Simple"; - let symbols = symbols.split(' ').chain(primitives.split(' ')); + Match"; + let nonreserved_kw = "Any Simple Acyclic Bindings Bound Destination \ + Different Directed Edge Edges Elements label Labeled Node Paths Properties Property \ + PROPERTY_GRAPH_CaTalOg PROPERTY_GRAPH_NaMe PROPERTY_GRAPH_SchEma Relationship Relationships \ + Shortest Singletons Step Tables Trail Vertex Walk"; + let symbols = symbols.split(' '); + let graph_symbols = graph_symbols.split(' '); + let primitives = primitives.split(' '); + let nonreserved_kws = nonreserved_kw.split(' '); let keywords = keywords.split(' '); - let text = symbols.interleave(keywords).join("\n"); + let text = symbols + .chain(graph_symbols) + .chain(primitives) + .chain(keywords) + .chain(nonreserved_kws) + .join("\n"); let s = text.as_str(); let mut offset_tracker = LineOffsetTracker::default(); let lexer = PartiqlLexer::new(s, &mut offset_tracker); let toks: Vec<_> = lexer.collect::>().unwrap(); - #[rustfmt::skip] - let expected = vec![ - "(", "WITH", "[", "WHERE", "{", "VALUE", "}", "USING", "]", "UNPIVOT", ")", "UNION", - "<<", "TRUE", ">>", "SELECT", ";", "RIGHT", ",", "PRESERVE", "<", "PIVOT", ">", "OUTER", - "<=", "ORDER", ">=", "OR", "!=", "ON", "<>", "OFFSET", "=", "NULLS", "==", "NULL", "-", - "NOT", "+", "NATURAL", "*", "MISSING", "?", "LIMIT", "%", "LIKE", "/", "LEFT", "^", - "LATERAL", ".", "LAST", "||", "JOIN", ":", "INTERSECT", "--", "IS", "/**/", "INNER", - "", "IN", "", "HAVING", - "", "GROUP", "", - "FROM", "FOR", "FULL", "FIRST", "FALSE", "EXCEPT", "ESCAPE", "DESC", "CROSS", "TABLE", - "TIME", "TIMESTAMP", "DATE", "BY", "BETWEEN", "AT", "AS", "AND", "ASC", "ALL", "VALUES", - "CASE", "WHEN", "THEN", "ELSE", "END", "MATCH", "ANY", "SHORTEST", "TRAIL", "ACYCLIC", "SIMPLE" - ]; - let displayed = toks - .into_iter() - .map(|(_s, t, _e)| t.to_string()) - .collect::>(); - assert_eq!(expected, displayed); + let toks = toks.into_iter().map(|(_s, t, _e)| t.to_string()).join("\n"); + insta::assert_snapshot!(toks); Ok(()) } diff --git a/partiql-parser/src/lexer/snapshots/partiql_parser__lexer__tests__display.snap b/partiql-parser/src/lexer/snapshots/partiql_parser__lexer__tests__display.snap new file mode 100644 index 00000000..04eca47e --- /dev/null +++ b/partiql-parser/src/lexer/snapshots/partiql_parser__lexer__tests__display.snap @@ -0,0 +1,150 @@ +--- +source: partiql-parser/src/lexer/mod.rs +expression: toks +--- +( +[ +{ +} +] +) +<< +>> +; +, +< +> +<= +>= +!= +<> += +== +- ++ +* +? +% +/ +^ +. +|| +: +-- +/**/ +|+| +<- +~ +-> +<~ +~> +<-[ +]- +~[ +]~ +-[ +]-> +<~[ +]~> +<-> +<-/ +/- +~/ +/~ +-/ +/-> +<~/ +/~> + + + + +WITH +WHERE +VALUE +USING +UNPIVOT +UNION +TRUE +SELECT +RIGHT +PRESERVE +PIVOT +OUTER +ORDER +OR +ON +OFFSET +NULLS +NULL +NOT +NATURAL +MISSING +LIMIT +LIKE +LEFT +LATERAL +LAST +JOIN +INTERSECT +IS +INNER +IN +HAVING +GROUP +FROM +FOR +FULL +FIRST +FALSE +EXCEPT +ESCAPE +DESC +CROSS +TABLE +TIME +TIMESTAMP +DATE +BY +BETWEEN +AT +AS +AND +ASC +ALL +VALUES +CASE +WHEN +THEN +ELSE +END +MATCH +ANY("ANY") +SIMPLE("SIMPLE") +ACYCLIC("ACYCLIC") +BINDINGS("BINDINGS") +BOUND("BOUND") +DESTINATION("DESTINATION") +DIFFERENT("DIFFERENT") +DIRECTED("DIRECTED") +EDGE("EDGE") +EDGES("EDGES") +ELEMENTS("ELEMENTS") +LABEL("LABEL") +LABELED("LABELED") +NODE("NODE") +PATHS("PATHS") +PROPERTIES("PROPERTIES") +PROPERTY("PROPERTY") +PROPERTYGRAPHCATALOG("PROPERTY_GRAPH_CATALOG") +PROPERTYGRAPHNAME("PROPERTY_GRAPH_NAME") +PROPERTYGRAPHSCHEMA("PROPERTY_GRAPH_SCHEMA") +RELATIONSHIP("RELATIONSHIP") +RELATIONSHIPS("RELATIONSHIPS") +SHORTEST("SHORTEST") +SINGLETONS("SINGLETONS") +STEP("STEP") +TABLES("TABLES") +TRAIL("TRAIL") +VERTEX("VERTEX") +WALK("WALK") diff --git a/partiql/tests/graph.rs b/partiql/tests/graph.rs index d8d2c5e7..c93a159c 100644 --- a/partiql/tests/graph.rs +++ b/partiql/tests/graph.rs @@ -180,15 +180,39 @@ fn snapshot_test_graph_eval(name: &'static str, contents: &'static str, query: & fn select_star_rfc_0025() { // The RFC 0025 graph has no payloads so these just return an appropriate number of `{}` let contents = include_str!("resources/rfc0025-example.ion"); - snapshot_test_graph_eval("RFC0025 Nodes", contents, "(g MATCH (x))"); - snapshot_test_graph_eval("RFC0025 L Triples", contents, "(g MATCH (x) -> (y))"); - snapshot_test_graph_eval("RFC0025 LUR Triples", contents, "(g MATCH (x) - (y))"); + snapshot_test_graph_eval( + "RFC0025 Nodes", + contents, + "SELECT * FROM GRAPH_TABLE (g MATCH (x))", + ); + snapshot_test_graph_eval( + "RFC0025 L Triples", + contents, + "SELECT * FROM GRAPH_TABLE (g MATCH (x) -> (y))", + ); + snapshot_test_graph_eval( + "RFC0025 LUR Triples", + contents, + "SELECT * FROM GRAPH_TABLE (g MATCH (x) - (y))", + ); } #[test] fn select_star_gpml_paper() { let contents = include_str!("resources/gpml-paper-example.ion"); - snapshot_test_graph_eval("GPML Nodes", contents, "(g MATCH (x))"); - snapshot_test_graph_eval("GPML L Triples", contents, "(g MATCH (x) -[e]-> (y))"); - snapshot_test_graph_eval("GPML LUR Triples", contents, "(g MATCH (x) -[e]- (y))"); + snapshot_test_graph_eval( + "GPML Nodes", + contents, + "SELECT * FROM GRAPH_TABLE (g MATCH (x))", + ); + snapshot_test_graph_eval( + "GPML L Triples", + contents, + "SELECT * FROM GRAPH_TABLE (g MATCH (x) -[e]-> (y))", + ); + snapshot_test_graph_eval( + "GPML LUR Triples", + contents, + "SELECT * FROM GRAPH_TABLE (g MATCH (x) -[e]- (y))", + ); } diff --git a/partiql/tests/pretty.rs b/partiql/tests/pretty.rs index 5523a499..ebb8659e 100644 --- a/partiql/tests/pretty.rs +++ b/partiql/tests/pretty.rs @@ -501,6 +501,98 @@ mod graph { parse!("SELECT * FROM (g MATCH ALL SHORTEST ( (x)-[e]->*(y) ))"); parse!("SELECT * FROM (g MATCH ALL SHORTEST ( TRAIL (x)-[e]->*(y) ))"); } + #[test] + fn shapes() { + macro_rules! parse { + ($q:expr) => {{ + parse_test!("shapes", $q) + }}; + } + parse!( + "SELECT * \ + FROM (g MATCH (x)-[e]->*(y) \ + ONE ROW PER MATCH \ + )" + ); + parse!( + "SELECT * \ + FROM (g MATCH (x)-[e]->*(y) \ + ONE ROW PER NODE ( x ) \ + )" + ); + parse!( + "SELECT * \ + FROM (g MATCH p = (x)-[e]->*(y) \ + ONE ROW PER NODE ( x ) IN ( p ) \ + )" + ); + parse!( + "SELECT * \ + FROM (g MATCH (x)-[e]->*(y) \ + ONE ROW PER STEP ( x, e, y ) \ + )" + ); + parse!( + "SELECT * \ + FROM (g MATCH p = (x)-[e]->*(y)-[e2]-(z) \ + ONE ROW PER STEP ( y, e2, z ) IN ( p ) \ + )" + ); + parse!( + "SELECT * \ + FROM (g MATCH (x)-[e]->*(y) \ + COLUMNS(x as node1, e, y.*) \ + )" + ); + parse!( + "SELECT * \ + FROM (g MATCH (x)-[e]->*(y) \ + EXPORT ALL SINGLETONS \ + )" + ); + parse!( + "SELECT * \ + FROM (g MATCH (x)-[e]->*(y) \ + EXPORT ALL SINGLETONS EXCEPT ( e, y ) \ + )" + ); + parse!( + "SELECT * \ + FROM (g MATCH (x)-[e]->*(y) \ + EXPORT SINGLETONS ( x, e ) \ + )" + ); + parse!( + "SELECT * \ + FROM (g MATCH (x)-[e]->*(y) \ + EXPORT NO SINGLETONS \ + )" + ); + parse!( + "SELECT * \ + FROM (g MATCH (x)-[e]->*(y) \ + ONE ROW PER MATCH \ + COLUMNS(x as node1, e, y.*) \ + EXPORT ALL SINGLETONS \ + )" + ); + parse!( + "SELECT * \ + FROM (g MATCH (x)-[e]->*(y) \ + ONE ROW PER MATCH \ + COLUMNS(x as node1, e, y.*) \ + EXPORT ALL SINGLETONS \ + )" + ); + parse!( + "SELECT * \ + FROM (g MATCH (x)-[e]->*(y) \ + ONE ROW PER MATCH \ + COLUMNS(x,y) \ + EXPORT NO SINGLETONS \ + )" + ); + } } #[test] diff --git a/partiql/tests/snapshots/pretty__graph_shapes_1.snap b/partiql/tests/snapshots/pretty__graph_shapes_1.snap new file mode 100644 index 00000000..15128574 --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_shapes_1.snap @@ -0,0 +1,38 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT * FROM (g MATCH (x)-[e]->*(y) ONE ROW PER MATCH ) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER MATCH) + +------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER MATCH) + +-------------------------------------------------------------------------------- +SELECT * FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER MATCH) + +---------------------------------------- +SELECT * FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER MATCH) + +------------------------------ +SELECT * +FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER MATCH) + +-------------------- +SELECT * +FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER MATCH) + +---------- +SELECT * +FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER MATCH) diff --git a/partiql/tests/snapshots/pretty__graph_shapes_10.snap b/partiql/tests/snapshots/pretty__graph_shapes_10.snap new file mode 100644 index 00000000..e4547d64 --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_shapes_10.snap @@ -0,0 +1,38 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT * FROM (g MATCH (x)-[e]->*(y) EXPORT NO SINGLETONS ) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH (x) -[e]->* (y) + EXPORT NO SINGLETONS) + +------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH (x) -[e]->* (y) + EXPORT NO SINGLETONS) + +-------------------------------------------------------------------------------- +SELECT * FROM (g MATCH (x) -[e]->* (y) + EXPORT NO SINGLETONS) + +---------------------------------------- +SELECT * FROM (g MATCH (x) -[e]->* (y) + EXPORT NO SINGLETONS) + +------------------------------ +SELECT * +FROM (g MATCH (x) -[e]->* (y) + EXPORT NO SINGLETONS) + +-------------------- +SELECT * +FROM (g MATCH (x) -[e]->* (y) + EXPORT NO SINGLETONS) + +---------- +SELECT * +FROM (g MATCH (x) -[e]->* (y) + EXPORT NO SINGLETONS) diff --git a/partiql/tests/snapshots/pretty__graph_shapes_11.snap b/partiql/tests/snapshots/pretty__graph_shapes_11.snap new file mode 100644 index 00000000..37e620be --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_shapes_11.snap @@ -0,0 +1,54 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT * FROM (g MATCH (x)-[e]->*(y) ONE ROW PER MATCH COLUMNS(x as node1, e, y.*) EXPORT ALL SINGLETONS ) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER MATCH + COLUMNS (x AS node1, e, y.*) + EXPORT ALL SINGLETONS) + +------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER MATCH + COLUMNS (x AS node1, e, y.*) + EXPORT ALL SINGLETONS) + +-------------------------------------------------------------------------------- +SELECT * FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER MATCH + COLUMNS (x AS node1, e, y.*) + EXPORT ALL SINGLETONS) + +---------------------------------------- +SELECT * FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER MATCH + COLUMNS (x AS node1, e, y.*) + EXPORT ALL SINGLETONS) + +------------------------------ +SELECT * +FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER MATCH + COLUMNS (x AS node1, e, y.*) + EXPORT ALL SINGLETONS) + +-------------------- +SELECT * +FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER MATCH + COLUMNS (x AS node1, + e, y.*) + EXPORT ALL SINGLETONS) + +---------- +SELECT * +FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER MATCH + COLUMNS (x AS node1, + e, y.*) + EXPORT ALL SINGLETONS) diff --git a/partiql/tests/snapshots/pretty__graph_shapes_12.snap b/partiql/tests/snapshots/pretty__graph_shapes_12.snap new file mode 100644 index 00000000..37e620be --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_shapes_12.snap @@ -0,0 +1,54 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT * FROM (g MATCH (x)-[e]->*(y) ONE ROW PER MATCH COLUMNS(x as node1, e, y.*) EXPORT ALL SINGLETONS ) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER MATCH + COLUMNS (x AS node1, e, y.*) + EXPORT ALL SINGLETONS) + +------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER MATCH + COLUMNS (x AS node1, e, y.*) + EXPORT ALL SINGLETONS) + +-------------------------------------------------------------------------------- +SELECT * FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER MATCH + COLUMNS (x AS node1, e, y.*) + EXPORT ALL SINGLETONS) + +---------------------------------------- +SELECT * FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER MATCH + COLUMNS (x AS node1, e, y.*) + EXPORT ALL SINGLETONS) + +------------------------------ +SELECT * +FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER MATCH + COLUMNS (x AS node1, e, y.*) + EXPORT ALL SINGLETONS) + +-------------------- +SELECT * +FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER MATCH + COLUMNS (x AS node1, + e, y.*) + EXPORT ALL SINGLETONS) + +---------- +SELECT * +FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER MATCH + COLUMNS (x AS node1, + e, y.*) + EXPORT ALL SINGLETONS) diff --git a/partiql/tests/snapshots/pretty__graph_shapes_13.snap b/partiql/tests/snapshots/pretty__graph_shapes_13.snap new file mode 100644 index 00000000..e1745fbd --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_shapes_13.snap @@ -0,0 +1,53 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT * FROM (g MATCH (x)-[e]->*(y) ONE ROW PER MATCH COLUMNS(x,y) EXPORT NO SINGLETONS ) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER MATCH + COLUMNS (x, y) + EXPORT NO SINGLETONS) + +------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER MATCH + COLUMNS (x, y) + EXPORT NO SINGLETONS) + +-------------------------------------------------------------------------------- +SELECT * FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER MATCH + COLUMNS (x, y) + EXPORT NO SINGLETONS) + +---------------------------------------- +SELECT * FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER MATCH + COLUMNS (x, y) + EXPORT NO SINGLETONS) + +------------------------------ +SELECT * +FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER MATCH + COLUMNS (x, y) + EXPORT NO SINGLETONS) + +-------------------- +SELECT * +FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER MATCH + COLUMNS (x, y) + EXPORT NO SINGLETONS) + +---------- +SELECT * +FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER MATCH + COLUMNS (x, + y) + EXPORT NO SINGLETONS) diff --git a/partiql/tests/snapshots/pretty__graph_shapes_2.snap b/partiql/tests/snapshots/pretty__graph_shapes_2.snap new file mode 100644 index 00000000..7ca054c6 --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_shapes_2.snap @@ -0,0 +1,40 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT * FROM (g MATCH (x)-[e]->*(y) ONE ROW PER NODE ( x ) ) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER NODE (x)) + +------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER NODE (x)) + +-------------------------------------------------------------------------------- +SELECT * FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER NODE (x)) + +---------------------------------------- +SELECT * FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER NODE (x)) + +------------------------------ +SELECT * +FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER NODE (x)) + +-------------------- +SELECT * +FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER NODE + (x)) + +---------- +SELECT * +FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER NODE + (x)) diff --git a/partiql/tests/snapshots/pretty__graph_shapes_3.snap b/partiql/tests/snapshots/pretty__graph_shapes_3.snap new file mode 100644 index 00000000..3e1b24a0 --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_shapes_3.snap @@ -0,0 +1,42 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT * FROM (g MATCH p = (x)-[e]->*(y) ONE ROW PER NODE ( x ) IN ( p ) ) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH p = (x) -[e]->* (y) + ONE ROW PER NODE (x) IN (p)) + +------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH p = (x) -[e]->* (y) + ONE ROW PER NODE (x) IN (p)) + +-------------------------------------------------------------------------------- +SELECT * FROM (g MATCH p = (x) -[e]->* (y) + ONE ROW PER NODE (x) IN (p)) + +---------------------------------------- +SELECT * +FROM (g MATCH p = (x) -[e]->* (y) + ONE ROW PER NODE (x) IN (p)) + +------------------------------ +SELECT * +FROM (g MATCH p = (x) -[e]->* (y) + ONE ROW PER NODE (x) IN (p)) + +-------------------- +SELECT * +FROM (g MATCH p = (x) -[e]->* (y) + ONE ROW PER NODE + (x) IN (p)) + +---------- +SELECT * +FROM (g MATCH p = (x) -[e]->* (y) + ONE ROW PER NODE + (x) IN + (p)) diff --git a/partiql/tests/snapshots/pretty__graph_shapes_4.snap b/partiql/tests/snapshots/pretty__graph_shapes_4.snap new file mode 100644 index 00000000..05e16290 --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_shapes_4.snap @@ -0,0 +1,41 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT * FROM (g MATCH (x)-[e]->*(y) ONE ROW PER STEP ( x, e, y ) ) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER STEP (x, e, y)) + +------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER STEP (x, e, y)) + +-------------------------------------------------------------------------------- +SELECT * FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER STEP (x, e, y)) + +---------------------------------------- +SELECT * FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER STEP (x, e, y)) + +------------------------------ +SELECT * +FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER STEP (x, e, y)) + +-------------------- +SELECT * +FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER STEP + (x, e, y)) + +---------- +SELECT * +FROM (g MATCH (x) -[e]->* (y) + ONE ROW PER STEP + (x, e, + y)) diff --git a/partiql/tests/snapshots/pretty__graph_shapes_5.snap b/partiql/tests/snapshots/pretty__graph_shapes_5.snap new file mode 100644 index 00000000..0b7d3bdc --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_shapes_5.snap @@ -0,0 +1,44 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT * FROM (g MATCH p = (x)-[e]->*(y)-[e2]-(z) ONE ROW PER STEP ( y, e2, z ) IN ( p ) ) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH p = (x) -[e]->* (y) -[e2]- (z) + ONE ROW PER STEP (y, e2, z) IN (p)) + +------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH p = (x) -[e]->* (y) -[e2]- (z) + ONE ROW PER STEP (y, e2, z) IN (p)) + +-------------------------------------------------------------------------------- +SELECT * FROM (g MATCH p = (x) -[e]->* (y) -[e2]- (z) + ONE ROW PER STEP (y, e2, z) IN (p)) + +---------------------------------------- +SELECT * +FROM (g MATCH p = (x) -[e]->* (y) -[e2]- (z) + ONE ROW PER STEP (y, e2, z) IN (p)) + +------------------------------ +SELECT * +FROM (g MATCH p = (x) -[e]->* (y) -[e2]- (z) + ONE ROW PER STEP (y, e2, z) + IN (p)) + +-------------------- +SELECT * +FROM (g MATCH p = (x) -[e]->* (y) -[e2]- (z) + ONE ROW PER STEP + (y, e2, z) IN (p)) + +---------- +SELECT * +FROM (g MATCH p = (x) -[e]->* (y) -[e2]- (z) + ONE ROW PER STEP + (y, e2, + z) IN + (p)) diff --git a/partiql/tests/snapshots/pretty__graph_shapes_6.snap b/partiql/tests/snapshots/pretty__graph_shapes_6.snap new file mode 100644 index 00000000..008c5907 --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_shapes_6.snap @@ -0,0 +1,41 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT * FROM (g MATCH (x)-[e]->*(y) COLUMNS(x as node1, e, y.*) ) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH (x) -[e]->* (y) + COLUMNS (x AS node1, e, y.*)) + +------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH (x) -[e]->* (y) + COLUMNS (x AS node1, e, y.*)) + +-------------------------------------------------------------------------------- +SELECT * FROM (g MATCH (x) -[e]->* (y) + COLUMNS (x AS node1, e, y.*)) + +---------------------------------------- +SELECT * FROM (g MATCH (x) -[e]->* (y) + COLUMNS (x AS node1, e, y.*)) + +------------------------------ +SELECT * +FROM (g MATCH (x) -[e]->* (y) + COLUMNS (x AS node1, e, + y.*)) + +-------------------- +SELECT * +FROM (g MATCH (x) -[e]->* (y) + COLUMNS (x AS node1, + e, y.*)) + +---------- +SELECT * +FROM (g MATCH (x) -[e]->* (y) + COLUMNS (x AS node1, + e, y.*)) diff --git a/partiql/tests/snapshots/pretty__graph_shapes_7.snap b/partiql/tests/snapshots/pretty__graph_shapes_7.snap new file mode 100644 index 00000000..c49c321f --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_shapes_7.snap @@ -0,0 +1,38 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT * FROM (g MATCH (x)-[e]->*(y) EXPORT ALL SINGLETONS ) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH (x) -[e]->* (y) + EXPORT ALL SINGLETONS) + +------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH (x) -[e]->* (y) + EXPORT ALL SINGLETONS) + +-------------------------------------------------------------------------------- +SELECT * FROM (g MATCH (x) -[e]->* (y) + EXPORT ALL SINGLETONS) + +---------------------------------------- +SELECT * FROM (g MATCH (x) -[e]->* (y) + EXPORT ALL SINGLETONS) + +------------------------------ +SELECT * +FROM (g MATCH (x) -[e]->* (y) + EXPORT ALL SINGLETONS) + +-------------------- +SELECT * +FROM (g MATCH (x) -[e]->* (y) + EXPORT ALL SINGLETONS) + +---------- +SELECT * +FROM (g MATCH (x) -[e]->* (y) + EXPORT ALL SINGLETONS) diff --git a/partiql/tests/snapshots/pretty__graph_shapes_8.snap b/partiql/tests/snapshots/pretty__graph_shapes_8.snap new file mode 100644 index 00000000..55d8d861 --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_shapes_8.snap @@ -0,0 +1,41 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT * FROM (g MATCH (x)-[e]->*(y) EXPORT ALL SINGLETONS EXCEPT ( e, y ) ) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH (x) -[e]->* (y) + EXPORT ALL SINGLETONS EXCEPT (e, y)) + +------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH (x) -[e]->* (y) + EXPORT ALL SINGLETONS EXCEPT (e, y)) + +-------------------------------------------------------------------------------- +SELECT * FROM (g MATCH (x) -[e]->* (y) + EXPORT ALL SINGLETONS EXCEPT (e, y)) + +---------------------------------------- +SELECT * FROM (g MATCH (x) -[e]->* (y) + EXPORT ALL SINGLETONS EXCEPT (e, y)) + +------------------------------ +SELECT * +FROM (g MATCH (x) -[e]->* (y) + EXPORT ALL SINGLETONS EXCEPT (e, + y)) + +-------------------- +SELECT * +FROM (g MATCH (x) -[e]->* (y) + EXPORT ALL SINGLETONS EXCEPT (e, + y)) + +---------- +SELECT * +FROM (g MATCH (x) -[e]->* (y) + EXPORT ALL SINGLETONS EXCEPT (e, + y)) diff --git a/partiql/tests/snapshots/pretty__graph_shapes_9.snap b/partiql/tests/snapshots/pretty__graph_shapes_9.snap new file mode 100644 index 00000000..3403862b --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_shapes_9.snap @@ -0,0 +1,40 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT * FROM (g MATCH (x)-[e]->*(y) EXPORT SINGLETONS ( x, e ) ) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH (x) -[e]->* (y) + EXPORT SINGLETONS (x, e)) + +------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH (x) -[e]->* (y) + EXPORT SINGLETONS (x, e)) + +-------------------------------------------------------------------------------- +SELECT * FROM (g MATCH (x) -[e]->* (y) + EXPORT SINGLETONS (x, e)) + +---------------------------------------- +SELECT * FROM (g MATCH (x) -[e]->* (y) + EXPORT SINGLETONS (x, e)) + +------------------------------ +SELECT * +FROM (g MATCH (x) -[e]->* (y) + EXPORT SINGLETONS (x, e)) + +-------------------- +SELECT * +FROM (g MATCH (x) -[e]->* (y) + EXPORT SINGLETONS (x, + e)) + +---------- +SELECT * +FROM (g MATCH (x) -[e]->* (y) + EXPORT SINGLETONS (x, + e)) From 62331849ecc495db5be734353c70456fc9b3ba2d Mon Sep 17 00:00:00 2001 From: Josh Pschorr Date: Sat, 5 Apr 2025 12:34:41 -0700 Subject: [PATCH 8/9] Refactor graph ast/pretty into modules --- partiql-ast/src/ast/graph.rs | 305 +++++++++++ partiql-ast/src/{ast.rs => ast/mod.rs} | 313 +---------- partiql-ast/src/pretty/graph.rs | 494 ++++++++++++++++++ partiql-ast/src/{pretty.rs => pretty/mod.rs} | 487 +---------------- partiql-ast/src/visit.rs | 68 ++- partiql-logical-planner/src/graph.rs | 46 +- partiql-logical-planner/src/lower.rs | 14 +- partiql-parser/src/parse/partiql.lalrpop | 4 +- partiql/tests/pretty.rs | 16 + .../tests/snapshots/pretty__graph_etc_3.snap | 34 ++ .../tests/snapshots/pretty__graph_etc_4.snap | 34 ++ 11 files changed, 975 insertions(+), 840 deletions(-) create mode 100644 partiql-ast/src/ast/graph.rs rename partiql-ast/src/{ast.rs => ast/mod.rs} (73%) create mode 100644 partiql-ast/src/pretty/graph.rs rename partiql-ast/src/{pretty.rs => pretty/mod.rs} (69%) create mode 100644 partiql/tests/snapshots/pretty__graph_etc_3.snap create mode 100644 partiql/tests/snapshots/pretty__graph_etc_4.snap diff --git a/partiql-ast/src/ast/graph.rs b/partiql-ast/src/ast/graph.rs new file mode 100644 index 00000000..d6f3519c --- /dev/null +++ b/partiql-ast/src/ast/graph.rs @@ -0,0 +1,305 @@ +use crate::ast::{AstNode, Expr, SymbolPrimitive}; +use partiql_ast_macros::Visit; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; +use std::num::NonZeroU32; + +#[derive(Visit, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct GraphTable { + pub graph_match: AstNode, +} + +/// ` MATCH ` +#[derive(Visit, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct GraphMatch { + pub expr: Box, + pub pattern: AstNode, + #[visit(skip)] + pub shape: GraphTableShape, +} + +#[derive(Visit, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct GraphPattern { + #[visit(skip)] + pub mode: Option, + pub patterns: Vec>, + #[visit(skip)] + pub keep: Option, + pub where_clause: Option>, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum GraphMatchMode { + DifferentEdges, + RepeatableElements, +} + +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct GraphTableShape { + pub rows: Option>, + pub cols: Option>, + pub export: Option>, +} + +#[derive(Clone, Default, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum GraphTableRows { + #[default] + OneRowPerMatch, + OneRowPerVertex { + v: SymbolPrimitive, + in_paths: Option>, + }, + OneRowPerStep { + v1: SymbolPrimitive, + e: SymbolPrimitive, + v2: SymbolPrimitive, + in_paths: Option>, + }, +} + +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct GraphTableColumns { + pub columns: Vec, +} + +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum GraphTableColumnDef { + Expr(Box, Option), + AllProperties(SymbolPrimitive), +} + +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum GraphTableExport { + AllSingletons { + except: Option>, + }, + Singletons { + exports: Vec, + }, + NoSingletons, +} + +/// The direction of an edge +/// | Orientation | Edge pattern | Abbreviation | +/// |---------------------------+--------------+--------------| +/// | Pointing left | <−[ spec ]− | <− | +/// | Undirected | ~[ spec ]~ | ~ | +/// | Pointing right | −[ spec ]−> | −> | +/// | Left or undirected | <~[ spec ]~ | <~ | +/// | Undirected or right | ~[ spec ]~> | ~> | +/// | Left or right | <−[ spec ]−> | <−> | +/// | Left, undirected or right | −[ spec ]− | − | +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum GraphMatchDirection { + Left, + Undirected, + Right, + LeftOrUndirected, + UndirectedOrRight, + LeftOrRight, + LeftOrUndirectedOrRight, +} + +/// A quantifier for graph edges or patterns. (e.g., the `{2,5}` in `MATCH (x)->{2,5}(y)`) +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct GraphMatchQuantifier { + pub lower: u32, + pub upper: Option, +} + +/// A path mode +/// | Keyword | Description +/// |----------------+-------------- +/// | WALK | +/// | TRAIL | No repeated edges. +/// | ACYCLIC | No repeated nodes. +/// | SIMPLE | No repeated nodes, except that the first and last nodes may be the same. + +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum GraphPathMode { + Walk, + Trail, + Acyclic, + Simple, +} + +/// A single node in a graph pattern. +#[derive(Visit, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct GraphMatchNode { + /// the optional element variable of the node match, e.g.: `x` in `MATCH (x)` + #[visit(skip)] + pub variable: Option, + /// the optional label(s) to match for the node, e.g.: `Entity` in `MATCH (x:Entity)` + #[visit(skip)] + pub label: Option>, + /// an optional node where clause, e.g.: `WHERE c.name='Alarm'` in `MATCH (c WHERE c.name='Alarm')` + pub where_clause: Option>, +} + +/// A single edge in a graph pattern. +#[derive(Visit, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct GraphMatchEdge { + /// edge direction + #[visit(skip)] + pub direction: GraphMatchDirection, + /// an optional quantifier for the edge match + #[visit(skip)] + pub quantifier: Option>, + /// the optional element variable of the edge match, e.g.: `t` in `MATCH −[t]−>` + #[visit(skip)] + pub variable: Option, + /// the optional label(s) to match for the edge. e.g.: `Target` in `MATCH −[t:Target]−>` + #[visit(skip)] + pub label: Option>, + /// an optional edge where clause, e.g.: `WHERE t.capacity>100` in `MATCH −[t:hasSupply WHERE t.capacity>100]−>` + pub where_clause: Option>, +} + +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum GraphMatchLabel { + Name(SymbolPrimitive), + Wildcard, + Negated(Box>), + Conjunction(Vec>), + Disjunction(Vec>), +} + +#[derive(Visit, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct GraphPathPattern { + /// the optional element variable of the pattern, e.g.: `p` in `MATCH p = (a) −[t]−> (b)` + #[visit(skip)] + pub variable: Option, + #[visit(skip)] + pub prefix: Option, + /// the ordered pattern parts + pub path: AstNode, +} + +#[derive(Visit, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct GraphPathSubPattern { + /// the optional element variable of the pattern, e.g.: `p` in `MATCH p = (a) −[t]−> (b)` + #[visit(skip)] + pub variable: Option, + #[visit(skip)] + pub mode: Option, + /// the ordered pattern parts + pub path: AstNode, + /// an optional pattern where e.g.: `WHERE a.name=b.name` in `MATCH [(a)->(b) WHERE a.name=b.name]` + pub where_clause: Option>, +} + +#[derive(Visit, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum GraphMatchPathPattern { + Path(Vec>), + Union(Vec>), + Multiset(Vec>), + + Questioned(Box>), + Quantified(GraphMatchPathPatternQuantified), + + Sub(Box>), + + /// A single node in a graph pattern. + Node(AstNode), + + /// A single edge in a graph pattern. + Edge(AstNode), + + #[visit(skip)] + Simplified(AstNode), +} + +#[derive(Visit, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct GraphMatchPathPatternQuantified { + pub path: Box>, + #[visit(skip)] + pub quant: AstNode, +} + +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct GraphMatchElement { + pub variable: Option, + pub label: Option>, + pub where_clause: Option>, +} + +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct GraphMatchSimplified { + pub dir: GraphMatchDirection, + pub pattern: AstNode, +} + +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum GraphMatchSimplifiedPattern { + Union(Vec>), + Multiset(Vec>), + + Path(Vec>), + + Conjunction(Vec>), + + Questioned(Box>), + Quantified( + Box>, + AstNode, + ), + + /// Direction override + Direction( + GraphMatchDirection, + Box>, + ), + + Negated(Box>), + Label(SymbolPrimitive), +} + +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum GraphPathPrefix { + Mode(GraphPathMode), + Search(GraphPathSearchPrefix, Option), +} + +/// | Keyword +/// |------------------ +/// | ALL +/// | Any +/// | ANY k +/// | ALL SHORTEST +/// | ANY SHORTEST +/// | SHORTEST k +/// | SHORTEST k GROUP +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum GraphPathSearchPrefix { + All, + Any, + AnyK(NonZeroU32), + AllShortest, + AnyShortest, + ShortestK(NonZeroU32), + ShortestKGroup(Option), +} diff --git a/partiql-ast/src/ast.rs b/partiql-ast/src/ast/mod.rs similarity index 73% rename from partiql-ast/src/ast.rs rename to partiql-ast/src/ast/mod.rs index 9e745272..f2268864 100644 --- a/partiql-ast/src/ast.rs +++ b/partiql-ast/src/ast/mod.rs @@ -10,14 +10,15 @@ use rust_decimal::Decimal as RustDecimal; +use partiql_ast_macros::Visit; +use partiql_common::node::NodeId; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use std::fmt; -use std::num::NonZeroU32; use std::ops::Deref; -use partiql_ast_macros::Visit; -use partiql_common::node::NodeId; +mod graph; +pub use graph::*; /// Represents an AST node. #[derive(Clone, Debug, Eq, PartialEq)] @@ -844,312 +845,6 @@ pub enum JoinSpec { Natural, } -// TODO #[derive(Visit, Clone, Debug, PartialEq)] -#[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct GraphTable { - pub graph_match: AstNode, -} - -/// ` MATCH ` -#[derive(Visit, Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct GraphMatch { - pub expr: Box, - // TODO remove - #[visit(skip)] - pub pattern: AstNode, - // TODO remove - #[visit(skip)] - pub shape: GraphTableShape, -} - -// TODO #[derive(Visit, Clone, Debug, PartialEq)] -#[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct GraphPattern { - // TODO #[visit(skip)] - pub mode: Option, - pub patterns: Vec>, - // TODO #[visit(skip)] - pub keep: Option, - pub where_clause: Option>, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum GraphMatchMode { - DifferentEdges, - RepeatableElements, -} - -#[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct GraphTableShape { - pub rows: Option>, - pub cols: Option>, - pub export: Option>, -} - -#[derive(Clone, Default, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum GraphTableRows { - #[default] - OneRowPerMatch, - OneRowPerVertex { - v: SymbolPrimitive, - in_paths: Option>, - }, - OneRowPerStep { - v1: SymbolPrimitive, - e: SymbolPrimitive, - v2: SymbolPrimitive, - in_paths: Option>, - }, -} - -#[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct GraphTableColumns { - pub columns: Vec, -} - -#[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum GraphTableColumnDef { - Expr(Box, Option), - AllProperties(SymbolPrimitive), -} - -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum GraphTableExport { - AllSingletons { - except: Option>, - }, - Singletons { - exports: Vec, - }, - NoSingletons, -} - -/// The direction of an edge -/// | Orientation | Edge pattern | Abbreviation | -/// |---------------------------+--------------+--------------| -/// | Pointing left | <−[ spec ]− | <− | -/// | Undirected | ~[ spec ]~ | ~ | -/// | Pointing right | −[ spec ]−> | −> | -/// | Left or undirected | <~[ spec ]~ | <~ | -/// | Undirected or right | ~[ spec ]~> | ~> | -/// | Left or right | <−[ spec ]−> | <−> | -/// | Left, undirected or right | −[ spec ]− | − | -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum GraphMatchDirection { - Left, - Undirected, - Right, - LeftOrUndirected, - UndirectedOrRight, - LeftOrRight, - LeftOrUndirectedOrRight, -} - -/// A quantifier for graph edges or patterns. (e.g., the `{2,5}` in `MATCH (x)->{2,5}(y)`) -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct GraphMatchQuantifier { - pub lower: u32, - pub upper: Option, -} - -/// A path mode -/// | Keyword | Description -/// |----------------+-------------- -/// | WALK | -/// | TRAIL | No repeated edges. -/// | ACYCLIC | No repeated nodes. -/// | SIMPLE | No repeated nodes, except that the first and last nodes may be the same. - -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum GraphPathMode { - Walk, - Trail, - Acyclic, - Simple, -} - -/// A single node in a graph pattern. -#[derive(Visit, Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct GraphMatchNode { - /// the optional element variable of the node match, e.g.: `x` in `MATCH (x)` - #[visit(skip)] - pub variable: Option, - /// the optional label(s) to match for the node, e.g.: `Entity` in `MATCH (x:Entity)` - #[visit(skip)] - pub label: Option>, - /// an optional node where clause, e.g.: `WHERE c.name='Alarm'` in `MATCH (c WHERE c.name='Alarm')` - pub where_clause: Option>, -} - -/// A single edge in a graph pattern. -#[derive(Visit, Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct GraphMatchEdge { - /// edge direction - #[visit(skip)] - pub direction: GraphMatchDirection, - /// an optional quantifier for the edge match - #[visit(skip)] - pub quantifier: Option>, - /// the optional element variable of the edge match, e.g.: `t` in `MATCH −[t]−>` - #[visit(skip)] - pub variable: Option, - /// the optional label(s) to match for the edge. e.g.: `Target` in `MATCH −[t:Target]−>` - #[visit(skip)] - pub label: Option>, - /// an optional edge where clause, e.g.: `WHERE t.capacity>100` in `MATCH −[t:hasSupply WHERE t.capacity>100]−>` - pub where_clause: Option>, -} - -#[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum GraphMatchLabel { - Name(SymbolPrimitive), - Wildcard, - Negated(Box>), - Conjunction(Vec>), - Disjunction(Vec>), -} - -// TODO #[derive(Visit, Clone, Debug, PartialEq)] -#[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct GraphPathPattern { - /// the optional element variable of the pattern, e.g.: `p` in `MATCH p = (a) −[t]−> (b)` - // TODO #[visit(skip)] - pub variable: Option, - // TODO #[visit(skip)] - pub prefix: Option, - /// the ordered pattern parts - // TODO #[visit(skip)] - pub path: AstNode, -} - -// TODO #[derive(Visit, Clone, Debug, PartialEq)] -#[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct GraphPathSubPattern { - /// the optional element variable of the pattern, e.g.: `p` in `MATCH p = (a) −[t]−> (b)` - // TODO #[visit(skip)] - pub variable: Option, - // TODO #[visit(skip)] - pub mode: Option, - /// the ordered pattern parts - // TODO #[visit(skip)] - pub path: AstNode, - /// an optional pattern where e.g.: `WHERE a.name=b.name` in `MATCH [(a)->(b) WHERE a.name=b.name]` - pub where_clause: Option>, -} - -// TODO #[derive(Visit, Clone, Debug, PartialEq)] -#[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum GraphMatchPathPattern { - Path(Vec>), - Union(Vec>), - Multiset(Vec>), - - Questioned(Box>), - Quantified( - Box>, - AstNode, - ), - - Sub(Box>), - - /// A single node in a graph pattern. - Node(AstNode), - - /// A single edge in a graph pattern. - Edge(AstNode), - - Simplified(AstNode), -} - -#[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct GraphMatchElement { - pub variable: Option, - pub label: Option>, - pub where_clause: Option>, -} - -// TODO #[derive(Visit, Clone, Debug, PartialEq)] -#[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct GraphMatchSimplified { - pub dir: GraphMatchDirection, - pub pattern: AstNode, -} - -// TODO #[derive(Visit, Clone, Debug, PartialEq)] -#[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum GraphMatchSimplifiedPattern { - Union(Vec>), - Multiset(Vec>), - - Path(Vec>), - - Conjunction(Vec>), - - Questioned(Box>), - Quantified( - Box>, - AstNode, - ), - - /// Direction override - Direction( - GraphMatchDirection, - Box>, - ), - - Negated(Box>), - Label(SymbolPrimitive), -} - -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum GraphPathPrefix { - Mode(GraphPathMode), - Search(GraphPathSearchPrefix, Option), -} - -/// | Keyword -/// |------------------ -/// | ALL -/// | Any -/// | ANY k -/// | ALL SHORTEST -/// | ANY SHORTEST -/// | SHORTEST k -/// | SHORTEST k GROUP -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum GraphPathSearchPrefix { - All, - Any, - AnyK(NonZeroU32), - AllShortest, - AnyShortest, - ShortestK(NonZeroU32), - ShortestKGroup(Option), -} - /// GROUP BY <`grouping_strategy`> <`group_key`>[, <`group_key`>]... \[AS \] #[derive(Visit, Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] diff --git a/partiql-ast/src/pretty/graph.rs b/partiql-ast/src/pretty/graph.rs new file mode 100644 index 00000000..1d081c01 --- /dev/null +++ b/partiql-ast/src/pretty/graph.rs @@ -0,0 +1,494 @@ +use crate::ast::*; +use partiql_common::pretty::{ + pretty_doc_list, pretty_list, pretty_parenthesized_doc, pretty_surrounded, + pretty_surrounded_doc, PrettyDoc, PRETTY_INDENT_MINOR_NEST, +}; +use pretty::{DocAllocator, DocBuilder}; +use std::borrow::Cow; + +impl PrettyDoc for GraphMatch { + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + let GraphMatch { + expr, + pattern, + shape, + } = self; + let head = arena.intersperse([expr.pretty_doc(arena), arena.text("MATCH")], arena.space()); + let patterns = pattern.pretty_doc(arena).group(); + let mut match_expr = arena.intersperse([head, patterns], arena.space()); + let shapes = [ + shape.rows.as_ref().map(|d| d.pretty_doc(arena)), + shape.cols.as_ref().map(|d| d.pretty_doc(arena)), + shape.export.as_ref().map(|d| d.pretty_doc(arena)), + ] + .into_iter() + .flatten() + .collect::>(); + if !shapes.is_empty() { + let docs = std::iter::once(match_expr).chain(shapes); + match_expr = arena.intersperse(docs, arena.hardline()); + } + + pretty_parenthesized_doc(match_expr, arena) + } +} + +impl PrettyDoc for GraphTableRows { + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + match self { + GraphTableRows::OneRowPerMatch => arena.text("ONE ROW PER MATCH"), + GraphTableRows::OneRowPerVertex { v, in_paths } => { + let prefix = arena.text("ONE ROW PER NODE"); + let spec = pretty_parenthesized_doc(arena.text(&v.value), arena); + let in_paths = in_paths.as_ref().map(|paths| { + let paths = pretty_parenthesized_doc( + pretty_doc_list(paths.iter().map(|p| arena.text(&p.value)), 0, arena), + arena, + ); + [arena.text("IN"), paths] + }); + arena.intersperse( + [Some([prefix, spec]), in_paths] + .into_iter() + .flatten() + .flatten(), + arena.softline(), + ) + } + GraphTableRows::OneRowPerStep { + v1, + e, + v2, + in_paths, + } => { + let prefix = arena.text("ONE ROW PER STEP"); + let step = pretty_doc_list( + [v1, e, v2].into_iter().map(|n| arena.text(&n.value)), + 0, + arena, + ); + let spec = pretty_parenthesized_doc(step, arena); + let in_paths = in_paths.as_ref().map(|paths| { + let paths = + pretty_doc_list(paths.iter().map(|p| arena.text(&p.value)), 0, arena); + [arena.text("IN"), pretty_parenthesized_doc(paths, arena)] + }); + arena.intersperse( + [Some([prefix, spec]), in_paths] + .into_iter() + .flatten() + .flatten(), + arena.softline(), + ) + } + } + } +} + +impl PrettyDoc for GraphTableColumns { + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + let col_defs = pretty_list(&self.columns, 0, arena); + arena.intersperse( + [ + arena.text("COLUMNS"), + pretty_parenthesized_doc(col_defs, arena), + ], + arena.space(), + ) + } +} + +impl PrettyDoc for GraphTableColumnDef { + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + match self { + GraphTableColumnDef::Expr(expr, as_ident) => { + let parts = if let Some(as_ident) = as_ident { + vec![ + expr.pretty_doc(arena), + arena.text("AS"), + arena.text(&as_ident.value), + ] + } else { + vec![expr.pretty_doc(arena)] + }; + arena.intersperse(parts, arena.space()) + } + GraphTableColumnDef::AllProperties(_) => { + unreachable!() + } + } + } +} + +impl PrettyDoc for GraphTableExport { + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + match self { + GraphTableExport::AllSingletons { except } => { + let prefix = arena.text("EXPORT ALL SINGLETONS"); + if let Some(except) = except { + let except = + pretty_doc_list(except.iter().map(|s| arena.text(&s.value)), 0, arena); + let parts = [ + prefix, + arena.text("EXCEPT"), + pretty_parenthesized_doc(except, arena), + ]; + arena.intersperse(parts, arena.space()) + } else { + prefix + } + } + GraphTableExport::Singletons { exports } => { + let prefix = arena.text("EXPORT SINGLETONS"); + let exports = + pretty_doc_list(exports.iter().map(|e| arena.text(&e.value)), 0, arena); + let parts = [prefix, pretty_parenthesized_doc(exports, arena)]; + arena.intersperse(parts, arena.space()) + } + GraphTableExport::NoSingletons => arena.text("EXPORT NO SINGLETONS"), + } + } +} + +impl PrettyDoc for GraphPattern { + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + let GraphPattern { + mode, + patterns, + keep, + where_clause, + } = &self; + let patterns = pretty_list(patterns, PRETTY_INDENT_MINOR_NEST, arena).group(); + let parts = [ + mode.as_ref().map(|inner| inner.pretty_doc(arena)), + Some(patterns), + keep.as_ref().map(|keep| { + arena.intersperse([arena.text("KEEP"), keep.pretty_doc(arena)], arena.space()) + }), + where_clause.as_ref().map(|clause| { + arena.intersperse( + [arena.text("WHERE"), clause.pretty_doc(arena)], + arena.space(), + ) + }), + ] + .into_iter() + .flatten(); + + arena.intersperse(parts, arena.space()) + } +} + +impl PrettyDoc for GraphMatchMode { + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + match self { + GraphMatchMode::DifferentEdges => arena.text("DIFFERENT EDGES"), + GraphMatchMode::RepeatableElements => arena.text("REPEATABLE ELEMENTS"), + } + } +} + +impl PrettyDoc for GraphPathPrefix { + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + match self { + GraphPathPrefix::Mode(mode) => mode.pretty_doc(arena), + GraphPathPrefix::Search(search, mode) => { + let mode = mode.as_ref().map(|mode| mode.pretty_doc(arena)); + let parts = match search { + GraphPathSearchPrefix::All => vec![Some(arena.text("ALL")), mode], + GraphPathSearchPrefix::Any => vec![Some(arena.text("ANY")), mode], + GraphPathSearchPrefix::AnyK(k) => vec![ + Some(arena.text("ANY")), + Some(arena.text(k.to_string())), + mode, + ], + GraphPathSearchPrefix::AllShortest => { + vec![Some(arena.text("ALL")), Some(arena.text("SHORTEST")), mode] + } + GraphPathSearchPrefix::AnyShortest => { + vec![Some(arena.text("ANY")), Some(arena.text("SHORTEST")), mode] + } + GraphPathSearchPrefix::ShortestK(k) => vec![ + Some(arena.text("SHORTEST")), + Some(arena.text(k.to_string())), + mode, + ], + GraphPathSearchPrefix::ShortestKGroup(k) => vec![ + Some(arena.text("SHORTEST")), + k.as_ref().map(|k| arena.text(k.to_string())), + mode, + Some(arena.text("GROUPS")), + ], + } + .into_iter() + .flatten() + .collect::>(); + arena.intersperse(parts, arena.space()) + } + } + } +} + +impl PrettyDoc for GraphPathMode { + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + let txt = match self { + GraphPathMode::Walk => "WALK", + GraphPathMode::Trail => "TRAIL", + GraphPathMode::Acyclic => "ACYCLIC", + GraphPathMode::Simple => "SIMPLE", + }; + arena.text(txt) + } +} + +impl PrettyDoc for GraphPathPattern { + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + let var = self + .variable + .as_ref() + .map(|var| arena.intersperse([arena.text(&var.value), arena.text("=")], arena.space())); + + let prefix = self.prefix.as_ref().map(|prefix| prefix.pretty_doc(arena)); + + let parts = [var, prefix, Some(self.path.pretty_doc(arena))] + .into_iter() + .flatten(); + arena.intersperse(parts, arena.space()) + } +} + +impl PrettyDoc for GraphPathSubPattern { + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + let var = self + .variable + .as_ref() + .map(|var| arena.intersperse([arena.text(&var.value), arena.text("=")], arena.space())); + + let mode = self.mode.as_ref().map(|prefix| prefix.pretty_doc(arena)); + let where_clause = self.where_clause.as_ref().map(|clause| { + arena.intersperse( + [arena.text("WHERE"), clause.pretty_doc(arena)], + arena.space(), + ) + }); + + let parts = [var, mode, Some(self.path.pretty_doc(arena)), where_clause] + .into_iter() + .flatten(); + arena.intersperse(parts, arena.space()) + } +} + +impl PrettyDoc for GraphMatchPathPattern { + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + match self { + GraphMatchPathPattern::Path(path) => { + arena.intersperse(path.iter().map(|e| e.pretty_doc(arena)), arena.space()) + } + GraphMatchPathPattern::Quantified(GraphMatchPathPatternQuantified { path, quant }) => { + arena.concat([path.pretty_doc(arena), quant.pretty_doc(arena)]) + } + GraphMatchPathPattern::Sub(path) => pretty_surrounded(path, "(", ")", arena), + GraphMatchPathPattern::Node(node) => node.pretty_doc(arena), + GraphMatchPathPattern::Edge(edge) => edge.pretty_doc(arena), + GraphMatchPathPattern::Union(_) => todo!("{:?}", self), + GraphMatchPathPattern::Multiset(_) => todo!("{:?}", self), + GraphMatchPathPattern::Questioned(_) => todo!("{:?}", self), + GraphMatchPathPattern::Simplified(_) => todo!("{:?}", self), + } + } +} + +impl PrettyDoc for GraphMatchNode { + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + let name: Vec<_> = [ + self.variable.as_ref().map(|var| arena.text(&var.value)), + self.label + .as_ref() + .map(|label| arena.concat([arena.text(":"), label.pretty_doc(arena)])), + ] + .into_iter() + .flatten() + .collect(); + let name = if name.is_empty() { + None + } else { + Some(arena.concat(name)) + }; + let where_clause = self.where_clause.as_ref().map(|clause| { + arena.intersperse( + [arena.text("WHERE"), clause.pretty_doc(arena)], + arena.space(), + ) + }); + let parts = [name, where_clause].into_iter().flatten(); + + let spec = arena.intersperse(parts, arena.space()); + pretty_surrounded_doc(spec, "(", ")", arena).group() + } +} + +impl PrettyDoc for GraphMatchEdge { + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + let name: Vec<_> = [ + self.variable.as_ref().map(|var| arena.text(&var.value)), + self.label + .as_ref() + .map(|label| arena.concat([arena.text(":"), label.pretty_doc(arena)])), + ] + .into_iter() + .flatten() + .collect(); + let name = if name.is_empty() { + None + } else { + Some(arena.concat(name)) + }; + let where_clause = self.where_clause.as_ref().map(|clause| { + arena.intersperse( + [arena.text("WHERE"), clause.pretty_doc(arena)], + arena.space(), + ) + }); + let parts = [name, where_clause] + .into_iter() + .flatten() + .collect::>(); + + let mut edge = if !parts.is_empty() { + let (prefix, suffix) = match self.direction { + GraphMatchDirection::Right => ("-[", "]->"), + GraphMatchDirection::Left => ("<-[", "]-"), + GraphMatchDirection::Undirected => ("~[", "]~"), + GraphMatchDirection::UndirectedOrRight => ("~[", "]~>"), + GraphMatchDirection::LeftOrUndirected => ("<~[", "]~"), + GraphMatchDirection::LeftOrRight => ("<-[", "]->"), + GraphMatchDirection::LeftOrUndirectedOrRight => ("-[", "]-"), + }; + let spec = arena.intersperse(parts, arena.space()); + pretty_surrounded_doc(spec, prefix, suffix, arena) + } else { + let edge = match self.direction { + GraphMatchDirection::Right => "->", + GraphMatchDirection::Left => "<-", + GraphMatchDirection::Undirected => "~", + GraphMatchDirection::UndirectedOrRight => "~>", + GraphMatchDirection::LeftOrUndirected => "<~", + GraphMatchDirection::LeftOrRight => "<->", + GraphMatchDirection::LeftOrUndirectedOrRight => "-", + }; + arena.text(edge) + }; + if let Some(q) = &self.quantifier { + edge = arena.concat([edge, q.pretty_doc(arena)]); + } + edge.group() + } +} + +impl PrettyDoc for GraphMatchLabel { + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + match self { + GraphMatchLabel::Name(name) => arena.text(&name.value), + GraphMatchLabel::Wildcard => todo!("{:?}", self), + GraphMatchLabel::Negated(_) => todo!("{:?}", self), + GraphMatchLabel::Conjunction(_) => todo!("{:?}", self), + GraphMatchLabel::Disjunction(_) => todo!("{:?}", self), + } + } +} + +impl PrettyDoc for GraphMatchQuantifier { + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + let GraphMatchQuantifier { lower, upper } = &self; + match (lower, upper) { + (0, None) => arena.text("*"), + (1, None) => arena.text("+"), + (l, u) => { + let l = Cow::Owned(l.to_string()); + let u = u.map(|u| Cow::Owned(u.to_string())).unwrap_or("".into()); + pretty_surrounded_doc(arena.concat([l, ",".into(), u]), "{", "}", arena) + } + } + } +} diff --git a/partiql-ast/src/pretty.rs b/partiql-ast/src/pretty/mod.rs similarity index 69% rename from partiql-ast/src/pretty.rs rename to partiql-ast/src/pretty/mod.rs index fd9ab54f..0cc372b0 100644 --- a/partiql-ast/src/pretty.rs +++ b/partiql-ast/src/pretty/mod.rs @@ -1,11 +1,11 @@ use crate::ast::*; use partiql_common::pretty::{ - pretty_doc_list, pretty_list, pretty_parenthesized_doc, pretty_prefixed_doc, pretty_seperated, - pretty_seperated_doc, pretty_seq, pretty_seq_doc, pretty_surrounded, pretty_surrounded_doc, - PrettyDoc, PRETTY_INDENT_MINOR_NEST, PRETTY_INDENT_SUBORDINATE_CLAUSE_NEST, + pretty_list, pretty_parenthesized_doc, pretty_prefixed_doc, pretty_seperated, + pretty_seperated_doc, pretty_seq, pretty_seq_doc, PrettyDoc, PRETTY_INDENT_MINOR_NEST, + PRETTY_INDENT_SUBORDINATE_CLAUSE_NEST, }; use pretty::{DocAllocator, DocBuilder}; -use std::borrow::Cow; +mod graph; impl PrettyDoc for AstNode where @@ -999,485 +999,6 @@ impl PrettyDoc for Join { } } -impl PrettyDoc for GraphMatch { - fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> - where - D: DocAllocator<'b, A>, - D::Doc: Clone, - A: Clone, - { - let GraphMatch { - expr, - pattern, - shape, - } = self; - let head = arena.intersperse([expr.pretty_doc(arena), arena.text("MATCH")], arena.space()); - let patterns = pattern.pretty_doc(arena).group(); - let mut match_expr = arena.intersperse([head, patterns], arena.space()); - let shapes = [ - shape.rows.as_ref().map(|d| d.pretty_doc(arena)), - shape.cols.as_ref().map(|d| d.pretty_doc(arena)), - shape.export.as_ref().map(|d| d.pretty_doc(arena)), - ] - .into_iter() - .flatten() - .collect::>(); - if !shapes.is_empty() { - let docs = std::iter::once(match_expr).chain(shapes); - match_expr = arena.intersperse(docs, arena.hardline()); - } - - pretty_parenthesized_doc(match_expr, arena) - } -} - -impl PrettyDoc for GraphTableRows { - fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> - where - D: DocAllocator<'b, A>, - D::Doc: Clone, - A: Clone, - { - match self { - GraphTableRows::OneRowPerMatch => arena.text("ONE ROW PER MATCH"), - GraphTableRows::OneRowPerVertex { v, in_paths } => { - let prefix = arena.text("ONE ROW PER NODE"); - let spec = pretty_parenthesized_doc(arena.text(&v.value), arena); - let in_paths = in_paths.as_ref().map(|paths| { - let paths = pretty_parenthesized_doc( - pretty_doc_list(paths.iter().map(|p| arena.text(&p.value)), 0, arena), - arena, - ); - [arena.text("IN"), paths] - }); - arena.intersperse( - [Some([prefix, spec]), in_paths] - .into_iter() - .flatten() - .flatten(), - arena.softline(), - ) - } - GraphTableRows::OneRowPerStep { - v1, - e, - v2, - in_paths, - } => { - let prefix = arena.text("ONE ROW PER STEP"); - let step = pretty_doc_list( - [v1, e, v2].into_iter().map(|n| arena.text(&n.value)), - 0, - arena, - ); - let spec = pretty_parenthesized_doc(step, arena); - let in_paths = in_paths.as_ref().map(|paths| { - let paths = - pretty_doc_list(paths.iter().map(|p| arena.text(&p.value)), 0, arena); - [arena.text("IN"), pretty_parenthesized_doc(paths, arena)] - }); - arena.intersperse( - [Some([prefix, spec]), in_paths] - .into_iter() - .flatten() - .flatten(), - arena.softline(), - ) - } - } - } -} - -impl PrettyDoc for GraphTableColumns { - fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> - where - D: DocAllocator<'b, A>, - D::Doc: Clone, - A: Clone, - { - let col_defs = pretty_list(&self.columns, 0, arena); - arena.intersperse( - [ - arena.text("COLUMNS"), - pretty_parenthesized_doc(col_defs, arena), - ], - arena.space(), - ) - } -} - -impl PrettyDoc for GraphTableColumnDef { - fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> - where - D: DocAllocator<'b, A>, - D::Doc: Clone, - A: Clone, - { - match self { - GraphTableColumnDef::Expr(expr, as_ident) => { - let parts = if let Some(as_ident) = as_ident { - vec![ - expr.pretty_doc(arena), - arena.text("AS"), - arena.text(&as_ident.value), - ] - } else { - vec![expr.pretty_doc(arena)] - }; - arena.intersperse(parts, arena.space()) - } - GraphTableColumnDef::AllProperties(_) => { - unreachable!() - } - } - } -} - -impl PrettyDoc for GraphTableExport { - fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> - where - D: DocAllocator<'b, A>, - D::Doc: Clone, - A: Clone, - { - match self { - GraphTableExport::AllSingletons { except } => { - let prefix = arena.text("EXPORT ALL SINGLETONS"); - if let Some(except) = except { - let except = - pretty_doc_list(except.iter().map(|s| arena.text(&s.value)), 0, arena); - let parts = [ - prefix, - arena.text("EXCEPT"), - pretty_parenthesized_doc(except, arena), - ]; - arena.intersperse(parts, arena.space()) - } else { - prefix - } - } - GraphTableExport::Singletons { exports } => { - let prefix = arena.text("EXPORT SINGLETONS"); - let exports = - pretty_doc_list(exports.iter().map(|e| arena.text(&e.value)), 0, arena); - let parts = [prefix, pretty_parenthesized_doc(exports, arena)]; - arena.intersperse(parts, arena.space()) - } - GraphTableExport::NoSingletons => arena.text("EXPORT NO SINGLETONS"), - } - } -} - -impl PrettyDoc for GraphPattern { - fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> - where - D: DocAllocator<'b, A>, - D::Doc: Clone, - A: Clone, - { - let GraphPattern { - mode, - patterns, - keep, - where_clause, - } = &self; - let patterns = pretty_list(patterns, PRETTY_INDENT_MINOR_NEST, arena).group(); - let parts = [ - mode.as_ref().map(|inner| inner.pretty_doc(arena)), - Some(patterns), - keep.as_ref().map(|inner| inner.pretty_doc(arena)), - where_clause.as_ref().map(|clause| { - arena.intersperse( - [arena.text("WHERE"), clause.pretty_doc(arena)], - arena.space(), - ) - }), - ] - .into_iter() - .flatten(); - - arena.intersperse(parts, arena.space()) - } -} - -impl PrettyDoc for GraphMatchMode { - fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> - where - D: DocAllocator<'b, A>, - D::Doc: Clone, - A: Clone, - { - match self { - GraphMatchMode::DifferentEdges => arena.text("DIFFERENT EDGES"), - GraphMatchMode::RepeatableElements => arena.text("REPEATABLE ELEMENTS"), - } - } -} - -impl PrettyDoc for GraphPathPrefix { - fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> - where - D: DocAllocator<'b, A>, - D::Doc: Clone, - A: Clone, - { - match self { - GraphPathPrefix::Mode(mode) => mode.pretty_doc(arena), - GraphPathPrefix::Search(search, mode) => { - let mode = mode.as_ref().map(|mode| mode.pretty_doc(arena)); - let parts = match search { - GraphPathSearchPrefix::All => vec![Some(arena.text("ALL")), mode], - GraphPathSearchPrefix::Any => vec![Some(arena.text("ANY")), mode], - GraphPathSearchPrefix::AnyK(k) => vec![ - Some(arena.text("ANY")), - Some(arena.text(k.to_string())), - mode, - ], - GraphPathSearchPrefix::AllShortest => { - vec![Some(arena.text("ALL")), Some(arena.text("SHORTEST")), mode] - } - GraphPathSearchPrefix::AnyShortest => { - vec![Some(arena.text("ANY")), Some(arena.text("SHORTEST")), mode] - } - GraphPathSearchPrefix::ShortestK(k) => vec![ - Some(arena.text("SHORTEST")), - Some(arena.text(k.to_string())), - mode, - ], - GraphPathSearchPrefix::ShortestKGroup(k) => vec![ - Some(arena.text("SHORTEST")), - k.as_ref().map(|k| arena.text(k.to_string())), - mode, - Some(arena.text("GROUPS")), - ], - } - .into_iter() - .flatten() - .collect::>(); - arena.intersperse(parts, arena.space()) - } - } - } -} - -impl PrettyDoc for GraphPathMode { - fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> - where - D: DocAllocator<'b, A>, - D::Doc: Clone, - A: Clone, - { - let txt = match self { - GraphPathMode::Walk => "WALK", - GraphPathMode::Trail => "TRAIL", - GraphPathMode::Acyclic => "ACYCLIC", - GraphPathMode::Simple => "SIMPLE", - }; - arena.text(txt) - } -} - -impl PrettyDoc for GraphPathPattern { - fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> - where - D: DocAllocator<'b, A>, - D::Doc: Clone, - A: Clone, - { - let var = self - .variable - .as_ref() - .map(|var| arena.intersperse([arena.text(&var.value), arena.text("=")], arena.space())); - - let prefix = self.prefix.as_ref().map(|prefix| prefix.pretty_doc(arena)); - - let parts = [var, prefix, Some(self.path.pretty_doc(arena))] - .into_iter() - .flatten(); - arena.intersperse(parts, arena.space()) - } -} - -impl PrettyDoc for GraphPathSubPattern { - fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> - where - D: DocAllocator<'b, A>, - D::Doc: Clone, - A: Clone, - { - let var = self - .variable - .as_ref() - .map(|var| arena.intersperse([arena.text(&var.value), arena.text("=")], arena.space())); - - let mode = self.mode.as_ref().map(|prefix| prefix.pretty_doc(arena)); - let where_clause = self.where_clause.as_ref().map(|clause| { - arena.intersperse( - [arena.text("WHERE"), clause.pretty_doc(arena)], - arena.space(), - ) - }); - - let parts = [var, mode, Some(self.path.pretty_doc(arena)), where_clause] - .into_iter() - .flatten(); - arena.intersperse(parts, arena.space()) - } -} - -impl PrettyDoc for GraphMatchPathPattern { - fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> - where - D: DocAllocator<'b, A>, - D::Doc: Clone, - A: Clone, - { - match self { - GraphMatchPathPattern::Path(path) => { - arena.intersperse(path.iter().map(|e| e.pretty_doc(arena)), arena.space()) - } - GraphMatchPathPattern::Quantified(path, quantified) => { - arena.concat([path.pretty_doc(arena), quantified.pretty_doc(arena)]) - } - GraphMatchPathPattern::Sub(path) => pretty_surrounded(path, "(", ")", arena), - GraphMatchPathPattern::Node(node) => node.pretty_doc(arena), - GraphMatchPathPattern::Edge(edge) => edge.pretty_doc(arena), - _ => todo!("{:?}", self), - } - } -} - -impl PrettyDoc for GraphMatchNode { - fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> - where - D: DocAllocator<'b, A>, - D::Doc: Clone, - A: Clone, - { - let name: Vec<_> = [ - self.variable.as_ref().map(|var| arena.text(&var.value)), - self.label - .as_ref() - .map(|label| arena.concat([arena.text(":"), label.pretty_doc(arena)])), - ] - .into_iter() - .flatten() - .collect(); - let name = if name.is_empty() { - None - } else { - Some(arena.concat(name)) - }; - let where_clause = self.where_clause.as_ref().map(|clause| { - arena.intersperse( - [arena.text("WHERE"), clause.pretty_doc(arena)], - arena.space(), - ) - }); - let parts = [name, where_clause].into_iter().flatten(); - - let spec = arena.intersperse(parts, arena.space()); - pretty_surrounded_doc(spec, "(", ")", arena).group() - } -} - -impl PrettyDoc for GraphMatchEdge { - fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> - where - D: DocAllocator<'b, A>, - D::Doc: Clone, - A: Clone, - { - let name: Vec<_> = [ - self.variable.as_ref().map(|var| arena.text(&var.value)), - self.label - .as_ref() - .map(|label| arena.concat([arena.text(":"), label.pretty_doc(arena)])), - ] - .into_iter() - .flatten() - .collect(); - let name = if name.is_empty() { - None - } else { - Some(arena.concat(name)) - }; - let where_clause = self.where_clause.as_ref().map(|clause| { - arena.intersperse( - [arena.text("WHERE"), clause.pretty_doc(arena)], - arena.space(), - ) - }); - let parts = [name, where_clause] - .into_iter() - .flatten() - .collect::>(); - - let mut edge = if !parts.is_empty() { - let (prefix, suffix) = match self.direction { - GraphMatchDirection::Right => ("-[", "]->"), - GraphMatchDirection::Left => ("<-[", "]-"), - GraphMatchDirection::Undirected => ("~[", "]~"), - GraphMatchDirection::UndirectedOrRight => ("~[", "]~>"), - GraphMatchDirection::LeftOrUndirected => ("<~[", "]~"), - GraphMatchDirection::LeftOrRight => ("<-[", "]->"), - GraphMatchDirection::LeftOrUndirectedOrRight => ("-[", "]-"), - }; - let spec = arena.intersperse(parts, arena.space()); - pretty_surrounded_doc(spec, prefix, suffix, arena) - } else { - let edge = match self.direction { - GraphMatchDirection::Right => "->", - GraphMatchDirection::Left => "<-", - GraphMatchDirection::Undirected => "~", - GraphMatchDirection::UndirectedOrRight => "~>", - GraphMatchDirection::LeftOrUndirected => "<~", - GraphMatchDirection::LeftOrRight => "<->", - GraphMatchDirection::LeftOrUndirectedOrRight => "-", - }; - arena.text(edge) - }; - if let Some(q) = &self.quantifier { - edge = arena.concat([edge, q.pretty_doc(arena)]); - } - edge.group() - } -} - -impl PrettyDoc for GraphMatchLabel { - fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> - where - D: DocAllocator<'b, A>, - D::Doc: Clone, - A: Clone, - { - match self { - GraphMatchLabel::Name(name) => arena.text(&name.value), - _ => todo!("{:?}", self), - } - } -} - -impl PrettyDoc for GraphMatchQuantifier { - fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> - where - D: DocAllocator<'b, A>, - D::Doc: Clone, - A: Clone, - { - let GraphMatchQuantifier { lower, upper } = &self; - match (lower, upper) { - (0, None) => arena.text("*"), - (1, None) => arena.text("+"), - (l, u) => { - let l = Cow::Owned(l.to_string()); - let u = u.map(|u| Cow::Owned(u.to_string())).unwrap_or("".into()); - pretty_surrounded_doc(arena.concat([l, ",".into(), u]), "{", "}", arena) - } - } - } -} - impl PrettyDoc for Let { fn pretty_doc<'b, D, A>(&'b self, _arena: &'b D) -> DocBuilder<'b, D, A> where diff --git a/partiql-ast/src/visit.rs b/partiql-ast/src/visit.rs index 0992b064..3caac65e 100644 --- a/partiql-ast/src/visit.rs +++ b/partiql-ast/src/visit.rs @@ -514,51 +514,87 @@ pub trait Visitor<'ast> { Traverse::Continue } - fn enter_graph_match(&mut self, _graph_pattern: &'ast ast::GraphMatch) -> Traverse { + fn enter_graph_table(&mut self, _gtable: &'ast ast::GraphTable) -> Traverse { Traverse::Continue } - fn exit_graph_match(&mut self, _graph_pattern: &'ast ast::GraphMatch) -> Traverse { + fn exit_graph_table(&mut self, _gtable: &'ast ast::GraphTable) -> Traverse { Traverse::Continue } - fn enter_graph_match_pattern( - &mut self, - _graph_pattern: &'ast ast::GraphPathPattern, - ) -> Traverse { + fn enter_graph_match(&mut self, _gmatch: &'ast ast::GraphMatch) -> Traverse { + Traverse::Continue + } + fn exit_graph_match(&mut self, _gmatch: &'ast ast::GraphMatch) -> Traverse { + Traverse::Continue + } + + fn enter_graph_pattern(&mut self, _graph_pattern: &'ast ast::GraphPattern) -> Traverse { + Traverse::Continue + } + fn exit_graph_pattern(&mut self, _graph_pattern: &'ast ast::GraphPattern) -> Traverse { Traverse::Continue } - fn exit_graph_match_pattern( + + fn enter_graph_path_pattern( &mut self, _graph_pattern: &'ast ast::GraphPathPattern, ) -> Traverse { Traverse::Continue } - fn enter_graph_match_node(&mut self, _graph_pattern: &'ast ast::GraphMatchNode) -> Traverse { + fn exit_graph_path_pattern(&mut self, _graph_pattern: &'ast ast::GraphPathPattern) -> Traverse { Traverse::Continue } - fn exit_graph_match_node(&mut self, _graph_pattern: &'ast ast::GraphMatchNode) -> Traverse { + + fn enter_graph_path_sub_pattern( + &mut self, + _graph_pattern: &'ast ast::GraphPathSubPattern, + ) -> Traverse { Traverse::Continue } - fn enter_graph_match_edge(&mut self, _graph_pattern: &'ast ast::GraphMatchEdge) -> Traverse { + fn exit_graph_path_sub_pattern( + &mut self, + _graph_pattern: &'ast ast::GraphPathSubPattern, + ) -> Traverse { Traverse::Continue } - fn exit_graph_match_edge(&mut self, _graph_pattern: &'ast ast::GraphMatchEdge) -> Traverse { + + fn enter_graph_match_path_pattern( + &mut self, + _graph_pattern: &'ast ast::GraphMatchPathPattern, + ) -> Traverse { + Traverse::Continue + } + fn exit_graph_match_path_pattern( + &mut self, + _graph_pattern: &'ast ast::GraphMatchPathPattern, + ) -> Traverse { Traverse::Continue } - //TODO - fn enter_graph_match_element( + fn enter_graph_match_path_pattern_quantified( &mut self, - _graph_pattern: &'ast ast::GraphMatchElement, + _graph_pattern: &'ast ast::GraphMatchPathPatternQuantified, ) -> Traverse { Traverse::Continue } - fn exit_graph_match_element( + fn exit_graph_match_path_pattern_quantified( &mut self, - _graph_pattern: &'ast ast::GraphMatchElement, + _graph_pattern: &'ast ast::GraphMatchPathPatternQuantified, ) -> Traverse { Traverse::Continue } + fn enter_graph_match_node(&mut self, _graph_pattern: &'ast ast::GraphMatchNode) -> Traverse { + Traverse::Continue + } + fn exit_graph_match_node(&mut self, _graph_pattern: &'ast ast::GraphMatchNode) -> Traverse { + Traverse::Continue + } + fn enter_graph_match_edge(&mut self, _graph_pattern: &'ast ast::GraphMatchEdge) -> Traverse { + Traverse::Continue + } + fn exit_graph_match_edge(&mut self, _graph_pattern: &'ast ast::GraphMatchEdge) -> Traverse { + Traverse::Continue + } fn enter_group_by_expr(&mut self, _group_by_expr: &'ast ast::GroupByExpr) -> Traverse { Traverse::Continue diff --git a/partiql-logical-planner/src/graph.rs b/partiql-logical-planner/src/graph.rs index a6207bd4..f64e6531 100644 --- a/partiql-logical-planner/src/graph.rs +++ b/partiql-logical-planner/src/graph.rs @@ -1,6 +1,6 @@ use num::Integer; use partiql_ast::ast; -use partiql_ast::ast::{GraphMatchDirection, GraphMatchLabel, GraphMatchPathPattern}; + use partiql_logical::graph::bind_name::FreshBinder; use partiql_logical::graph::{ BindSpec, DirectionFilter, EdgeFilter, EdgeMatch, LabelFilter, NodeFilter, NodeMatch, @@ -240,29 +240,29 @@ impl GraphToLogical { pattern: &ast::GraphMatchPathPattern, ) -> Result, String> { match pattern { - GraphMatchPathPattern::Path(path) => { + ast::GraphMatchPathPattern::Path(path) => { let path: Result>, _> = path .iter() .map(|elt| self.plan_graph_match_path_pattern(elt)) .collect(); Ok(path?.into_iter().flatten().collect()) } - GraphMatchPathPattern::Union(_) => { + ast::GraphMatchPathPattern::Union(_) => { not_yet_implemented_result!("MATCH expression UNION is not yet supported."); } - GraphMatchPathPattern::Multiset(_) => { + ast::GraphMatchPathPattern::Multiset(_) => { not_yet_implemented_result!("MATCH expression MULTISET is not yet supported."); } - GraphMatchPathPattern::Questioned(_) => { + ast::GraphMatchPathPattern::Questioned(_) => { not_yet_implemented_result!("MATCH expression QUESTIONED is not yet supported."); } - GraphMatchPathPattern::Quantified(_, _) => { + ast::GraphMatchPathPattern::Quantified(_) => { not_yet_implemented_result!("MATCH expression QUANTIFIED is not yet supported."); } - GraphMatchPathPattern::Sub(subpath) => self.plan_graph_subpath_pattern(subpath), - GraphMatchPathPattern::Node(n) => self.plan_graph_pattern_part_node(n), - GraphMatchPathPattern::Edge(e) => self.plan_graph_pattern_part_edge(e), - GraphMatchPathPattern::Simplified(_) => { + ast::GraphMatchPathPattern::Sub(subpath) => self.plan_graph_subpath_pattern(subpath), + ast::GraphMatchPathPattern::Node(n) => self.plan_graph_pattern_part_node(n), + ast::GraphMatchPathPattern::Edge(e) => self.plan_graph_pattern_part_edge(e), + ast::GraphMatchPathPattern::Simplified(_) => { not_yet_implemented_result!( "MATCH expression Simplified Edge Expressions are not yet supported." ); @@ -272,23 +272,23 @@ impl GraphToLogical { fn plan_graph_pattern_label( &self, - label: Option<&GraphMatchLabel>, + label: Option<&ast::GraphMatchLabel>, ) -> Result { if let Some(label) = label { match label { - GraphMatchLabel::Name(n) => Ok(LabelFilter::Named(n.value.clone())), - GraphMatchLabel::Wildcard => Ok(LabelFilter::Always), - GraphMatchLabel::Negated(_) => { + ast::GraphMatchLabel::Name(n) => Ok(LabelFilter::Named(n.value.clone())), + ast::GraphMatchLabel::Wildcard => Ok(LabelFilter::Always), + ast::GraphMatchLabel::Negated(_) => { not_yet_implemented_result!( "MATCH expression label negation is not yet supported." ); } - GraphMatchLabel::Conjunction(_) => { + ast::GraphMatchLabel::Conjunction(_) => { not_yet_implemented_result!( "MATCH expression label conjunction is not yet supported." ); } - GraphMatchLabel::Disjunction(_) => { + ast::GraphMatchLabel::Disjunction(_) => { not_yet_implemented_result!( "MATCH expression label disjunction is not yet supported." ); @@ -332,13 +332,13 @@ impl GraphToLogical { not_yet_implemented_result!("MATCH edge where_clauses are not yet supported."); } let direction = match &edge.direction { - GraphMatchDirection::Left => DirectionFilter::L, - GraphMatchDirection::Undirected => DirectionFilter::U, - GraphMatchDirection::Right => DirectionFilter::R, - GraphMatchDirection::LeftOrUndirected => DirectionFilter::LU, - GraphMatchDirection::UndirectedOrRight => DirectionFilter::UR, - GraphMatchDirection::LeftOrRight => DirectionFilter::LR, - GraphMatchDirection::LeftOrUndirectedOrRight => DirectionFilter::LUR, + ast::GraphMatchDirection::Left => DirectionFilter::L, + ast::GraphMatchDirection::Undirected => DirectionFilter::U, + ast::GraphMatchDirection::Right => DirectionFilter::R, + ast::GraphMatchDirection::LeftOrUndirected => DirectionFilter::LU, + ast::GraphMatchDirection::UndirectedOrRight => DirectionFilter::UR, + ast::GraphMatchDirection::LeftOrRight => DirectionFilter::LR, + ast::GraphMatchDirection::LeftOrUndirectedOrRight => DirectionFilter::LUR, }; let binder = match &edge.variable { None => self.graph_id.node(), diff --git a/partiql-logical-planner/src/lower.rs b/partiql-logical-planner/src/lower.rs index 8ee0b42e..5baf792d 100644 --- a/partiql-logical-planner/src/lower.rs +++ b/partiql-logical-planner/src/lower.rs @@ -6,11 +6,11 @@ use partiql_ast::ast; use partiql_ast::ast::{ Assignment, Bag, BagOpExpr, BagOperator, Between, BinOp, BinOpKind, Call, CallAgg, CallArg, CallArgNamed, CaseSensitivity, CreateIndex, CreateTable, Ddl, DdlOp, Delete, Dml, DmlOp, - DropIndex, DropTable, Exclusion, Expr, FromClause, FromLet, FromLetKind, GraphMatch, - GroupByExpr, GroupKey, GroupingStrategy, Insert, InsertValue, Item, Join, JoinKind, JoinSpec, - Like, List, Lit, NullOrderingSpec, OnConflict, OrderByExpr, OrderingSpec, Path, PathStep, - ProjectExpr, Projection, ProjectionKind, Query, QuerySet, Remove, SearchedCase, Select, Set, - SetQuantifier, Sexp, SimpleCase, SortSpec, Struct, SymbolPrimitive, UniOp, UniOpKind, VarRef, + DropIndex, DropTable, Exclusion, Expr, FromClause, FromLet, FromLetKind, GroupByExpr, GroupKey, + GroupingStrategy, Insert, InsertValue, Item, Join, JoinKind, JoinSpec, Like, List, Lit, + NullOrderingSpec, OnConflict, OrderByExpr, OrderingSpec, Path, PathStep, ProjectExpr, + Projection, ProjectionKind, Query, QuerySet, Remove, SearchedCase, Select, Set, SetQuantifier, + Sexp, SimpleCase, SortSpec, Struct, SymbolPrimitive, UniOp, UniOpKind, VarRef, }; use partiql_ast::visit::{Traverse, Visit, Visitor}; use partiql_logical as logical; @@ -1900,11 +1900,11 @@ impl<'ast> Visitor<'ast> for AstToLogical<'_> { Traverse::Continue } - fn enter_graph_match(&mut self, _graph_pattern: &'ast GraphMatch) -> Traverse { + fn enter_graph_match(&mut self, _graph_pattern: &'ast ast::GraphMatch) -> Traverse { self.enter_env(); Traverse::Continue } - fn exit_graph_match(&mut self, graph_match: &'ast GraphMatch) -> Traverse { + fn exit_graph_match(&mut self, graph_match: &'ast ast::GraphMatch) -> Traverse { let mut env = self.exit_env(); true_or_fault!(self, env.len() == 1, "env.len() is not 1"); diff --git a/partiql-parser/src/parse/partiql.lalrpop b/partiql-parser/src/parse/partiql.lalrpop index a29bf4fc..a1c5ab84 100644 --- a/partiql-parser/src/parse/partiql.lalrpop +++ b/partiql-parser/src/parse/partiql.lalrpop @@ -512,8 +512,8 @@ GraphPathTerm: ast::AstNode = { GraphPathFactor: ast::AstNode = { , - - => state.node(ast::GraphMatchPathPattern::Quantified(Box::new(p), q), lo..hi), + + => state.node(ast::GraphMatchPathPattern::Quantified(ast::GraphMatchPathPatternQuantified{path: Box::new(p), quant}), lo..hi), "?" => state.node(ast::GraphMatchPathPattern::Questioned(Box::new(p)), lo..hi), } diff --git a/partiql/tests/pretty.rs b/partiql/tests/pretty.rs index ebb8659e..0be2dc5c 100644 --- a/partiql/tests/pretty.rs +++ b/partiql/tests/pretty.rs @@ -500,6 +500,22 @@ mod graph { } parse!("SELECT * FROM (g MATCH ALL SHORTEST ( (x)-[e]->*(y) ))"); parse!("SELECT * FROM (g MATCH ALL SHORTEST ( TRAIL (x)-[e]->*(y) ))"); + parse!( + "SELECT * FROM (g MATCH \ + REPEATABLE ELEMENTS \ + ALL SHORTEST ( (x)-[e]->*(y) ) \ + KEEP ANY SIMPLE PATHS \ + WHERE x.foo = 'bar' + )" + ); + parse!( + "SELECT * FROM (g MATCH \ + DIFFERENT EDGES \ + ALL SHORTEST ( (x)-[e]->*(y) ) \ + KEEP ANY SIMPLE PATHS \ + WHERE x.foo = 'bar' + )" + ); } #[test] fn shapes() { diff --git a/partiql/tests/snapshots/pretty__graph_etc_3.snap b/partiql/tests/snapshots/pretty__graph_etc_3.snap new file mode 100644 index 00000000..dc209ee0 --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_etc_3.snap @@ -0,0 +1,34 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT * FROM (g MATCH REPEATABLE ELEMENTS ALL SHORTEST ( (x)-[e]->*(y) ) KEEP ANY SIMPLE PATHS WHERE x.foo = 'bar' + ) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH REPEATABLE ELEMENTS ALL SHORTEST ((x) -[e]->* (y)) KEEP ANY SIMPLE WHERE (x.foo = 'bar')) + +------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH REPEATABLE ELEMENTS ALL SHORTEST ((x) -[e]->* (y)) KEEP ANY SIMPLE WHERE (x.foo = 'bar')) + +-------------------------------------------------------------------------------- +SELECT * +FROM (g MATCH REPEATABLE ELEMENTS ALL SHORTEST ((x) -[e]->* (y)) KEEP ANY SIMPLE WHERE (x.foo = 'bar')) + +---------------------------------------- +SELECT * +FROM (g MATCH REPEATABLE ELEMENTS ALL SHORTEST ((x) -[e]->* (y)) KEEP ANY SIMPLE WHERE (x.foo = 'bar')) + +------------------------------ +SELECT * +FROM (g MATCH REPEATABLE ELEMENTS ALL SHORTEST ((x) -[e]->* (y)) KEEP ANY SIMPLE WHERE (x.foo = 'bar')) + +-------------------- +SELECT * +FROM (g MATCH REPEATABLE ELEMENTS ALL SHORTEST ((x) -[e]->* (y)) KEEP ANY SIMPLE WHERE (x.foo = 'bar')) + +---------- +SELECT * +FROM (g MATCH REPEATABLE ELEMENTS ALL SHORTEST ((x) -[e]->* (y)) KEEP ANY SIMPLE WHERE (x.foo = 'bar')) diff --git a/partiql/tests/snapshots/pretty__graph_etc_4.snap b/partiql/tests/snapshots/pretty__graph_etc_4.snap new file mode 100644 index 00000000..db97daa7 --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_etc_4.snap @@ -0,0 +1,34 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT * FROM (g MATCH DIFFERENT EDGES ALL SHORTEST ( (x)-[e]->*(y) ) KEEP ANY SIMPLE PATHS WHERE x.foo = 'bar' + ) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH DIFFERENT EDGES ALL SHORTEST ((x) -[e]->* (y)) KEEP ANY SIMPLE WHERE (x.foo = 'bar')) + +------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH DIFFERENT EDGES ALL SHORTEST ((x) -[e]->* (y)) KEEP ANY SIMPLE WHERE (x.foo = 'bar')) + +-------------------------------------------------------------------------------- +SELECT * +FROM (g MATCH DIFFERENT EDGES ALL SHORTEST ((x) -[e]->* (y)) KEEP ANY SIMPLE WHERE (x.foo = 'bar')) + +---------------------------------------- +SELECT * +FROM (g MATCH DIFFERENT EDGES ALL SHORTEST ((x) -[e]->* (y)) KEEP ANY SIMPLE WHERE (x.foo = 'bar')) + +------------------------------ +SELECT * +FROM (g MATCH DIFFERENT EDGES ALL SHORTEST ((x) -[e]->* (y)) KEEP ANY SIMPLE WHERE (x.foo = 'bar')) + +-------------------- +SELECT * +FROM (g MATCH DIFFERENT EDGES ALL SHORTEST ((x) -[e]->* (y)) KEEP ANY SIMPLE WHERE (x.foo = 'bar')) + +---------- +SELECT * +FROM (g MATCH DIFFERENT EDGES ALL SHORTEST ((x) -[e]->* (y)) KEEP ANY SIMPLE WHERE (x.foo = 'bar')) From d4786df7e33c3b44e5eb33be7ada50f4f962819f Mon Sep 17 00:00:00 2001 From: Josh Pschorr Date: Mon, 7 Apr 2025 11:43:56 -0700 Subject: [PATCH 9/9] Add pretty-printing for simplified path patterns --- partiql-ast/src/ast/graph.rs | 28 +++-- partiql-ast/src/pretty/graph.rs | 118 ++++++++++++++++-- partiql-logical-planner/src/graph.rs | 3 - partiql-parser/src/parse/partiql.lalrpop | 29 +++-- partiql/tests/pretty.rs | 51 ++++++++ .../tests/snapshots/pretty__graph_etc_5.snap | 32 +++++ .../tests/snapshots/pretty__graph_etc_6.snap | 32 +++++ .../snapshots/pretty__graph_filters_3.snap | 39 ++++++ .../snapshots/pretty__graph_labels_1.snap | 33 +++++ .../snapshots/pretty__graph_labels_2.snap | 33 +++++ .../snapshots/pretty__graph_labels_3.snap | 33 +++++ .../snapshots/pretty__graph_labels_4.snap | 33 +++++ .../snapshots/pretty__graph_labels_5.snap | 33 +++++ .../snapshots/pretty__graph_path_mode_4.snap | 33 +++++ .../pretty__graph_quantifiers_9.snap | 33 +++++ .../pretty__graph_search_prefix_7.snap | 33 +++++ .../snapshots/pretty__graph_simplified_1.snap | 32 +++++ .../snapshots/pretty__graph_simplified_2.snap | 32 +++++ .../snapshots/pretty__graph_simplified_3.snap | 32 +++++ .../snapshots/pretty__graph_simplified_4.snap | 32 +++++ .../snapshots/pretty__graph_simplified_5.snap | 32 +++++ .../snapshots/pretty__graph_simplified_6.snap | 32 +++++ .../snapshots/pretty__graph_simplified_7.snap | 32 +++++ .../snapshots/pretty__graph_simplified_8.snap | 32 +++++ .../snapshots/pretty__graph_simplified_9.snap | 32 +++++ 25 files changed, 847 insertions(+), 37 deletions(-) create mode 100644 partiql/tests/snapshots/pretty__graph_etc_5.snap create mode 100644 partiql/tests/snapshots/pretty__graph_etc_6.snap create mode 100644 partiql/tests/snapshots/pretty__graph_filters_3.snap create mode 100644 partiql/tests/snapshots/pretty__graph_labels_1.snap create mode 100644 partiql/tests/snapshots/pretty__graph_labels_2.snap create mode 100644 partiql/tests/snapshots/pretty__graph_labels_3.snap create mode 100644 partiql/tests/snapshots/pretty__graph_labels_4.snap create mode 100644 partiql/tests/snapshots/pretty__graph_labels_5.snap create mode 100644 partiql/tests/snapshots/pretty__graph_path_mode_4.snap create mode 100644 partiql/tests/snapshots/pretty__graph_quantifiers_9.snap create mode 100644 partiql/tests/snapshots/pretty__graph_search_prefix_7.snap create mode 100644 partiql/tests/snapshots/pretty__graph_simplified_1.snap create mode 100644 partiql/tests/snapshots/pretty__graph_simplified_2.snap create mode 100644 partiql/tests/snapshots/pretty__graph_simplified_3.snap create mode 100644 partiql/tests/snapshots/pretty__graph_simplified_4.snap create mode 100644 partiql/tests/snapshots/pretty__graph_simplified_5.snap create mode 100644 partiql/tests/snapshots/pretty__graph_simplified_6.snap create mode 100644 partiql/tests/snapshots/pretty__graph_simplified_7.snap create mode 100644 partiql/tests/snapshots/pretty__graph_simplified_8.snap create mode 100644 partiql/tests/snapshots/pretty__graph_simplified_9.snap diff --git a/partiql-ast/src/ast/graph.rs b/partiql-ast/src/ast/graph.rs index d6f3519c..f93cbe12 100644 --- a/partiql-ast/src/ast/graph.rs +++ b/partiql-ast/src/ast/graph.rs @@ -156,9 +156,6 @@ pub struct GraphMatchEdge { /// edge direction #[visit(skip)] pub direction: GraphMatchDirection, - /// an optional quantifier for the edge match - #[visit(skip)] - pub quantifier: Option>, /// the optional element variable of the edge match, e.g.: `t` in `MATCH −[t]−>` #[visit(skip)] pub variable: Option, @@ -257,25 +254,34 @@ pub enum GraphMatchSimplifiedPattern { Multiset(Vec>), Path(Vec>), + Sub(Box>), Conjunction(Vec>), Questioned(Box>), - Quantified( - Box>, - AstNode, - ), + Quantified(GraphMatchSimplifiedPatternQuantified), /// Direction override - Direction( - GraphMatchDirection, - Box>, - ), + Direction(GraphMatchSimplifiedPatternDirected), Negated(Box>), Label(SymbolPrimitive), } +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct GraphMatchSimplifiedPatternQuantified { + pub path: Box>, + pub quant: AstNode, +} + +#[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct GraphMatchSimplifiedPatternDirected { + pub dir: GraphMatchDirection, + pub path: Box>, +} + #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum GraphPathPrefix { diff --git a/partiql-ast/src/pretty/graph.rs b/partiql-ast/src/pretty/graph.rs index 1d081c01..1694d95d 100644 --- a/partiql-ast/src/pretty/graph.rs +++ b/partiql-ast/src/pretty/graph.rs @@ -1,4 +1,5 @@ use crate::ast::*; +use crate::pretty::pretty_parenthesized_expr; use partiql_common::pretty::{ pretty_doc_list, pretty_list, pretty_parenthesized_doc, pretty_surrounded, pretty_surrounded_doc, PrettyDoc, PRETTY_INDENT_MINOR_NEST, @@ -348,13 +349,101 @@ impl PrettyDoc for GraphMatchPathPattern { GraphMatchPathPattern::Quantified(GraphMatchPathPatternQuantified { path, quant }) => { arena.concat([path.pretty_doc(arena), quant.pretty_doc(arena)]) } + GraphMatchPathPattern::Questioned(p) => { + arena.concat([p.pretty_doc(arena), arena.text("?")]) + } GraphMatchPathPattern::Sub(path) => pretty_surrounded(path, "(", ")", arena), GraphMatchPathPattern::Node(node) => node.pretty_doc(arena), GraphMatchPathPattern::Edge(edge) => edge.pretty_doc(arena), - GraphMatchPathPattern::Union(_) => todo!("{:?}", self), - GraphMatchPathPattern::Multiset(_) => todo!("{:?}", self), - GraphMatchPathPattern::Questioned(_) => todo!("{:?}", self), - GraphMatchPathPattern::Simplified(_) => todo!("{:?}", self), + GraphMatchPathPattern::Union(u) => { + arena.intersperse(u.iter().map(|l| l.pretty_doc(arena)), arena.text(" | ")) + } + GraphMatchPathPattern::Multiset(s) => { + arena.intersperse(s.iter().map(|l| l.pretty_doc(arena)), arena.text(" |+| ")) + } + GraphMatchPathPattern::Simplified(simplified) => simplified.pretty_doc(arena), + } + } +} + +impl PrettyDoc for GraphMatchSimplified { + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + let ends = match self.dir { + GraphMatchDirection::Left => ["<-/", "/-"], + GraphMatchDirection::Undirected => ["~/", "/~"], + GraphMatchDirection::Right => ["-/", "/->"], + GraphMatchDirection::LeftOrUndirected => ["<~/", "/~"], + GraphMatchDirection::UndirectedOrRight => ["~/", "/~>"], + GraphMatchDirection::LeftOrRight => ["<-/", "/->"], + GraphMatchDirection::LeftOrUndirectedOrRight => ["-/", "/-"], + }; + + let parts = [ + arena.text(ends[0]), + self.pattern.pretty_doc(arena), + arena.text(ends[1]), + ]; + arena.intersperse(parts, arena.space()).group() + } +} + +impl PrettyDoc for GraphMatchSimplifiedPattern { + fn pretty_doc<'b, D, A>(&'b self, arena: &'b D) -> DocBuilder<'b, D, A> + where + D: DocAllocator<'b, A>, + D::Doc: Clone, + A: Clone, + { + match self { + GraphMatchSimplifiedPattern::Union(u) => { + arena.intersperse(u.iter().map(|l| l.pretty_doc(arena)), arena.text(" | ")) + } + GraphMatchSimplifiedPattern::Multiset(s) => { + arena.intersperse(s.iter().map(|l| l.pretty_doc(arena)), arena.text(" |+| ")) + } + GraphMatchSimplifiedPattern::Path(path) => { + arena.intersperse(path.iter().map(|e| e.pretty_doc(arena)), arena.space()) + } + GraphMatchSimplifiedPattern::Sub(path) => pretty_surrounded(path, "(", ")", arena), + GraphMatchSimplifiedPattern::Conjunction(c) => { + arena.intersperse(c.iter().map(|l| l.pretty_doc(arena)), arena.text("&")) + } + GraphMatchSimplifiedPattern::Questioned(p) => { + arena.concat([p.pretty_doc(arena), arena.text("?")]) + } + GraphMatchSimplifiedPattern::Quantified(GraphMatchSimplifiedPatternQuantified { + path, + quant, + }) => arena.concat([path.pretty_doc(arena), quant.pretty_doc(arena)]), + GraphMatchSimplifiedPattern::Direction(GraphMatchSimplifiedPatternDirected { + dir, + path, + }) => { + let path = path.pretty_doc(arena); + let parts = match dir { + GraphMatchDirection::Left => vec![arena.text("<"), path], + GraphMatchDirection::Undirected => vec![arena.text("~"), path], + GraphMatchDirection::Right => vec![path, arena.text(">")], + GraphMatchDirection::LeftOrUndirected => vec![arena.text("<~"), path], + GraphMatchDirection::UndirectedOrRight => { + vec![arena.text("~"), path, arena.text(">")] + } + GraphMatchDirection::LeftOrRight => { + vec![arena.text("<"), path, arena.text(">")] + } + GraphMatchDirection::LeftOrUndirectedOrRight => vec![arena.text("-"), path], + }; + arena.concat(parts).group() + } + GraphMatchSimplifiedPattern::Negated(l) => { + arena.concat([arena.text("!"), l.pretty_doc(arena)]) + } + GraphMatchSimplifiedPattern::Label(l) => arena.text(&l.value), } } } @@ -425,7 +514,7 @@ impl PrettyDoc for GraphMatchEdge { .flatten() .collect::>(); - let mut edge = if !parts.is_empty() { + let edge = if !parts.is_empty() { let (prefix, suffix) = match self.direction { GraphMatchDirection::Right => ("-[", "]->"), GraphMatchDirection::Left => ("<-[", "]-"), @@ -449,9 +538,6 @@ impl PrettyDoc for GraphMatchEdge { }; arena.text(edge) }; - if let Some(q) = &self.quantifier { - edge = arena.concat([edge, q.pretty_doc(arena)]); - } edge.group() } } @@ -465,10 +551,18 @@ impl PrettyDoc for GraphMatchLabel { { match self { GraphMatchLabel::Name(name) => arena.text(&name.value), - GraphMatchLabel::Wildcard => todo!("{:?}", self), - GraphMatchLabel::Negated(_) => todo!("{:?}", self), - GraphMatchLabel::Conjunction(_) => todo!("{:?}", self), - GraphMatchLabel::Disjunction(_) => todo!("{:?}", self), + GraphMatchLabel::Wildcard => arena.text("%"), + GraphMatchLabel::Negated(l) => { + arena.concat([arena.text("!"), pretty_parenthesized_expr(l, 0, arena)]) + } + GraphMatchLabel::Conjunction(c) => pretty_parenthesized_doc( + arena.intersperse(c.iter().map(|l| l.pretty_doc(arena)), arena.text("&")), + arena, + ), + GraphMatchLabel::Disjunction(d) => pretty_parenthesized_doc( + arena.intersperse(d.iter().map(|l| l.pretty_doc(arena)), arena.text("|")), + arena, + ), } } } diff --git a/partiql-logical-planner/src/graph.rs b/partiql-logical-planner/src/graph.rs index f64e6531..e38b2fb5 100644 --- a/partiql-logical-planner/src/graph.rs +++ b/partiql-logical-planner/src/graph.rs @@ -325,9 +325,6 @@ impl GraphToLogical { &self, edge: &ast::GraphMatchEdge, ) -> Result, String> { - if edge.quantifier.is_some() { - not_yet_implemented_result!("MATCH edge quantifiers are not yet supported."); - } if edge.where_clause.is_some() { not_yet_implemented_result!("MATCH edge where_clauses are not yet supported."); } diff --git a/partiql-parser/src/parse/partiql.lalrpop b/partiql-parser/src/parse/partiql.lalrpop index a1c5ab84..bd94aee8 100644 --- a/partiql-parser/src/parse/partiql.lalrpop +++ b/partiql-parser/src/parse/partiql.lalrpop @@ -577,7 +577,7 @@ GraphFullEdgePattern: ast::AstNode = { GraphEdgePatternFiller: ast::GraphMatchEdge = { => { let ast::GraphMatchElement{variable, label, where_clause} = pat; - ast::GraphMatchEdge {direction: ast::GraphMatchDirection::Undirected, quantifier: None, variable, label, where_clause} + ast::GraphMatchEdge {direction: ast::GraphMatchDirection::Undirected, variable, label, where_clause} } } @@ -585,7 +585,6 @@ GraphAbbreviatedEdgePattern: ast::AstNode = { => { state.node(ast::GraphMatchEdge { direction, - quantifier: None, variable: None, label: None, where_clause: None, @@ -757,19 +756,27 @@ GraphSimplifiedConjunction: ast::AstNode = { GraphSimplifiedFactorHigh: ast::AstNode = { , - => state.node(ast::GraphMatchSimplifiedPattern::Quantified(Box::new(s), q), lo..hi), + => + state.node(ast::GraphMatchSimplifiedPattern::Quantified(ast::GraphMatchSimplifiedPatternQuantified{path: Box::new(s), quant}), lo..hi), "?" => state.node(ast::GraphMatchSimplifiedPattern::Questioned(Box::new(s)), lo..hi), } GraphSimplifiedTertiary: ast::AstNode = { , - "<" => state.node(ast::GraphMatchSimplifiedPattern::Direction(ast::GraphMatchDirection::Left, Box::new(s)), lo..hi), - "~" => state.node(ast::GraphMatchSimplifiedPattern::Direction(ast::GraphMatchDirection::Undirected, Box::new(s)), lo..hi), - ">" => state.node(ast::GraphMatchSimplifiedPattern::Direction(ast::GraphMatchDirection::Right, Box::new(s)), lo..hi), - "<~" => state.node(ast::GraphMatchSimplifiedPattern::Direction(ast::GraphMatchDirection::LeftOrUndirected, Box::new(s)), lo..hi), - "~" ">" => state.node(ast::GraphMatchSimplifiedPattern::Direction(ast::GraphMatchDirection::UndirectedOrRight, Box::new(s)), lo..hi), - "<" ">" => state.node(ast::GraphMatchSimplifiedPattern::Direction(ast::GraphMatchDirection::LeftOrRight, Box::new(s)), lo..hi), - "-" => state.node(ast::GraphMatchSimplifiedPattern::Direction(ast::GraphMatchDirection::LeftOrUndirectedOrRight, Box::new(s)), lo..hi), + "<" + => state.node(ast::GraphMatchSimplifiedPattern::Direction(ast::GraphMatchSimplifiedPatternDirected{dir: ast::GraphMatchDirection::Left, path: Box::new(s)}), lo..hi), + "~" + => state.node(ast::GraphMatchSimplifiedPattern::Direction(ast::GraphMatchSimplifiedPatternDirected{dir: ast::GraphMatchDirection::Undirected, path: Box::new(s)}), lo..hi), + ">" + => state.node(ast::GraphMatchSimplifiedPattern::Direction(ast::GraphMatchSimplifiedPatternDirected{dir: ast::GraphMatchDirection::Right, path: Box::new(s)}), lo..hi), + "<~" + => state.node(ast::GraphMatchSimplifiedPattern::Direction(ast::GraphMatchSimplifiedPatternDirected{dir: ast::GraphMatchDirection::LeftOrUndirected, path: Box::new(s)}), lo..hi), + "~" ">" + => state.node(ast::GraphMatchSimplifiedPattern::Direction(ast::GraphMatchSimplifiedPatternDirected{dir: ast::GraphMatchDirection::UndirectedOrRight, path: Box::new(s)}), lo..hi), + "<" ">" + => state.node(ast::GraphMatchSimplifiedPattern::Direction(ast::GraphMatchSimplifiedPatternDirected{dir: ast::GraphMatchDirection::LeftOrRight, path: Box::new(s)}), lo..hi), + "-" + => state.node(ast::GraphMatchSimplifiedPattern::Direction(ast::GraphMatchSimplifiedPatternDirected{dir: ast::GraphMatchDirection::LeftOrUndirectedOrRight, path: Box::new(s)}), lo..hi), } GraphSimplifiedSecondary: ast::AstNode = { @@ -785,7 +792,7 @@ GraphSimplifiedSecondary: ast::AstNode = { #[inline] GraphSimplifiedPrimary: ast::AstNode = { => state.node(ast::GraphMatchSimplifiedPattern::Label(l), lo..hi), - "(" ")", + "(" ")" => state.node(ast::GraphMatchSimplifiedPattern::Sub(Box::new(path)), lo..hi), } // 10.10 diff --git a/partiql/tests/pretty.rs b/partiql/tests/pretty.rs index 0be2dc5c..63ca3fe4 100644 --- a/partiql/tests/pretty.rs +++ b/partiql/tests/pretty.rs @@ -342,6 +342,23 @@ mod graph { parse!(r#"SELECT a,b FROM (g MATCH (a:A) -[e:E]- (b:B))"#); parse!(r#"SELECT a,b FROM (g MATCH (a:A) - (b:B))"#); } + + #[test] + fn labels() { + macro_rules! parse { + ($q:expr) => {{ + parse_test!("labels", $q) + }}; + } + + parse!(r#"SELECT a,b FROM (g MATCH (a:%) -[e:%]-> (b:%))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:!Z) -[e:!D]-> (b:!Y))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A|Z) -[e:E|D]-> (b:B|Y))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A&Z) -[e:E&D]-> (b:B&Y))"#); + + parse!(r#"SELECT a,b FROM (g MATCH (a:(A|Z)&!Y) -[e:E&!D]-> (b:%&!(!B)))"#); + } + #[test] fn quantifiers() { macro_rules! parse { @@ -357,6 +374,7 @@ mod graph { parse!(r#"SELECT a,b FROM (g MATCH (a:A)<-+(b:B))"#); parse!(r#"SELECT a,b FROM (g MATCH (a:A)~{5,}(b:B))"#); parse!(r#"SELECT a,b FROM (g MATCH (a:A)-{2,6}(b:B))"#); + parse!(r#"SELECT a,b FROM (g MATCH (a:A) -? (b:B))"#); } #[test] fn patterns() { @@ -423,6 +441,14 @@ mod graph { ) WHERE p.title LIKE '%considered harmful%'"# ); + parse!( + r#"SELECT u as banCandidate + FROM (g MATCH + (p:Post Where p.isFlagged = true) + <-[e:createdPost where e.isMobile = true]- + (u) + )"# + ); } #[test] fn path_mode() { @@ -440,6 +466,9 @@ mod graph { parse!( r#"SELECT p FROM (g MATCH p = ACYCLIC (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"# ); + parse!( + r#"SELECT p FROM (g MATCH p = WALK (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"# + ); } #[test] fn search_prefix() { @@ -466,6 +495,9 @@ mod graph { parse!( r#"SELECT p FROM (g MATCH p = SHORTEST 5 GROUP (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"# ); + parse!( + r#"SELECT p FROM (g MATCH p = ALL (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha'))"# + ); } #[test] fn match_and_join() { @@ -516,6 +548,8 @@ mod graph { WHERE x.foo = 'bar' )" ); + parse!("SELECT * FROM (g MATCH ( (x)-[e]->*(y) ) | (z) ~ (q) )"); + parse!("SELECT * FROM (g MATCH ( (x)-[e]->*(y) ) |+| (z) ~ (q) )"); } #[test] fn shapes() { @@ -609,6 +643,23 @@ mod graph { )" ); } + #[test] + fn simplified() { + macro_rules! parse { + ($q:expr) => {{ + parse_test!("simplified", $q) + }}; + } + parse!("SELECT * FROM (g MATCH <-/ start&begin fin> /- )"); + parse!("SELECT * FROM (g MATCH ~/ !begin -fin /~ )"); + parse!("SELECT * FROM (g MATCH -/ start )"); + parse!("SELECT * FROM (g MATCH <~/ start <~fin |+| begin ~fin> /~ )"); + parse!("SELECT * FROM (g MATCH ~/ start | begin -fin /~> )"); + parse!("SELECT * FROM (g MATCH <-/ start? intermediate{1,} fin /-> )"); + parse!("SELECT * FROM (g MATCH <-/ start? intermediate* fin /-> )"); + parse!("SELECT * FROM (g MATCH <-/ edge+ fin /-> )"); + parse!("SELECT * FROM (g MATCH -/ start !(intermediate other){2,3} fin /- )"); + } } #[test] diff --git a/partiql/tests/snapshots/pretty__graph_etc_5.snap b/partiql/tests/snapshots/pretty__graph_etc_5.snap new file mode 100644 index 00000000..661366e7 --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_etc_5.snap @@ -0,0 +1,32 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT * FROM (g MATCH ( (x)-[e]->*(y) ) | (z) ~ (q) ) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH ((x) -[e]->* (y)) | (z) ~ (q)) + +------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH ((x) -[e]->* (y)) | (z) ~ (q)) + +-------------------------------------------------------------------------------- +SELECT * FROM (g MATCH ((x) -[e]->* (y)) | (z) ~ (q)) + +---------------------------------------- +SELECT * +FROM (g MATCH ((x) -[e]->* (y)) | (z) ~ (q)) + +------------------------------ +SELECT * +FROM (g MATCH ((x) -[e]->* (y)) | (z) ~ (q)) + +-------------------- +SELECT * +FROM (g MATCH ((x) -[e]->* (y)) | (z) ~ (q)) + +---------- +SELECT * +FROM (g MATCH ((x) -[e]->* (y)) | (z) ~ (q)) diff --git a/partiql/tests/snapshots/pretty__graph_etc_6.snap b/partiql/tests/snapshots/pretty__graph_etc_6.snap new file mode 100644 index 00000000..507b0078 --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_etc_6.snap @@ -0,0 +1,32 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT * FROM (g MATCH ( (x)-[e]->*(y) ) |+| (z) ~ (q) ) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH ((x) -[e]->* (y)) |+| (z) ~ (q)) + +------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH ((x) -[e]->* (y)) |+| (z) ~ (q)) + +-------------------------------------------------------------------------------- +SELECT * FROM (g MATCH ((x) -[e]->* (y)) |+| (z) ~ (q)) + +---------------------------------------- +SELECT * +FROM (g MATCH ((x) -[e]->* (y)) |+| (z) ~ (q)) + +------------------------------ +SELECT * +FROM (g MATCH ((x) -[e]->* (y)) |+| (z) ~ (q)) + +-------------------- +SELECT * +FROM (g MATCH ((x) -[e]->* (y)) |+| (z) ~ (q)) + +---------- +SELECT * +FROM (g MATCH ((x) -[e]->* (y)) |+| (z) ~ (q)) diff --git a/partiql/tests/snapshots/pretty__graph_filters_3.snap b/partiql/tests/snapshots/pretty__graph_filters_3.snap new file mode 100644 index 00000000..2a715a88 --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_filters_3.snap @@ -0,0 +1,39 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT u as banCandidate + FROM (g MATCH + (p:Post Where p.isFlagged = true) + <-[e:createdPost where e.isMobile = true]- + (u) + ) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT u AS banCandidate FROM (g MATCH (p:Post WHERE (p.isFlagged = true)) <-[e:createdPost WHERE (e.isMobile = true)]- (u)) + +------------------------------------------------------------------------------------------------------------------------ +SELECT u AS banCandidate +FROM (g MATCH (p:Post WHERE (p.isFlagged = true)) <-[e:createdPost WHERE (e.isMobile = true)]- (u)) + +-------------------------------------------------------------------------------- +SELECT u AS banCandidate +FROM (g MATCH (p:Post WHERE (p.isFlagged = true)) <-[e:createdPost WHERE (e.isMobile = true)]- (u)) + +---------------------------------------- +SELECT u AS banCandidate +FROM (g MATCH (p:Post WHERE (p.isFlagged = true)) <-[e:createdPost WHERE (e.isMobile = true)]- (u)) + +------------------------------ +SELECT u AS banCandidate +FROM (g MATCH (p:Post WHERE (p.isFlagged = true)) <-[e:createdPost WHERE (e.isMobile = true)]- (u)) + +-------------------- +SELECT u AS banCandidate +FROM (g MATCH (p:Post WHERE (p.isFlagged = true)) <-[e:createdPost WHERE (e.isMobile = true)]- (u)) + +---------- +SELECT u AS banCandidate +FROM (g MATCH (p:Post WHERE (p.isFlagged = true)) <-[e:createdPost WHERE (e.isMobile = true)]- (u)) diff --git a/partiql/tests/snapshots/pretty__graph_labels_1.snap b/partiql/tests/snapshots/pretty__graph_labels_1.snap new file mode 100644 index 00000000..d5c6a5e6 --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_labels_1.snap @@ -0,0 +1,33 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT a,b FROM (g MATCH (a:%) -[e:%]-> (b:%)) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT a, b FROM (g MATCH (a:%) -[e:%]-> (b:%)) + +------------------------------------------------------------------------------------------------------------------------ +SELECT a, b FROM (g MATCH (a:%) -[e:%]-> (b:%)) + +-------------------------------------------------------------------------------- +SELECT a, b FROM (g MATCH (a:%) -[e:%]-> (b:%)) + +---------------------------------------- +SELECT a, b +FROM (g MATCH (a:%) -[e:%]-> (b:%)) + +------------------------------ +SELECT a, b +FROM (g MATCH (a:%) -[e:%]-> (b:%)) + +-------------------- +SELECT a, b +FROM (g MATCH (a:%) -[e:%]-> (b:%)) + +---------- +SELECT a, + b +FROM (g MATCH (a:%) -[e:%]-> (b:%)) diff --git a/partiql/tests/snapshots/pretty__graph_labels_2.snap b/partiql/tests/snapshots/pretty__graph_labels_2.snap new file mode 100644 index 00000000..eb869f6f --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_labels_2.snap @@ -0,0 +1,33 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT a,b FROM (g MATCH (a:!Z) -[e:!D]-> (b:!Y)) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT a, b FROM (g MATCH (a:!(Z)) -[e:!(D)]-> (b:!(Y))) + +------------------------------------------------------------------------------------------------------------------------ +SELECT a, b FROM (g MATCH (a:!(Z)) -[e:!(D)]-> (b:!(Y))) + +-------------------------------------------------------------------------------- +SELECT a, b FROM (g MATCH (a:!(Z)) -[e:!(D)]-> (b:!(Y))) + +---------------------------------------- +SELECT a, b +FROM (g MATCH (a:!(Z)) -[e:!(D)]-> (b:!(Y))) + +------------------------------ +SELECT a, b +FROM (g MATCH (a:!(Z)) -[e:!(D)]-> (b:!(Y))) + +-------------------- +SELECT a, b +FROM (g MATCH (a:!(Z)) -[e:!(D)]-> (b:!(Y))) + +---------- +SELECT a, + b +FROM (g MATCH (a:!(Z)) -[e:!(D)]-> (b:!(Y))) diff --git a/partiql/tests/snapshots/pretty__graph_labels_3.snap b/partiql/tests/snapshots/pretty__graph_labels_3.snap new file mode 100644 index 00000000..3fff73b1 --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_labels_3.snap @@ -0,0 +1,33 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT a,b FROM (g MATCH (a:A|Z) -[e:E|D]-> (b:B|Y)) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT a, b FROM (g MATCH (a:(A|Z)) -[e:(E|D)]-> (b:(B|Y))) + +------------------------------------------------------------------------------------------------------------------------ +SELECT a, b FROM (g MATCH (a:(A|Z)) -[e:(E|D)]-> (b:(B|Y))) + +-------------------------------------------------------------------------------- +SELECT a, b FROM (g MATCH (a:(A|Z)) -[e:(E|D)]-> (b:(B|Y))) + +---------------------------------------- +SELECT a, b +FROM (g MATCH (a:(A|Z)) -[e:(E|D)]-> (b:(B|Y))) + +------------------------------ +SELECT a, b +FROM (g MATCH (a:(A|Z)) -[e:(E|D)]-> (b:(B|Y))) + +-------------------- +SELECT a, b +FROM (g MATCH (a:(A|Z)) -[e:(E|D)]-> (b:(B|Y))) + +---------- +SELECT a, + b +FROM (g MATCH (a:(A|Z)) -[e:(E|D)]-> (b:(B|Y))) diff --git a/partiql/tests/snapshots/pretty__graph_labels_4.snap b/partiql/tests/snapshots/pretty__graph_labels_4.snap new file mode 100644 index 00000000..dcd949b2 --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_labels_4.snap @@ -0,0 +1,33 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT a,b FROM (g MATCH (a:A&Z) -[e:E&D]-> (b:B&Y)) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT a, b FROM (g MATCH (a:(A&Z)) -[e:(E&D)]-> (b:(B&Y))) + +------------------------------------------------------------------------------------------------------------------------ +SELECT a, b FROM (g MATCH (a:(A&Z)) -[e:(E&D)]-> (b:(B&Y))) + +-------------------------------------------------------------------------------- +SELECT a, b FROM (g MATCH (a:(A&Z)) -[e:(E&D)]-> (b:(B&Y))) + +---------------------------------------- +SELECT a, b +FROM (g MATCH (a:(A&Z)) -[e:(E&D)]-> (b:(B&Y))) + +------------------------------ +SELECT a, b +FROM (g MATCH (a:(A&Z)) -[e:(E&D)]-> (b:(B&Y))) + +-------------------- +SELECT a, b +FROM (g MATCH (a:(A&Z)) -[e:(E&D)]-> (b:(B&Y))) + +---------- +SELECT a, + b +FROM (g MATCH (a:(A&Z)) -[e:(E&D)]-> (b:(B&Y))) diff --git a/partiql/tests/snapshots/pretty__graph_labels_5.snap b/partiql/tests/snapshots/pretty__graph_labels_5.snap new file mode 100644 index 00000000..db5832ed --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_labels_5.snap @@ -0,0 +1,33 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT a,b FROM (g MATCH (a:(A|Z)&!Y) -[e:E&!D]-> (b:%&!(!B))) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT a, b FROM (g MATCH (a:((A|Z)&!(Y))) -[e:(E&!(D))]-> (b:(%&!(!(B))))) + +------------------------------------------------------------------------------------------------------------------------ +SELECT a, b FROM (g MATCH (a:((A|Z)&!(Y))) -[e:(E&!(D))]-> (b:(%&!(!(B))))) + +-------------------------------------------------------------------------------- +SELECT a, b FROM (g MATCH (a:((A|Z)&!(Y))) -[e:(E&!(D))]-> (b:(%&!(!(B))))) + +---------------------------------------- +SELECT a, b +FROM (g MATCH (a:((A|Z)&!(Y))) -[e:(E&!(D))]-> (b:(%&!(!(B))))) + +------------------------------ +SELECT a, b +FROM (g MATCH (a:((A|Z)&!(Y))) -[e:(E&!(D))]-> (b:(%&!(!(B))))) + +-------------------- +SELECT a, b +FROM (g MATCH (a:((A|Z)&!(Y))) -[e:(E&!(D))]-> (b:(%&!(!(B))))) + +---------- +SELECT a, + b +FROM (g MATCH (a:((A|Z)&!(Y))) -[e:(E&!(D))]-> (b:(%&!(!(B))))) diff --git a/partiql/tests/snapshots/pretty__graph_path_mode_4.snap b/partiql/tests/snapshots/pretty__graph_path_mode_4.snap new file mode 100644 index 00000000..14532383 --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_path_mode_4.snap @@ -0,0 +1,33 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT p FROM (g MATCH p = WALK (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT p FROM (g MATCH p = WALK (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +------------------------------------------------------------------------------------------------------------------------ +SELECT p FROM (g MATCH p = WALK (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +-------------------------------------------------------------------------------- +SELECT p +FROM (g MATCH p = WALK (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +---------------------------------------- +SELECT p +FROM (g MATCH p = WALK (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +------------------------------ +SELECT p +FROM (g MATCH p = WALK (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +-------------------- +SELECT p +FROM (g MATCH p = WALK (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +---------- +SELECT p +FROM (g MATCH p = WALK (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) diff --git a/partiql/tests/snapshots/pretty__graph_quantifiers_9.snap b/partiql/tests/snapshots/pretty__graph_quantifiers_9.snap new file mode 100644 index 00000000..f530e89a --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_quantifiers_9.snap @@ -0,0 +1,33 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT a,b FROM (g MATCH (a:A) -? (b:B)) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT a, b FROM (g MATCH (a:A) -? (b:B)) + +------------------------------------------------------------------------------------------------------------------------ +SELECT a, b FROM (g MATCH (a:A) -? (b:B)) + +-------------------------------------------------------------------------------- +SELECT a, b FROM (g MATCH (a:A) -? (b:B)) + +---------------------------------------- +SELECT a, b +FROM (g MATCH (a:A) -? (b:B)) + +------------------------------ +SELECT a, b +FROM (g MATCH (a:A) -? (b:B)) + +-------------------- +SELECT a, b +FROM (g MATCH (a:A) -? (b:B)) + +---------- +SELECT a, + b +FROM (g MATCH (a:A) -? (b:B)) diff --git a/partiql/tests/snapshots/pretty__graph_search_prefix_7.snap b/partiql/tests/snapshots/pretty__graph_search_prefix_7.snap new file mode 100644 index 00000000..d1824bae --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_search_prefix_7.snap @@ -0,0 +1,33 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT p FROM (g MATCH p = ALL (a WHERE a.owner='Dave') -[t:Transfer]-> * (b WHERE b.owner='Aretha')) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT p FROM (g MATCH p = ALL (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +------------------------------------------------------------------------------------------------------------------------ +SELECT p FROM (g MATCH p = ALL (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +-------------------------------------------------------------------------------- +SELECT p +FROM (g MATCH p = ALL (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +---------------------------------------- +SELECT p +FROM (g MATCH p = ALL (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +------------------------------ +SELECT p +FROM (g MATCH p = ALL (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +-------------------- +SELECT p +FROM (g MATCH p = ALL (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) + +---------- +SELECT p +FROM (g MATCH p = ALL (a WHERE (a.owner = 'Dave')) -[t:Transfer]->* (b WHERE (b.owner = 'Aretha'))) diff --git a/partiql/tests/snapshots/pretty__graph_simplified_1.snap b/partiql/tests/snapshots/pretty__graph_simplified_1.snap new file mode 100644 index 00000000..e3300349 --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_simplified_1.snap @@ -0,0 +1,32 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT * FROM (g MATCH <-/ start&begin fin> /- ) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH <-/ start&begin fin> /-) + +------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH <-/ start&begin fin> /-) + +-------------------------------------------------------------------------------- +SELECT * FROM (g MATCH <-/ start&begin fin> /-) + +---------------------------------------- +SELECT * +FROM (g MATCH <-/ start&begin fin> /-) + +------------------------------ +SELECT * +FROM (g MATCH <-/ start&begin fin> /-) + +-------------------- +SELECT * +FROM (g MATCH <-/ start&begin fin> /-) + +---------- +SELECT * +FROM (g MATCH <-/ start&begin fin> /-) diff --git a/partiql/tests/snapshots/pretty__graph_simplified_2.snap b/partiql/tests/snapshots/pretty__graph_simplified_2.snap new file mode 100644 index 00000000..e388dc71 --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_simplified_2.snap @@ -0,0 +1,32 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT * FROM (g MATCH ~/ !begin -fin /~ ) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH ~/ !begin -fin /~) + +------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH ~/ !begin -fin /~) + +-------------------------------------------------------------------------------- +SELECT * FROM (g MATCH ~/ !begin -fin /~) + +---------------------------------------- +SELECT * +FROM (g MATCH ~/ !begin -fin /~) + +------------------------------ +SELECT * +FROM (g MATCH ~/ !begin -fin /~) + +-------------------- +SELECT * +FROM (g MATCH ~/ !begin -fin /~) + +---------- +SELECT * +FROM (g MATCH ~/ !begin -fin /~) diff --git a/partiql/tests/snapshots/pretty__graph_simplified_3.snap b/partiql/tests/snapshots/pretty__graph_simplified_3.snap new file mode 100644 index 00000000..11770932 --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_simplified_3.snap @@ -0,0 +1,32 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT * FROM (g MATCH -/ start ) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH -/ start ) + +------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH -/ start ) + +-------------------------------------------------------------------------------- +SELECT * FROM (g MATCH -/ start ) + +---------------------------------------- +SELECT * +FROM (g MATCH -/ start ) + +------------------------------ +SELECT * +FROM (g MATCH -/ start ) + +-------------------- +SELECT * +FROM (g MATCH -/ start ) + +---------- +SELECT * +FROM (g MATCH -/ start ) diff --git a/partiql/tests/snapshots/pretty__graph_simplified_4.snap b/partiql/tests/snapshots/pretty__graph_simplified_4.snap new file mode 100644 index 00000000..b668a73f --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_simplified_4.snap @@ -0,0 +1,32 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT * FROM (g MATCH <~/ start <~fin |+| begin ~fin> /~ ) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH <~/ start <~fin |+| begin ~fin> /~) + +------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH <~/ start <~fin |+| begin ~fin> /~) + +-------------------------------------------------------------------------------- +SELECT * FROM (g MATCH <~/ start <~fin |+| begin ~fin> /~) + +---------------------------------------- +SELECT * +FROM (g MATCH <~/ start <~fin |+| begin ~fin> /~) + +------------------------------ +SELECT * +FROM (g MATCH <~/ start <~fin |+| begin ~fin> /~) + +-------------------- +SELECT * +FROM (g MATCH <~/ start <~fin |+| begin ~fin> /~) + +---------- +SELECT * +FROM (g MATCH <~/ start <~fin |+| begin ~fin> /~) diff --git a/partiql/tests/snapshots/pretty__graph_simplified_5.snap b/partiql/tests/snapshots/pretty__graph_simplified_5.snap new file mode 100644 index 00000000..9db24209 --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_simplified_5.snap @@ -0,0 +1,32 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT * FROM (g MATCH ~/ start | begin -fin /~> ) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH ~/ start | begin -fin /~>) + +------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH ~/ start | begin -fin /~>) + +-------------------------------------------------------------------------------- +SELECT * FROM (g MATCH ~/ start | begin -fin /~>) + +---------------------------------------- +SELECT * +FROM (g MATCH ~/ start | begin -fin /~>) + +------------------------------ +SELECT * +FROM (g MATCH ~/ start | begin -fin /~>) + +-------------------- +SELECT * +FROM (g MATCH ~/ start | begin -fin /~>) + +---------- +SELECT * +FROM (g MATCH ~/ start | begin -fin /~>) diff --git a/partiql/tests/snapshots/pretty__graph_simplified_6.snap b/partiql/tests/snapshots/pretty__graph_simplified_6.snap new file mode 100644 index 00000000..07fcc2b8 --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_simplified_6.snap @@ -0,0 +1,32 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT * FROM (g MATCH <-/ start? intermediate{1,} fin /-> ) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH <-/ start? intermediate+ fin /->) + +------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH <-/ start? intermediate+ fin /->) + +-------------------------------------------------------------------------------- +SELECT * FROM (g MATCH <-/ start? intermediate+ fin /->) + +---------------------------------------- +SELECT * +FROM (g MATCH <-/ start? intermediate+ fin /->) + +------------------------------ +SELECT * +FROM (g MATCH <-/ start? intermediate+ fin /->) + +-------------------- +SELECT * +FROM (g MATCH <-/ start? intermediate+ fin /->) + +---------- +SELECT * +FROM (g MATCH <-/ start? intermediate+ fin /->) diff --git a/partiql/tests/snapshots/pretty__graph_simplified_7.snap b/partiql/tests/snapshots/pretty__graph_simplified_7.snap new file mode 100644 index 00000000..285520e2 --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_simplified_7.snap @@ -0,0 +1,32 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT * FROM (g MATCH <-/ start? intermediate* fin /-> ) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH <-/ start? intermediate* fin /->) + +------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH <-/ start? intermediate* fin /->) + +-------------------------------------------------------------------------------- +SELECT * FROM (g MATCH <-/ start? intermediate* fin /->) + +---------------------------------------- +SELECT * +FROM (g MATCH <-/ start? intermediate* fin /->) + +------------------------------ +SELECT * +FROM (g MATCH <-/ start? intermediate* fin /->) + +-------------------- +SELECT * +FROM (g MATCH <-/ start? intermediate* fin /->) + +---------- +SELECT * +FROM (g MATCH <-/ start? intermediate* fin /->) diff --git a/partiql/tests/snapshots/pretty__graph_simplified_8.snap b/partiql/tests/snapshots/pretty__graph_simplified_8.snap new file mode 100644 index 00000000..b15787ec --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_simplified_8.snap @@ -0,0 +1,32 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT * FROM (g MATCH <-/ edge+ fin /-> ) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH <-/ edge+ fin /->) + +------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH <-/ edge+ fin /->) + +-------------------------------------------------------------------------------- +SELECT * FROM (g MATCH <-/ edge+ fin /->) + +---------------------------------------- +SELECT * +FROM (g MATCH <-/ edge+ fin /->) + +------------------------------ +SELECT * +FROM (g MATCH <-/ edge+ fin /->) + +-------------------- +SELECT * +FROM (g MATCH <-/ edge+ fin /->) + +---------- +SELECT * +FROM (g MATCH <-/ edge+ fin /->) diff --git a/partiql/tests/snapshots/pretty__graph_simplified_9.snap b/partiql/tests/snapshots/pretty__graph_simplified_9.snap new file mode 100644 index 00000000..d8dd32ba --- /dev/null +++ b/partiql/tests/snapshots/pretty__graph_simplified_9.snap @@ -0,0 +1,32 @@ +--- +source: partiql/tests/pretty.rs +expression: doc +--- +======================================================================================================================================================================================================== +SELECT * FROM (g MATCH -/ start !(intermediate other){2,3} fin /- ) +======================================================================================================================================================================================================== + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH -/ start !(intermediate other){2,3} fin /-) + +------------------------------------------------------------------------------------------------------------------------ +SELECT * FROM (g MATCH -/ start !(intermediate other){2,3} fin /-) + +-------------------------------------------------------------------------------- +SELECT * FROM (g MATCH -/ start !(intermediate other){2,3} fin /-) + +---------------------------------------- +SELECT * +FROM (g MATCH -/ start !(intermediate other){2,3} fin /-) + +------------------------------ +SELECT * +FROM (g MATCH -/ start !(intermediate other){2,3} fin /-) + +-------------------- +SELECT * +FROM (g MATCH -/ start !(intermediate other){2,3} fin /-) + +---------- +SELECT * +FROM (g MATCH -/ start !(intermediate other){2,3} fin /-)