diff --git a/ast/ast.go b/ast/ast.go index 0d941103..56cad326 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -119,7 +119,9 @@ func (Insert) isStatement() {} func (Delete) isStatement() {} func (Update) isStatement() {} func (Call) isStatement() {} +func (GQLGraphQuery) isStatement() {} +// QueryExpr represents set operator operands. // QueryExpr represents query expression, which can be body of QueryStatement or subqueries. // Select and FromQuery are leaf QueryExpr and others wrap other QueryExpr. type QueryExpr interface { @@ -177,6 +179,7 @@ func (SubQueryTableExpr) isTableExpr() {} func (ParenTableExpr) isTableExpr() {} func (Join) isTableExpr() {} func (TVFCallExpr) isTableExpr() {} +func (GraphTableExpr) isTableExpr() {} // JoinCondition represents condition part of JOIN expression. type JoinCondition interface { @@ -199,6 +202,8 @@ func (UnaryExpr) isExpr() {} func (InExpr) isExpr() {} func (IsNullExpr) isExpr() {} func (IsBoolExpr) isExpr() {} +func (IsSourceExpr) isExpr() {} +func (IsDestinationExpr) isExpr() {} func (BetweenExpr) isExpr() {} func (SelectorExpr) isExpr() {} func (IndexExpr) isExpr() {} @@ -234,6 +239,9 @@ func (JSONLiteral) isExpr() {} func (NewConstructor) isExpr() {} func (BracedNewConstructor) isExpr() {} func (BracedConstructor) isExpr() {} +func (ArrayGQLSubQuery) isExpr() {} +func (ValueGQLSubQuery) isExpr() {} +func (ExistsGQLSubQuery) isExpr() {} // SubscriptSpecifier represents specifier of subscript operators. type SubscriptSpecifier interface { @@ -288,9 +296,10 @@ type InCondition interface { isInCondition() } -func (UnnestInCondition) isInCondition() {} -func (SubQueryInCondition) isInCondition() {} -func (ValuesInCondition) isInCondition() {} +func (UnnestInCondition) isInCondition() {} +func (SubQueryInCondition) isInCondition() {} +func (ValuesInCondition) isInCondition() {} +func (GQLSubQueryInCondition) isInCondition() {} // TypelessStructLiteralArg represents an argument of typeless STRUCT literals. type TypelessStructLiteralArg interface { @@ -1170,6 +1179,22 @@ type ParenTableExpr struct { Sample *TableSample // optional } +// GraphTableExpr is GRAPH_TABLE operator +// +// GRAPH_TABLE({{.PropertyGraphName | sql}} {{.Query | sql}}) {{.As | sqlOpt}} +type GraphTableExpr struct { + // pos = GraphTable + // end = As.end || Rparen + 1 + + GraphTable token.Pos + PropertyGraphName *Ident + + Lparen, Rparen token.Pos // position of "(" and ")" + + Query *GQLMultiLinearQueryStatement + As *AsAlias // optional +} + // Join is JOIN expression. // // {{.Left | sql}} @@ -1323,6 +1348,18 @@ type ValuesInCondition struct { Exprs []Expr // len(Exprs) > 0 } +// GQLSubQueryInCondition is GQL subquery at IN condition. +// +// {{"{"}}{{.Query | sql}}}{{"}"}} +type GQLSubQueryInCondition struct { + // pos = Lbrace + // end = Rbrace + 1 + + Lbrace, Rbrace token.Pos // position of "{" and "}" + + Query *GQLQueryExpr +} + // IsNullExpr is IS NULL expression node. // // {{.Left | sql}} IS {{if .Not}}NOT{{end}} NULL @@ -1350,6 +1387,28 @@ type IsBoolExpr struct { Right bool } +// IsSourceExpr is IS SOURCE expression node. +// +// {{.Node | sql}} IS {{if .Not}}NOT{{end}} SOURCE OF {{.Edge | sql}} +type IsSourceExpr struct { + // pos = Node.pos + // end = Edge.end + + Node, Edge Expr + Not bool +} + +// IsDestinationExpr is IS DESTINATION expression node. +// +// {{.Node | sql}} IS {{if .Not}}NOT{{end}} DESTINATION OF {{.Edge | sql}} +type IsDestinationExpr struct { + // pos = Node.pos + // end = Edge.end + + Node, Edge Expr + Not bool +} + // BetweenExpr is BETWEEN expression node. // // {{.Left | sql}} {{if .Not}}NOT{{end}} BETWEEN {{.RightStart | sql}} AND {{.RightEnd | sql}} @@ -1780,6 +1839,51 @@ type ArraySubQuery struct { Query QueryExpr } +// ArrayGQLSubQuery is GQL subquery as ARRAY. +// +// ARRAY {{"{"}}{{.Query | sql}}{{"}"}} +type ArrayGQLSubQuery struct { + // pos = Array + // end = Rbrace + 1 + + Array token.Pos // position of "ARRAY" keyword + Rbrace token.Pos // position of "}" + Query *GQLQueryExpr +} + +// ValueGQLSubQuery is GQL subquery as VALUE. +// +// VALUE {{"{"}}{{.Query | sql}}{{"}"}} +type ValueGQLSubQuery struct { + // pos = Array + // end = Rbrace + 1 + + Array token.Pos // position of "ARRAY" keyword + Rbrace token.Pos // position of "}" + Query *GQLQueryExpr +} + +// ExistsGQLSubQuery is GQL subquery as EXISTS. +// +// EXISTS{{"{"}}{{.Expr | sql}}{{"}"}} +type ExistsGQLSubQuery struct { + // pos = Exists + // end = Rbrace + 1 + + Exists token.Pos // position of "EXISTS" keyword + Rbrace token.Pos // "}" + Query GQLExistsExpr +} + +type GQLExistsExpr interface { + Node + isGQLExistsExpr() +} + +func (GQLQueryExpr) isGQLExistsExpr() {} +func (GQLMatchStatement) isGQLExistsExpr() {} +func (GQLGraphPattern) isGQLExistsExpr() {} + // ExistsSubQuery is subquery in EXISTS call. // // EXISTS {{.Hint | sqlOpt}} ({{.Query | sql}}) @@ -4073,3 +4177,768 @@ type Call struct { Name *Path Args []TVFArg // len(Args) > 0 } + +// ================================================================================ +// +// GQL +// +// https://cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements +// +// ================================================================================ + +// GQLGraphQuery is toplevel node of GRAPH query. +// +// {{.Hint | sqlOpt}} +// {{.Graph | sql}} +// {{.MultiLinearQueryStatement | sql}} +type GQLGraphQuery struct { + // pos = (Hint ?? GraphClause ?? MultiLinearQueryStatement).pos + // end = MultiLinearQueryStatement.end + + Hint *Hint + GraphClause *GQLGraphClause + MultiLinearQueryStatement *GQLMultiLinearQueryStatement +} + +// GQLQueryExpr is similar to GQLGraphQuery, +// but it is appeared in GQL subqueries and it can optionally have GRAPH clause +// +// {{.Graph | sqlOpt}} +// {{.MultiLinearQueryStatement | sql}} +type GQLQueryExpr struct { + // pos = (GraphClause ?? MultiLinearQueryStatement).pos + // end = MultiLinearQueryStatement.end + + GraphClause *GQLGraphClause // optional + MultiLinearQueryStatement *GQLMultiLinearQueryStatement +} + +// GQLGraphClause represents `GRAPH property_graph_name`. +// +// GRAPH {{.PropertyGraphName | sql}} +type GQLGraphClause struct { + // pos = Graph + // end = PropertyGraphName.end + + Graph token.Pos + PropertyGraphName *Ident +} + +// GQLMultiLinearQueryStatement is the body of a GQLGraphClause and GQLQueryExpr. +// It contains a list of LinearQueryStatementList chained together with the NEXT statement. +// +// {{.LinearQueryStatementList || sqlJoin "\nNEXT\n"}} +type GQLMultiLinearQueryStatement struct { + // pos = LinearQueryStatementList[0].pos + // end = LinearQueryStatementList[$].end + + LinearQueryStatementList []GQLLinearQueryStatement +} + +type GQLLinearQueryStatement interface { + Node + isGQLLinearQueryStatement() +} + +func (GQLSimpleLinearQueryStatement) isGQLLinearQueryStatement() {} +func (GQLCompositeLinearQueryStatement) isGQLLinearQueryStatement() {} + +// GQLSimpleLinearQueryStatement represents a list of primitive_query_statements that ends with a RETURN statement. +// +// {{.PrimitiveQueryStatementList | sqlJoin "\n"}} +type GQLSimpleLinearQueryStatement struct { + // pos = PrimitiveQueryStatementList[0].pos + // end = PrimitiveQueryStatementList[$].end + + // It contains at least one GQL statements, and It ends with a RETURN statement. + PrimitiveQueryStatementList []GQLPrimitiveQueryStatement +} + +// GQLSimpleLinearQueryStatementWithSetOperator represents GQLSimpleLinearQueryStatement composited with the set operators. +// TODO: template +// +// {{string(SetOperator)}} +type GQLSimpleLinearQueryStatementWithSetOperator struct { + // pos = StartPos + // end = Statement.end + + StartPos token.Pos + SetOperator GQLSetOperatorEnum + AllOrDistinct AllOrDistinct + Statement *GQLSimpleLinearQueryStatement +} + +// GQLCompositeLinearQueryStatement represents a list of GQLSimpleLinearQueryStatement composited with the set operators. +// +// {{.HeadSimpleLinearQueryStatement | sql}} +// {{.TailSimpleLinearQueryStatementList | sqlJoin "\n"}} +type GQLCompositeLinearQueryStatement struct { + // pos = HeadSimpleLinearQueryStatement.pos + // end = TailSimpleLinearQueryStatementList[$].end + + HeadSimpleLinearQueryStatement *GQLSimpleLinearQueryStatement + TailSimpleLinearQueryStatementList []*GQLSimpleLinearQueryStatementWithSetOperator +} + +// ================================================================================ +// +// GQL statements +// +// ================================================================================ + +type GQLPrimitiveQueryStatement interface { + Node + isGQLPrimitiveQueryStatement() +} + +func (GQLWithStatement) isGQLPrimitiveQueryStatement() {} +func (GQLOrderByStatement) isGQLPrimitiveQueryStatement() {} +func (GQLOffsetStatement) isGQLPrimitiveQueryStatement() {} +func (GQLLimitStatement) isGQLPrimitiveQueryStatement() {} +func (GQLForStatement) isGQLPrimitiveQueryStatement() {} +func (GQLFilterStatement) isGQLPrimitiveQueryStatement() {} +func (GQLMatchStatement) isGQLPrimitiveQueryStatement() {} +func (GQLLetStatement) isGQLPrimitiveQueryStatement() {} +func (GQLReturnStatement) isGQLPrimitiveQueryStatement() {} + +// GQLMatchStatement represents MATCH statement. +// +// {{if not .Optional.Invalid}}OPTIONAL{{end}} +// MATCH {{.MatchHint | sqlOpt}} {{.PrefixOrMode | sqlOpt}} {{.GraphPattern | sql}} +type GQLMatchStatement struct { + // pos = Optional || Match + // end = GraphPattern.end + + Optional token.Pos // optional + Match token.Pos + + MatchHint *Hint // optional + PrefixOrMode GQLPathSearchPrefixOrPathMode // optional + GraphPattern *GQLGraphPattern +} + +type GQLLimitAndOffsetClause interface { + Node + isGQLLimitAndOffsetClause() +} + +func (GQLLimitClause) isGQLLimitAndOffsetClause() {} +func (GQLOffsetClause) isGQLLimitAndOffsetClause() {} +func (GQLLimitWithOffsetClause) isGQLLimitAndOffsetClause() {} + +// GQLFilterStatement represents `FILTER [WHERE] bool_expression` +// +// FILTER {{if .Where.Invalid | not}}WHERE{{end}} {{.Expr | sql}} +type GQLFilterStatement struct { + // pos = Filter + // end = Expr.end + + Filter token.Pos + Where token.Pos + Expr Expr +} + +// GQLForStatement represents GQL FOR statement. +// +// FOR {{.ElementName | sql}} IN {{.ArrayExpression | sql}} {{.WithOffsetClause | sqlOpt}} +type GQLForStatement struct { + // pos = For + // end = (WithOffsetClause ?? ArrayExpression).end + + For token.Pos + ElementName *Ident + ArrayExpression Expr + WithOffsetClause *GQLWithOffsetClause +} + +// GQLWithOffsetClause represents WITH OFFSET [AS offset_name] in FOR statement. +// +// WITH OFFSET {{.OffsetName | sqlOpt}} +type GQLWithOffsetClause struct { + // pos = With + // end = OffsetName.end || Offset + 6 + + With token.Pos + Offset token.Pos + OffsetName *AsAlias // optional +} + +// GQLLimitClause is wrapper of Limit for GQL +// +// {{.Limit | sql}} +type GQLLimitClause struct { + // pos = Limit.pos + // end = Limit.end + + Limit *Limit +} + +// GQLOffsetClause is wrapper of Offset for GQL +// +// {{.Offset | sql}} +type GQLOffsetClause struct { + // pos = Offset.pos + // end = Offset.end + + Offset *Offset +} + +// GQLLimitWithOffsetClause is wrapper of Limit and Offset +// +// {{.Offset | sql}} {{.Offset | sql}} +type GQLLimitWithOffsetClause struct { + // pos = Offset.pos + // end = Limit.end + + Offset *Offset + Limit *Limit +} + +// GQLLimitStatement represents LIMIT statement +// +// LIMIT {{.Count | sql}} +type GQLLimitStatement struct { + // pos = Limit + // end = Count.end + + Limit token.Pos + Count IntValue +} + +// GQLOffsetStatement represents OFFSET statement. +// It also represents SKIP statement as the synonym. +// +// {{if IsSkip}}SKIP{{else}}OFFSET{{end}} {{.Count | sql}} +type GQLOffsetStatement struct { + // pos = Offset + // end = Count.end + + Offset token.Pos + IsSkip bool + Count IntValue +} + +// GQLOrderByStatement represents ORDER BY statement. +// +// ORDER BY {{.OrderBySpecificationList | sqlJoin ", "}} +type GQLOrderByStatement struct { + // pos = Order + // end = OrderBySpecificationList[$].end + + Order token.Pos + OrderBySpecificationList []*GQLOrderBySpecification +} + +// GQLOrderBySpecification represents a single sort criterion for an expression in ORDER BY. +// +// {{.Expr | sql}} {{.CollationSpecification | sqlOpt}} {{if DirectionPos.Invalid | not}}{{string(Direction)}}{{end}} +type GQLOrderBySpecification struct { + // pos = Expr.pos + // end = DirectionPos || CollationSpecification.end + + Expr Expr + + CollationSpecification *GQLCollationSpecification // optional + DirectionPos token.Pos // optional + Direction GQLDirectionEnum +} + +// GQLCollationSpecification represents `COLLATE collation_specification` +// +// COLLATE {{.Specification | sql}} +type GQLCollationSpecification struct { + // pos = Collate + // end = Specification.end + + Collate token.Pos + Specification StringValue +} + +// GQLWithStatement represents WITH statement. +// +// WITH {{string(.GQLAllOrDistinctEnum)}} {{.ReturnItemList | sqlJoin}} {{.GroupBy | sqlOpt}} +type GQLWithStatement struct { + // pos = With + // end = (GroupByClause ?? ReturnItemList[$]).end + + With token.Pos + AllOrDistinct AllOrDistinct + ReturnItemList []*GQLReturnItem + GroupByClause *GroupBy // optional +} + +// GQLReturnItem is similar to SelectItem, +// but it don't permit DotStar and AsAlias without AS. +// +// {{.Item | sql}} +type GQLReturnItem struct { + // pos = Item.pos + // end = Item.end + + Item SelectItem +} + +// GQLReturnStatement represents RETURN statement. +// +// RETURN {{.AllOrDistinct | sql}} {{.ReturnItemList | sqlJoin}} +// {{.GroupByClause | sqlOpt}} +// {{.OrderByClause | sqlOpt}} +// {{.LimitAntOffsetClause | sqlOpt}} +type GQLReturnStatement struct { + // pos = Return + // end = (LimitAndOffsetClause ?? OrderByClause ?? GroupByClause ?? ReturnItemList[$]).end + + Return token.Pos // position of "RETURN" keyword + AllOrDistinct AllOrDistinct + ReturnItemList []*GQLReturnItem + + // Use GoogleSQL GroupBy because it is referenced in docs + GroupByClause *GroupBy //optional + + // Use GoogleSQL OrderBy because it is referenced in docs + OrderByClause *OrderBy //optional + + LimitAndOffsetClause GQLLimitAndOffsetClause // optional +} + +// GQLLinearGraphVariable represents a single `variable_name = value` entry in LET statement. +// +// {{.VariableName | sql}} = {{.Value | sql}} +type GQLLinearGraphVariable struct { + // pos = VariableName.pos + // end = Value.end + + VariableName *Ident + Value Expr +} + +// GQLLetStatement represents LET statement. +// +// LET {{.LinearGraphVariableList | sqlJoin ", "}} +type GQLLetStatement struct { + // pos = Let + // end = LinearGraphVariableList[$].end + + Let token.Pos + LinearGraphVariableList []*GQLLinearGraphVariable // len(LinearGraphVariableList) > 0 +} + +// ================================================================================ +// +// GQL graph patterns +// +// ================================================================================ + +// GQLGraphPattern represents is the toplevel node of GQL graph patterns. +// +// {{.PathPatternList | sqlJoin}} {{.WhereClause | sqlOpt}} +type GQLGraphPattern struct { + // pos = PathPatternList[0].pos + // end = (WhereClause ?? PathPatternList[$]).end + + PathPatternList []*GQLTopLevelPathPattern + WhereClause *Where // optional +} + +// GQLTopLevelPathPattern is a PathPattern optionally prefixed by PathSearchPrefixOrPathMode. +// +// {{if .Var}}{{.Var}} = {{end}}{{.PathSearchPrefixOrPathMode | sqlOpt}} {{.PathPattern | sql}} +type GQLTopLevelPathPattern struct { + // pos = (PathSearchPrefixOrPathMode ?? PathPattern).pos + // end = PathPattern.end + + Var *Ident + PathSearchPrefixOrPathMode GQLPathSearchPrefixOrPathMode // optional + PathPattern *GQLPathPattern +} + +// GQLPathSearchPrefixOrPathMode represents `{ path_search_prefix | path_mode }` +type GQLPathSearchPrefixOrPathMode interface { + Node + isGQLPathSearchPrefixOrPathMode() +} + +func (GQLPathMode) isGQLPathSearchPrefixOrPathMode() {} +func (GQLPathSearchPrefix) isGQLPathSearchPrefixOrPathMode() {} + +// GQLEdgePattern represents edge pattern nodes. +type GQLEdgePattern interface { + GQLElementPattern + isGQLEdgePattern() +} + +func (GQLAbbreviatedEdgeLeft) isGQLEdgePattern() {} +func (GQLAbbreviatedEdgeAny) isGQLEdgePattern() {} +func (GQLFullEdgeRight) isGQLEdgePattern() {} +func (GQLFullEdgeLeft) isGQLEdgePattern() {} +func (GQLFullEdgeAny) isGQLEdgePattern() {} +func (GQLAbbreviatedEdgeRight) isGQLEdgePattern() {} + +// GQLFullEdgeAny is node representing`-[pattern_filler]-` . +// +// -[{{.PatternFiller | sqlOpt}}]- +type GQLFullEdgeAny struct { + // pos = FirstHyphen + // end = LastHyphen + 1 + + FirstHyphen, LastHyphen token.Pos // position of "-" + Lbrack, Rbrack token.Pos // position of "[" and "]" + + PatternFiller *GQLPatternFiller // optional +} + +// GQLFullEdgeLeft represents `<-[pattern_filler]-` +// +// <-[{{.PatternFiller | sqlOpt}}]- +type GQLFullEdgeLeft struct { + // pos = Lt + // end = Hyphen + 1 + + Lt token.Pos // position of "<" + Lbrack, Rbrack token.Pos // position of "[" and "]" + Hyphen token.Pos // position of the last "-" + + PatternFiller *GQLPatternFiller // optional +} + +// GQLFullEdgeRight represents “-[pattern_filler]-> +// +// -[{{.PatternFiller | sqlOpt}}]-> +type GQLFullEdgeRight struct { + // pos = Hyphen + // end = Arrow + 2 + + Hyphen token.Pos // position of the first "-" + Lbrack, Rbrack token.Pos // position of "[" and "]" + Arrow token.Pos // position of "->" + + PatternFiller *GQLPatternFiller // optional +} + +// GQLAbbreviatedEdgeAny represents `-`. +// +// - +type GQLAbbreviatedEdgeAny struct { + // pos = Hyphen + // end = Hyphen +1 + + Hyphen token.Pos // position of "-" +} + +// GQLAbbreviatedEdgeLeft represents `<-`. +// +// <- +type GQLAbbreviatedEdgeLeft struct { + // pos = Lt + // end = Hyphen + 1 + + Lt token.Pos // position of "<" + Hyphen token.Pos // position of "-" +} + +// GQLAbbreviatedEdgeRight represents `->`. +// +// -> +type GQLAbbreviatedEdgeRight struct { + // pos = Hyphen + // end = Arrow + 2 + + Hyphen token.Pos // position of "-" + Arrow token.Pos // position of "->" +} + +// GQLQuantifiablePathTerm represents GQLPathTerm with optional Hint and optional GQLQuantifier.. +// NOTE: This node is not documented in spec, but inferred by [quantified_path_primary] and [graph traversal hints]. +// +// [graph traversal hints]: https://cloud.google.com/spanner/docs/reference/standard-sql/graph-query-statements#graph_hints +// [quantified_path_primary] https://cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#quantified_paths +// +// {{.Hint | sqlOpt}}{{.PathTerm | sql}}{{.Quantifier | sqlOpt}} +type GQLQuantifiablePathTerm struct { + // pos = (Hint ?? PathTerm).pos + // end = (Quantifier ?? PathTerm).end + + Hint *Hint // optional + PathTerm GQLPathTerm + Quantifier GQLQuantifier // optional +} + +// GQLPathPattern represents a path pattern that matches paths in a property graph. +// +// {{.PathTermList | sqlJoin ""}} +type GQLPathPattern struct { + // pos = PathTermList[0].pos + // end = PathTermList[$].end + + PathTermList []*GQLQuantifiablePathTerm +} + +// GQLPathTerm represents `{ element_pattern | subpath_pattern }` +type GQLPathTerm interface { + Node + isGQLPathTerm() +} + +func (GQLSubpathPattern) isGQLPathTerm() {} +func (GQLNodePattern) isGQLPathTerm() {} +func (GQLAbbreviatedEdgeRight) isGQLPathTerm() {} +func (GQLAbbreviatedEdgeLeft) isGQLPathTerm() {} +func (GQLAbbreviatedEdgeAny) isGQLPathTerm() {} +func (GQLFullEdgeRight) isGQLPathTerm() {} +func (GQLFullEdgeLeft) isGQLPathTerm() {} +func (GQLFullEdgeAny) isGQLPathTerm() {} + +// GQLWhereClause represents `WHERE bool_expression` clause. +// +// WHERE {{.BoolExpression | sql}} +type GQLWhereClause struct { + // pos = Where + // end = BoolExpression.end + + Where token.Pos + BoolExpression Expr +} + +// GQLElementPattern represents a node pattern or an edge pattern. +type GQLElementPattern interface { + Node + GQLPathTerm + isGQLElementPattern() +} + +func (GQLFullEdgeAny) isGQLElementPattern() {} +func (GQLFullEdgeLeft) isGQLElementPattern() {} +func (GQLFullEdgeRight) isGQLElementPattern() {} +func (GQLAbbreviatedEdgeAny) isGQLElementPattern() {} +func (GQLAbbreviatedEdgeLeft) isGQLElementPattern() {} +func (GQLAbbreviatedEdgeRight) isGQLElementPattern() {} + +// GQLPathMode represents to include or exclude paths that have repeating edges based on the specified mode. +// +// {{.ModeToken | sql}} {{.PathOrPathsToken | sqlOpt}} +type GQLPathMode struct { + // pos = ModeToken.pos + // end = (PathOrPathsToken ?? ModeToken).end + + Mode GQLPathModeEnum + ModeToken *Ident + PathOrPathsToken *Ident // optional +} + +// GQLQuantifier represents `{ fixed_quantifier | bounded_quantifier }`. +type GQLQuantifier interface { + Node + isGQLQuantifier() +} + +func (GQLFixedQuantifier) isGQLQuantifier() {} +func (GQLBoundedQuantifier) isGQLQuantifier() {} + +// GQLFixedQuantifier represents the exact number of times the path pattern portion must repeat. +// +// {{"{"}}{{.Bound | sql}}{{"}"}} +type GQLFixedQuantifier struct { + // pos = Lbrace + // end = Rbrace + 1 + + Lbrace, Rbrace token.Pos + Bound IntValue +} + +// GQLBoundedQuantifier represents the minimum and maximum number of times the path pattern portion can repeat. +// +// {{"{"}}{{.LowerBound | sqlOpt}}, {{.UpperBound | sql}}{{"}"}} +type GQLBoundedQuantifier struct { + // pos = Lbrace + // end = Rbrace + 1 + + Lbrace, Rbrace token.Pos + LowerBound IntValue // optional + UpperBound IntValue +} + +// GQLSubpathPattern represents a path pattern enclosed in parentheses. +// +// ({{.PathMode | sqlOpt}} {{.PathPattern | sql}} {{.WhereClause | sqlOpt}}) +type GQLSubpathPattern struct { + // pos = Lparen + // end = Rparen + 1 + + Lparen, Rparen token.Pos // position of "(" and ")" + PathMode *GQLPathMode // optional + PathPattern *GQLPathPattern + WhereClause *Where // optional +} + +// GQLNodePattern represents a pattern to match nodes in a property graph. +// +// ({{.PatternFiller | sql}}) +type GQLNodePattern struct { + // pos = Lparen + // end = Rparen + 1 + + Lparen, Rparen token.Pos + PatternFiller *GQLPatternFiller +} + +// GQLPatternFiller represents specifications on the node or edge pattern that you want to match. +// Note: All fields are optional, but all non-nil pattern filler must have at least one non-nil field. +// +// {{.Hint | sqlOpt}} +// {{.GraphPatternVariable | sqlOpt}} +// {{.IsLabelCondition | sqlOpt}} +// {{.Filter | sqlOpt}} +type GQLPatternFiller struct { + // pos = (Hint ?? GraphPatternVariable ?? IsLabelCondition ?? Filter).pos + // end = (Filter ?? IsLabelCondition ?? GraphPatternVariable ?? Hint).end + + // Hint is graph element hint which is a table hint. + Hint *Hint // optional + GraphPatternVariable *Ident // optional + IsLabelCondition *GQLIsLabelCondition // optional + Filter GQLPatternFillerFilter // optional +} + +// GQLIsLabelCondition represents `{ IS | : } label_expression`. +// It normalizes `IS` to `:`. +// +// : {{.LabelExpression | sql}} +type GQLIsLabelCondition struct { + // pos = IsOrColon + // end = LabelExpression.end + + IsOrColon token.Pos + LabelExpression GQLLabelExpression +} + +// GQLLabelExpression represents the expression for the label. +// It is formed by combining one or more labels with logical operators (AND, OR, NOT) and parentheses for grouping. +// See https://cloud.google.com/spanner/docs/reference/standard-sql/graph-patterns#label_expression_definition. +type GQLLabelExpression interface { + Node + isGQLLabelExpression() +} + +// Note: Spanner Graph documentation don't say about paren expression, but there is. +func (GQLLabelParenExpression) isGQLLabelExpression() {} +func (GQLLabelOrExpression) isGQLLabelExpression() {} +func (GQLLabelAndExpression) isGQLLabelExpression() {} +func (GQLLabelNotExpression) isGQLLabelExpression() {} +func (GQLWildcardLabel) isGQLLabelExpression() {} +func (GQLElementLabel) isGQLLabelExpression() {} + +// GQLLabelOrExpression represents `label_expression|label_expression`. +// +// {{.Left | sql}}|{{.Right | sql}} +type GQLLabelOrExpression struct { + // pos = Left.pos + // end = Right.end + + Left, Right GQLLabelExpression +} + +// GQLLabelParenExpression represents `(label_expression)`. +// +// ({{.LabelExpr | sql}}) +type GQLLabelParenExpression struct { + // pos = Lparen + // end = Rparen + 1 + + Lparen, Rparen token.Pos + LabelExpr GQLLabelExpression +} + +// GQLLabelAndExpression represents `label_expression&label_expression`. +// +// {{.Left | sql}}&{{.Right | sql}} +type GQLLabelAndExpression struct { + // pos = Left.pos + // end = Right.end + + Left, Right GQLLabelExpression +} + +// GQLLabelNotExpression represents `!label_expression`. +// +// !{{.LabelExpression | sql}} +type GQLLabelNotExpression struct { + // pos = Not + // end = LabelExpression.end + + Not token.Pos // position of "!" + LabelExpression GQLLabelExpression +} + +type GQLLabelPrimary interface { + GQLLabelExpression + isGQLLabelPrimary() +} + +func (GQLElementLabel) isGQLLabelPrimary() {} +func (GQLWildcardLabel) isGQLLabelPrimary() {} + +// GQLWildcardLabel is % node in label expression. +// +// % +type GQLWildcardLabel struct { + // pos = Percent + // end = Percent + 1 + + Percent token.Pos // position of "%" +} + +// GQLElementLabel represents the label to match. +// +// {{.LabelName | sql}} +type GQLElementLabel struct { + // pos = LabelName.pos + // end = LabelName.end + + LabelName *Ident // optional +} + +// GQLPatternFillerFilter represents `{where_clause | property_filters}` in GQLPatternFiller. +type GQLPatternFillerFilter interface { + Node + isGQLPatternFillerFilter() +} + +func (GQLPropertyFilters) isGQLPatternFillerFilter() {} +func (GQLWhereClause) isGQLPatternFillerFilter() {} +func (Where) isGQLPatternFillerFilter() {} + +// GQLPropertyFilters represents `{ element_property[, ...] }` in GQLPatternFiller. +// +// {{"{"}}{{.PropertyFilterElemList | sqlJoin ", "}}{{"}"}} +type GQLPropertyFilters struct { + // pos = Lbrace + // end = Rbrace + 1 + + Lbrace token.Pos // position of "{" + PropertyFilterElemList []*GQLElementProperty + Rbrace token.Pos // position of "}" +} + +// GQLElementProperty represents an element of GQLPropertyFilters. +// +// {{.ElementPropertyName | sql}}: {{.ElementPropertyValue | sql}} +type GQLElementProperty struct { + // pos = ElementPropertyName.pos + // end = ElementPropertyValue.end + + ElementPropertyName *Ident + ElementPropertyValue Expr +} + +// GQLPathSearchPrefix represents `{"ALL" | "ANY" | "ANY SHORTEST"}`. +// +// {{string(.SearchPrefix)}} +type GQLPathSearchPrefix struct { + // pos = StartPos + // end = LastEnd + + StartPos token.Pos // position of "ALL" or "ANY" + LastEnd token.Pos // end of last token("ALL" or "ANY" or "SHORTEST") + + SearchPrefix GQLSearchPrefixEnum +} diff --git a/ast/ast_const.go b/ast/ast_const.go index eb629325..e51e17e8 100644 --- a/ast/ast_const.go +++ b/ast/ast_const.go @@ -158,3 +158,36 @@ const ( InsertOrTypeUpdate InsertOrType = "UPDATE" InsertOrTypeIgnore InsertOrType = "IGNORE" ) + +type GQLSetOperatorEnum string + +const ( + GQLSetOperatorUnion GQLSetOperatorEnum = "UNION" + GQLSetOperatorIntersect GQLSetOperatorEnum = "INTERSECT" + GQLSetOperatorExcept GQLSetOperatorEnum = "EXCEPT" +) + +type GQLDirectionEnum string + +const ( + GQLSortOrderAsc GQLDirectionEnum = "ASC" + GQLSortOrderAscending GQLDirectionEnum = "ASCENDING" + GQLSortOrderDesc GQLDirectionEnum = "DESC" + GQLSortOrderDescending GQLDirectionEnum = "DESCENDING" +) + +type GQLPathModeEnum string + +const ( + GQLPathModeWalk GQLPathModeEnum = "WALK" + GQLPathModeTrail GQLPathModeEnum = "TRAIL" + GQLPathModeAcyclic GQLPathModeEnum = "ACYCLIC" +) + +type GQLSearchPrefixEnum string + +const ( + GQLPathSearchPrefixAll GQLSearchPrefixEnum = "ALL" + GQLPathSearchPrefixAny GQLSearchPrefixEnum = "ANY" + GQLPathSearchPrefixAnyShortest GQLSearchPrefixEnum = "ANY SHORTEST" +) diff --git a/ast/pos.go b/ast/pos.go index b6ff62b2..45682d70 100644 --- a/ast/pos.go +++ b/ast/pos.go @@ -366,6 +366,14 @@ func (p *ParenTableExpr) End() token.Pos { return posChoice(nodeEnd(wrapNode(p.Sample)), posAdd(p.Rparen, 1)) } +func (g *GraphTableExpr) Pos() token.Pos { + return g.GraphTable +} + +func (g *GraphTableExpr) End() token.Pos { + return posChoice(nodeEnd(wrapNode(g.As)), posAdd(g.Rparen, 1)) +} + func (j *Join) Pos() token.Pos { return nodePos(wrapNode(j.Left)) } @@ -454,6 +462,14 @@ func (v *ValuesInCondition) End() token.Pos { return posAdd(v.Rparen, 1) } +func (g *GQLSubQueryInCondition) Pos() token.Pos { + return g.Lbrace +} + +func (g *GQLSubQueryInCondition) End() token.Pos { + return posAdd(g.Rbrace, 1) +} + func (i *IsNullExpr) Pos() token.Pos { return nodePos(wrapNode(i.Left)) } @@ -470,6 +486,22 @@ func (i *IsBoolExpr) End() token.Pos { return posAdd(i.RightPos, ifThenElse(i.Right, 4, 5)) } +func (i *IsSourceExpr) Pos() token.Pos { + return nodePos(wrapNode(i.Node)) +} + +func (i *IsSourceExpr) End() token.Pos { + return nodeEnd(wrapNode(i.Edge)) +} + +func (i *IsDestinationExpr) Pos() token.Pos { + return nodePos(wrapNode(i.Node)) +} + +func (i *IsDestinationExpr) End() token.Pos { + return nodeEnd(wrapNode(i.Edge)) +} + func (b *BetweenExpr) Pos() token.Pos { return nodePos(wrapNode(b.Left)) } @@ -726,6 +758,30 @@ func (a *ArraySubQuery) End() token.Pos { return posAdd(a.Rparen, 1) } +func (a *ArrayGQLSubQuery) Pos() token.Pos { + return a.Array +} + +func (a *ArrayGQLSubQuery) End() token.Pos { + return posAdd(a.Rbrace, 1) +} + +func (v *ValueGQLSubQuery) Pos() token.Pos { + return v.Array +} + +func (v *ValueGQLSubQuery) End() token.Pos { + return posAdd(v.Rbrace, 1) +} + +func (e *ExistsGQLSubQuery) Pos() token.Pos { + return e.Exists +} + +func (e *ExistsGQLSubQuery) End() token.Pos { + return posAdd(e.Rbrace, 1) +} + func (e *ExistsSubQuery) Pos() token.Pos { return e.Exists } @@ -2061,3 +2117,411 @@ func (c *Call) Pos() token.Pos { func (c *Call) End() token.Pos { return posAdd(c.Rparen, 1) } + +func (g *GQLGraphQuery) Pos() token.Pos { + return nodePos(nodeChoice(wrapNode(g.Hint), wrapNode(g.GraphClause), wrapNode(g.MultiLinearQueryStatement))) +} + +func (g *GQLGraphQuery) End() token.Pos { + return nodeEnd(wrapNode(g.MultiLinearQueryStatement)) +} + +func (g *GQLQueryExpr) Pos() token.Pos { + return nodePos(nodeChoice(wrapNode(g.GraphClause), wrapNode(g.MultiLinearQueryStatement))) +} + +func (g *GQLQueryExpr) End() token.Pos { + return nodeEnd(wrapNode(g.MultiLinearQueryStatement)) +} + +func (g *GQLGraphClause) Pos() token.Pos { + return g.Graph +} + +func (g *GQLGraphClause) End() token.Pos { + return nodeEnd(wrapNode(g.PropertyGraphName)) +} + +func (g *GQLMultiLinearQueryStatement) Pos() token.Pos { + return nodePos(nodeSliceIndex(g.LinearQueryStatementList, 0)) +} + +func (g *GQLMultiLinearQueryStatement) End() token.Pos { + return nodeEnd(nodeSliceLast(g.LinearQueryStatementList)) +} + +func (g *GQLSimpleLinearQueryStatement) Pos() token.Pos { + return nodePos(nodeSliceIndex(g.PrimitiveQueryStatementList, 0)) +} + +func (g *GQLSimpleLinearQueryStatement) End() token.Pos { + return nodeEnd(nodeSliceLast(g.PrimitiveQueryStatementList)) +} + +func (g *GQLSimpleLinearQueryStatementWithSetOperator) Pos() token.Pos { + return g.StartPos +} + +func (g *GQLSimpleLinearQueryStatementWithSetOperator) End() token.Pos { + return nodeEnd(wrapNode(g.Statement)) +} + +func (g *GQLCompositeLinearQueryStatement) Pos() token.Pos { + return nodePos(wrapNode(g.HeadSimpleLinearQueryStatement)) +} + +func (g *GQLCompositeLinearQueryStatement) End() token.Pos { + return nodeEnd(nodeSliceLast(g.TailSimpleLinearQueryStatementList)) +} + +func (g *GQLMatchStatement) Pos() token.Pos { + return posChoice(g.Optional, g.Match) +} + +func (g *GQLMatchStatement) End() token.Pos { + return nodeEnd(wrapNode(g.GraphPattern)) +} + +func (g *GQLFilterStatement) Pos() token.Pos { + return g.Filter +} + +func (g *GQLFilterStatement) End() token.Pos { + return nodeEnd(wrapNode(g.Expr)) +} + +func (g *GQLForStatement) Pos() token.Pos { + return g.For +} + +func (g *GQLForStatement) End() token.Pos { + return nodeEnd(nodeChoice(wrapNode(g.WithOffsetClause), wrapNode(g.ArrayExpression))) +} + +func (g *GQLWithOffsetClause) Pos() token.Pos { + return g.With +} + +func (g *GQLWithOffsetClause) End() token.Pos { + return posChoice(nodeEnd(wrapNode(g.OffsetName)), posAdd(g.Offset, 6)) +} + +func (g *GQLLimitClause) Pos() token.Pos { + return nodePos(wrapNode(g.Limit)) +} + +func (g *GQLLimitClause) End() token.Pos { + return nodeEnd(wrapNode(g.Limit)) +} + +func (g *GQLOffsetClause) Pos() token.Pos { + return nodePos(wrapNode(g.Offset)) +} + +func (g *GQLOffsetClause) End() token.Pos { + return nodeEnd(wrapNode(g.Offset)) +} + +func (g *GQLLimitWithOffsetClause) Pos() token.Pos { + return nodePos(wrapNode(g.Offset)) +} + +func (g *GQLLimitWithOffsetClause) End() token.Pos { + return nodeEnd(wrapNode(g.Limit)) +} + +func (g *GQLLimitStatement) Pos() token.Pos { + return g.Limit +} + +func (g *GQLLimitStatement) End() token.Pos { + return nodeEnd(wrapNode(g.Count)) +} + +func (g *GQLOffsetStatement) Pos() token.Pos { + return g.Offset +} + +func (g *GQLOffsetStatement) End() token.Pos { + return nodeEnd(wrapNode(g.Count)) +} + +func (g *GQLOrderByStatement) Pos() token.Pos { + return g.Order +} + +func (g *GQLOrderByStatement) End() token.Pos { + return nodeEnd(nodeSliceLast(g.OrderBySpecificationList)) +} + +func (g *GQLOrderBySpecification) Pos() token.Pos { + return nodePos(wrapNode(g.Expr)) +} + +func (g *GQLOrderBySpecification) End() token.Pos { + return posChoice(g.DirectionPos, nodeEnd(wrapNode(g.CollationSpecification))) +} + +func (g *GQLCollationSpecification) Pos() token.Pos { + return g.Collate +} + +func (g *GQLCollationSpecification) End() token.Pos { + return nodeEnd(wrapNode(g.Specification)) +} + +func (g *GQLWithStatement) Pos() token.Pos { + return g.With +} + +func (g *GQLWithStatement) End() token.Pos { + return nodeEnd(nodeChoice(wrapNode(g.GroupByClause), nodeSliceLast(g.ReturnItemList))) +} + +func (g *GQLReturnItem) Pos() token.Pos { + return nodePos(wrapNode(g.Item)) +} + +func (g *GQLReturnItem) End() token.Pos { + return nodeEnd(wrapNode(g.Item)) +} + +func (g *GQLReturnStatement) Pos() token.Pos { + return g.Return +} + +func (g *GQLReturnStatement) End() token.Pos { + return nodeEnd(nodeChoice(wrapNode(g.LimitAndOffsetClause), wrapNode(g.OrderByClause), wrapNode(g.GroupByClause), nodeSliceLast(g.ReturnItemList))) +} + +func (g *GQLLinearGraphVariable) Pos() token.Pos { + return nodePos(wrapNode(g.VariableName)) +} + +func (g *GQLLinearGraphVariable) End() token.Pos { + return nodeEnd(wrapNode(g.Value)) +} + +func (g *GQLLetStatement) Pos() token.Pos { + return g.Let +} + +func (g *GQLLetStatement) End() token.Pos { + return nodeEnd(nodeSliceLast(g.LinearGraphVariableList)) +} + +func (g *GQLGraphPattern) Pos() token.Pos { + return nodePos(nodeSliceIndex(g.PathPatternList, 0)) +} + +func (g *GQLGraphPattern) End() token.Pos { + return nodeEnd(nodeChoice(wrapNode(g.WhereClause), nodeSliceLast(g.PathPatternList))) +} + +func (g *GQLTopLevelPathPattern) Pos() token.Pos { + return nodePos(nodeChoice(wrapNode(g.PathSearchPrefixOrPathMode), wrapNode(g.PathPattern))) +} + +func (g *GQLTopLevelPathPattern) End() token.Pos { + return nodeEnd(wrapNode(g.PathPattern)) +} + +func (g *GQLFullEdgeAny) Pos() token.Pos { + return g.FirstHyphen +} + +func (g *GQLFullEdgeAny) End() token.Pos { + return posAdd(g.LastHyphen, 1) +} + +func (g *GQLFullEdgeLeft) Pos() token.Pos { + return g.Lt +} + +func (g *GQLFullEdgeLeft) End() token.Pos { + return posAdd(g.Hyphen, 1) +} + +func (g *GQLFullEdgeRight) Pos() token.Pos { + return g.Hyphen +} + +func (g *GQLFullEdgeRight) End() token.Pos { + return posAdd(g.Arrow, 2) +} + +func (g *GQLAbbreviatedEdgeAny) Pos() token.Pos { + return g.Hyphen +} + +func (g *GQLAbbreviatedEdgeAny) End() token.Pos { + return posAdd(g.Hyphen, 1) +} + +func (g *GQLAbbreviatedEdgeLeft) Pos() token.Pos { + return g.Lt +} + +func (g *GQLAbbreviatedEdgeLeft) End() token.Pos { + return posAdd(g.Hyphen, 1) +} + +func (g *GQLAbbreviatedEdgeRight) Pos() token.Pos { + return g.Hyphen +} + +func (g *GQLAbbreviatedEdgeRight) End() token.Pos { + return posAdd(g.Arrow, 2) +} + +func (g *GQLQuantifiablePathTerm) Pos() token.Pos { + return nodePos(nodeChoice(wrapNode(g.Hint), wrapNode(g.PathTerm))) +} + +func (g *GQLQuantifiablePathTerm) End() token.Pos { + return nodeEnd(nodeChoice(wrapNode(g.Quantifier), wrapNode(g.PathTerm))) +} + +func (g *GQLPathPattern) Pos() token.Pos { + return nodePos(nodeSliceIndex(g.PathTermList, 0)) +} + +func (g *GQLPathPattern) End() token.Pos { + return nodeEnd(nodeSliceLast(g.PathTermList)) +} + +func (g *GQLWhereClause) Pos() token.Pos { + return g.Where +} + +func (g *GQLWhereClause) End() token.Pos { + return nodeEnd(wrapNode(g.BoolExpression)) +} + +func (g *GQLPathMode) Pos() token.Pos { + return nodePos(wrapNode(g.ModeToken)) +} + +func (g *GQLPathMode) End() token.Pos { + return nodeEnd(nodeChoice(wrapNode(g.PathOrPathsToken), wrapNode(g.ModeToken))) +} + +func (g *GQLFixedQuantifier) Pos() token.Pos { + return g.Lbrace +} + +func (g *GQLFixedQuantifier) End() token.Pos { + return posAdd(g.Rbrace, 1) +} + +func (g *GQLBoundedQuantifier) Pos() token.Pos { + return g.Lbrace +} + +func (g *GQLBoundedQuantifier) End() token.Pos { + return posAdd(g.Rbrace, 1) +} + +func (g *GQLSubpathPattern) Pos() token.Pos { + return g.Lparen +} + +func (g *GQLSubpathPattern) End() token.Pos { + return posAdd(g.Rparen, 1) +} + +func (g *GQLNodePattern) Pos() token.Pos { + return g.Lparen +} + +func (g *GQLNodePattern) End() token.Pos { + return posAdd(g.Rparen, 1) +} + +func (g *GQLPatternFiller) Pos() token.Pos { + return nodePos(nodeChoice(wrapNode(g.Hint), wrapNode(g.GraphPatternVariable), wrapNode(g.IsLabelCondition), wrapNode(g.Filter))) +} + +func (g *GQLPatternFiller) End() token.Pos { + return nodeEnd(nodeChoice(wrapNode(g.Filter), wrapNode(g.IsLabelCondition), wrapNode(g.GraphPatternVariable), wrapNode(g.Hint))) +} + +func (g *GQLIsLabelCondition) Pos() token.Pos { + return g.IsOrColon +} + +func (g *GQLIsLabelCondition) End() token.Pos { + return nodeEnd(wrapNode(g.LabelExpression)) +} + +func (g *GQLLabelOrExpression) Pos() token.Pos { + return nodePos(wrapNode(g.Left)) +} + +func (g *GQLLabelOrExpression) End() token.Pos { + return nodeEnd(wrapNode(g.Right)) +} + +func (g *GQLLabelParenExpression) Pos() token.Pos { + return g.Lparen +} + +func (g *GQLLabelParenExpression) End() token.Pos { + return posAdd(g.Rparen, 1) +} + +func (g *GQLLabelAndExpression) Pos() token.Pos { + return nodePos(wrapNode(g.Left)) +} + +func (g *GQLLabelAndExpression) End() token.Pos { + return nodeEnd(wrapNode(g.Right)) +} + +func (g *GQLLabelNotExpression) Pos() token.Pos { + return g.Not +} + +func (g *GQLLabelNotExpression) End() token.Pos { + return nodeEnd(wrapNode(g.LabelExpression)) +} + +func (g *GQLWildcardLabel) Pos() token.Pos { + return g.Percent +} + +func (g *GQLWildcardLabel) End() token.Pos { + return posAdd(g.Percent, 1) +} + +func (g *GQLElementLabel) Pos() token.Pos { + return nodePos(wrapNode(g.LabelName)) +} + +func (g *GQLElementLabel) End() token.Pos { + return nodeEnd(wrapNode(g.LabelName)) +} + +func (g *GQLPropertyFilters) Pos() token.Pos { + return g.Lbrace +} + +func (g *GQLPropertyFilters) End() token.Pos { + return posAdd(g.Rbrace, 1) +} + +func (g *GQLElementProperty) Pos() token.Pos { + return nodePos(wrapNode(g.ElementPropertyName)) +} + +func (g *GQLElementProperty) End() token.Pos { + return nodeEnd(wrapNode(g.ElementPropertyValue)) +} + +func (g *GQLPathSearchPrefix) Pos() token.Pos { + return g.StartPos +} + +func (g *GQLPathSearchPrefix) End() token.Pos { + return g.LastEnd +} diff --git a/ast/sql.go b/ast/sql.go index 530d84ae..63dcb591 100644 --- a/ast/sql.go +++ b/ast/sql.go @@ -111,7 +111,7 @@ func exprPrec(e Expr) prec { return precLit case *IndexExpr, *SelectorExpr: return precSelector - case *InExpr, *IsNullExpr, *IsBoolExpr, *BetweenExpr: + case *InExpr, *IsNullExpr, *IsBoolExpr, *IsSourceExpr, *IsDestinationExpr, *BetweenExpr: return precComparison case *BinaryExpr: switch e.Op { @@ -391,6 +391,13 @@ func (t *TableSampleSize) SQL() string { return "(" + t.Value.SQL() + " " + string(t.Unit) + ")" } +func (g *GraphTableExpr) SQL() string { + return "GRAPH_TABLE(" + + g.PropertyGraphName.SQL() + " " + + g.Query.SQL() + + ")" + sqlOpt(" ", g.As, "") +} + // ================================================================================ // // Expr @@ -429,6 +436,10 @@ func (v *ValuesInCondition) SQL() string { return "(" + sqlJoin(v.Exprs, ", ") + ")" } +func (g *GQLSubQueryInCondition) SQL() string { + return "{" + g.Query.SQL() + "}" +} + func (i *IsNullExpr) SQL() string { p := exprPrec(i) return paren(p, i.Left) + @@ -440,6 +451,28 @@ func (i *IsBoolExpr) SQL() string { return paren(p, i.Left) + " IS " + strOpt(i.Not, "NOT ") + formatBoolUpper(i.Right) } +func (i *IsSourceExpr) SQL() string { + p := exprPrec(i) + sql := paren(p, i.Node) + sql += " IS " + if i.Not { + sql += "NOT " + } + sql += "SOURCE OF " + i.Edge.SQL() + return sql +} + +func (i *IsDestinationExpr) SQL() string { + p := exprPrec(i) + sql := paren(p, i.Node) + sql += " IS " + if i.Not { + sql += "NOT " + } + sql += "DESTINATION OF " + i.Edge.SQL() + return sql +} + func (b *BetweenExpr) SQL() string { p := exprPrec(b) return paren(p, b.Left) + @@ -725,6 +758,24 @@ func (c *CastNumValue) SQL() string { return "CAST(" + c.Expr.SQL() + " AS " + string(c.Type) + ")" } +// ================================================================================ +// +// GQL subquery expressions +// +// ================================================================================ + +func (a *ArrayGQLSubQuery) SQL() string { + return "ARRAY {" + a.Query.SQL() + "}" +} + +func (v *ValueGQLSubQuery) SQL() string { + return "VALUE {" + v.Query.SQL() + "}" +} + +func (v *ExistsGQLSubQuery) SQL() string { + return "EXISTS {" + v.Query.SQL() + "}" +} + // ================================================================================ // // DDL @@ -1367,3 +1418,213 @@ func (u *UpdateItem) SQL() string { func (c *Call) SQL() string { return "CALL " + c.Name.SQL() + "(" + sqlJoin(c.Args, ", ") + ")" } + +// ================================================================================ +// +// GQL +// +// ================================================================================ + +func (s *GQLGraphQuery) SQL() string { + return sqlOpt("", s.Hint, " ") + s.GraphClause.SQL() + " " + s.MultiLinearQueryStatement.SQL() +} + +func (g *GQLQueryExpr) SQL() string { + return sqlOpt("", g.GraphClause, " ") + g.MultiLinearQueryStatement.SQL() +} + +func (s *GQLGraphClause) SQL() string { return "GRAPH " + s.PropertyGraphName.SQL() } + +func (s *GQLMultiLinearQueryStatement) SQL() string { + return sqlJoin(s.LinearQueryStatementList, " NEXT ") +} + +func (s *GQLSimpleLinearQueryStatement) SQL() string { + return sqlJoin(s.PrimitiveQueryStatementList, " ") +} + +func (s *GQLCompositeLinearQueryStatement) SQL() string { + return s.HeadSimpleLinearQueryStatement.SQL() + + strOpt(len(s.TailSimpleLinearQueryStatementList) > 0, " ") + + sqlJoin(s.TailSimpleLinearQueryStatementList, " ") +} + +func (g *GQLSimpleLinearQueryStatementWithSetOperator) SQL() string { + return string(g.SetOperator) + + strOpt(g.AllOrDistinct != "", " "+string(g.AllOrDistinct)) + + " " + g.Statement.SQL() +} + +func (s *GQLLinearGraphVariable) SQL() string { return s.VariableName.SQL() + " = " + s.Value.SQL() } + +// ================================================================================ +// +// GQL statements +// +// ================================================================================ + +func (g *GQLMatchStatement) SQL() string { + return strIfElse(g.Optional.Invalid(), "MATCH", "OPTIONAL MATCH") + + sqlOpt("", g.MatchHint, "") + + sqlOpt(" ", g.PrefixOrMode, "") + + " " + g.GraphPattern.SQL() +} + +func (g *GQLFilterStatement) SQL() string { + return "FILTER " + strOpt(!g.Where.Invalid(), "WHERE ") + g.Expr.SQL() +} + +func (g *GQLForStatement) SQL() string { + return "FOR " + g.ElementName.SQL() + " IN " + + g.ArrayExpression.SQL() + sqlOpt(" ", g.WithOffsetClause, "") +} + +func (g *GQLWithOffsetClause) SQL() string { + return "WITH OFFSET" + sqlOpt(" ", g.OffsetName, "") +} + +func (g *GQLLimitClause) SQL() string { return g.Limit.SQL() } + +func (g *GQLOffsetClause) SQL() string { return g.Offset.SQL() } + +func (g *GQLLimitWithOffsetClause) SQL() string { return g.Offset.SQL() + " " + g.Limit.SQL() } + +func (g *GQLLimitStatement) SQL() string { return "LIMIT " + g.Count.SQL() } + +func (g *GQLOffsetStatement) SQL() string { + if g.IsSkip { + return "SKIP " + g.Count.SQL() + } + return "OFFSET " + g.Count.SQL() +} + +func (g *GQLOrderByStatement) SQL() string { + return "ORDER BY " + sqlJoin(g.OrderBySpecificationList, ", ") +} + +func (g *GQLOrderBySpecification) SQL() string { + return g.Expr.SQL() + sqlOpt(" ", g.CollationSpecification, "") + + strOpt(g.Direction != "", " "+string(g.Direction)) +} + +func (g *GQLCollationSpecification) SQL() string { + return "COLLATE " + g.Specification.SQL() +} + +func (g *GQLWithStatement) SQL() string { + return "WITH " + strOpt(g.AllOrDistinct != "", string(g.AllOrDistinct)+" ") + + sqlJoin(g.ReturnItemList, ", ") + + sqlOpt(" ", g.GroupByClause, "") +} + +func (s *GQLLetStatement) SQL() string { return "LET " + sqlJoin(s.LinearGraphVariableList, ", ") } + +func (g *GQLReturnItem) SQL() string { + return g.Item.SQL() +} + +func (g *GQLReturnStatement) SQL() string { + return "RETURN " + + strOpt(g.AllOrDistinct != "", string(g.AllOrDistinct)+" ") + + sqlJoin(g.ReturnItemList, ", ") + + sqlOpt(" ", g.GroupByClause, "") + + sqlOpt(" ", g.OrderByClause, "") + + sqlOpt(" ", g.LimitAndOffsetClause, "") +} + +// GQL graph patterns + +func (g *GQLGraphPattern) SQL() string { + return sqlJoin(g.PathPatternList, ", ") + sqlOpt(" ", g.WhereClause, "") +} + +func (g *GQLTopLevelPathPattern) SQL() string { + return sqlOpt("", g.Var, " = ") + sqlOpt("", g.PathSearchPrefixOrPathMode, " ") + g.PathPattern.SQL() +} + +func (g *GQLFullEdgeAny) SQL() string { return "-[" + sqlOpt("", g.PatternFiller, "") + "]-" } + +func (g *GQLFullEdgeLeft) SQL() string { return "<-[" + sqlOpt("", g.PatternFiller, "") + "]-" } + +func (g *GQLFullEdgeRight) SQL() string { return "-[" + sqlOpt("", g.PatternFiller, "") + "]->" } + +func (g *GQLAbbreviatedEdgeAny) SQL() string { return "-" } + +func (g *GQLAbbreviatedEdgeLeft) SQL() string { return "<-" } + +func (g *GQLAbbreviatedEdgeRight) SQL() string { return "->" } + +func (g *GQLQuantifiablePathTerm) SQL() string { + return sqlOpt("", g.Hint, "") + g.PathTerm.SQL() + sqlOpt("", g.Quantifier, "") +} + +func (g *GQLPathPattern) SQL() string { return sqlJoin(g.PathTermList, "") } + +func (g *GQLWhereClause) SQL() string { return "WHERE " + g.BoolExpression.SQL() } + +func (g *GQLPathMode) SQL() string { + return string(g.Mode) + sqlOpt(" ", g.PathOrPathsToken, "") +} + +func (g *GQLFixedQuantifier) SQL() string { return "{" + g.Bound.SQL() + "}" } + +func (g *GQLBoundedQuantifier) SQL() string { + return "{" + sqlOpt("", g.LowerBound, "") + "," + g.UpperBound.SQL() + "}" +} + +func (g *GQLSubpathPattern) SQL() string { + return "(" + + sqlOpt("", g.PathMode, " ") + + g.PathPattern.SQL() + + sqlOpt(" ", g.WhereClause, "") + + ")" +} + +func (g *GQLNodePattern) SQL() string { return "(" + g.PatternFiller.SQL() + ")" } + +func (g *GQLPatternFiller) SQL() string { + return sqlOpt("", g.Hint, "") + + sqlOpt("", g.GraphPatternVariable, "") + + sqlOpt("", g.IsLabelCondition, "") + + sqlOpt( + strOpt(g.Hint != nil || g.GraphPatternVariable != nil || g.IsLabelCondition != nil, " "), + g.Filter, "") +} + +func (g *GQLIsLabelCondition) SQL() string { return ":" + g.LabelExpression.SQL() } + +func (g *GQLLabelParenExpression) SQL() string { return "(" + g.LabelExpr.SQL() + ")" } + +func (g *GQLLabelOrExpression) SQL() string { return g.Left.SQL() + "|" + g.Right.SQL() } + +func (g *GQLLabelAndExpression) SQL() string { return g.Left.SQL() + "&" + g.Right.SQL() } + +func (g *GQLLabelNotExpression) SQL() string { return "!" + g.LabelExpression.SQL() } + +func (g *GQLWildcardLabel) SQL() string { + return "%" +} +func (g *GQLElementLabel) SQL() string { + return g.LabelName.SQL() +} + +func (g *GQLPropertyFilters) SQL() string { + return "{" + sqlJoin(g.PropertyFilterElemList, ", ") + "}" +} + +func (g *GQLElementProperty) SQL() string { + return g.ElementPropertyName.SQL() + ": " + g.ElementPropertyValue.SQL() +} + +func (p *GQLPathSearchPrefix) SQL() string { + switch p.SearchPrefix { + case GQLPathSearchPrefixAny: + return "ANY" + case GQLPathSearchPrefixAnyShortest: + return "ANY SHORTEST" + case GQLPathSearchPrefixAll: + return "ALL" + default: + panic("unreached condition: invalid GQLPathSearchPrefix") + } +} diff --git a/ast/walk_internal.go b/ast/walk_internal.go index 5a351eb8..66f5bb24 100644 --- a/ast/walk_internal.go +++ b/ast/walk_internal.go @@ -176,6 +176,11 @@ func walkInternal(node Node, v Visitor, stack []*stackItem) []*stackItem { stack = append(stack, &stackItem{node: wrapNode(n.Sample), visitor: v.Field("Sample")}) stack = append(stack, &stackItem{node: wrapNode(n.Source), visitor: v.Field("Source")}) + case *GraphTableExpr: + stack = append(stack, &stackItem{node: wrapNode(n.As), visitor: v.Field("As")}) + stack = append(stack, &stackItem{node: wrapNode(n.Query), visitor: v.Field("Query")}) + stack = append(stack, &stackItem{node: wrapNode(n.PropertyGraphName), visitor: v.Field("PropertyGraphName")}) + case *Join: stack = append(stack, &stackItem{node: wrapNode(n.Cond), visitor: v.Field("Cond")}) stack = append(stack, &stackItem{node: wrapNode(n.Right), visitor: v.Field("Right")}) @@ -214,12 +219,23 @@ func walkInternal(node Node, v Visitor, stack []*stackItem) []*stackItem { case *ValuesInCondition: stack = append(stack, &stackItem{nodes: wrapNodes(n.Exprs), visitor: v.Field("Exprs")}) + case *GQLSubQueryInCondition: + stack = append(stack, &stackItem{node: wrapNode(n.Query), visitor: v.Field("Query")}) + case *IsNullExpr: stack = append(stack, &stackItem{node: wrapNode(n.Left), visitor: v.Field("Left")}) case *IsBoolExpr: stack = append(stack, &stackItem{node: wrapNode(n.Left), visitor: v.Field("Left")}) + case *IsSourceExpr: + stack = append(stack, &stackItem{node: wrapNode(n.Edge), visitor: v.Field("Edge")}) + stack = append(stack, &stackItem{node: wrapNode(n.Node), visitor: v.Field("Node")}) + + case *IsDestinationExpr: + stack = append(stack, &stackItem{node: wrapNode(n.Edge), visitor: v.Field("Edge")}) + stack = append(stack, &stackItem{node: wrapNode(n.Node), visitor: v.Field("Node")}) + case *BetweenExpr: stack = append(stack, &stackItem{node: wrapNode(n.RightEnd), visitor: v.Field("RightEnd")}) stack = append(stack, &stackItem{node: wrapNode(n.RightStart), visitor: v.Field("RightStart")}) @@ -344,6 +360,15 @@ func walkInternal(node Node, v Visitor, stack []*stackItem) []*stackItem { case *ArraySubQuery: stack = append(stack, &stackItem{node: wrapNode(n.Query), visitor: v.Field("Query")}) + case *ArrayGQLSubQuery: + stack = append(stack, &stackItem{node: wrapNode(n.Query), visitor: v.Field("Query")}) + + case *ValueGQLSubQuery: + stack = append(stack, &stackItem{node: wrapNode(n.Query), visitor: v.Field("Query")}) + + case *ExistsGQLSubQuery: + // nothing to do + case *ExistsSubQuery: stack = append(stack, &stackItem{node: wrapNode(n.Query), visitor: v.Field("Query")}) stack = append(stack, &stackItem{node: wrapNode(n.Hint), visitor: v.Field("Hint")}) @@ -944,6 +969,184 @@ func walkInternal(node Node, v Visitor, stack []*stackItem) []*stackItem { case *Call: stack = append(stack, &stackItem{nodes: wrapNodes(n.Args), visitor: v.Field("Args")}) stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *GQLGraphQuery: + stack = append(stack, &stackItem{node: wrapNode(n.MultiLinearQueryStatement), visitor: v.Field("MultiLinearQueryStatement")}) + stack = append(stack, &stackItem{node: wrapNode(n.GraphClause), visitor: v.Field("GraphClause")}) + stack = append(stack, &stackItem{node: wrapNode(n.Hint), visitor: v.Field("Hint")}) + + case *GQLQueryExpr: + stack = append(stack, &stackItem{node: wrapNode(n.MultiLinearQueryStatement), visitor: v.Field("MultiLinearQueryStatement")}) + stack = append(stack, &stackItem{node: wrapNode(n.GraphClause), visitor: v.Field("GraphClause")}) + + case *GQLGraphClause: + stack = append(stack, &stackItem{node: wrapNode(n.PropertyGraphName), visitor: v.Field("PropertyGraphName")}) + + case *GQLMultiLinearQueryStatement: + // nothing to do + + case *GQLSimpleLinearQueryStatement: + // nothing to do + + case *GQLSimpleLinearQueryStatementWithSetOperator: + stack = append(stack, &stackItem{node: wrapNode(n.Statement), visitor: v.Field("Statement")}) + + case *GQLCompositeLinearQueryStatement: + stack = append(stack, &stackItem{nodes: wrapNodes(n.TailSimpleLinearQueryStatementList), visitor: v.Field("TailSimpleLinearQueryStatementList")}) + stack = append(stack, &stackItem{node: wrapNode(n.HeadSimpleLinearQueryStatement), visitor: v.Field("HeadSimpleLinearQueryStatement")}) + + case *GQLMatchStatement: + stack = append(stack, &stackItem{node: wrapNode(n.GraphPattern), visitor: v.Field("GraphPattern")}) + stack = append(stack, &stackItem{node: wrapNode(n.MatchHint), visitor: v.Field("MatchHint")}) + + case *GQLFilterStatement: + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *GQLForStatement: + stack = append(stack, &stackItem{node: wrapNode(n.WithOffsetClause), visitor: v.Field("WithOffsetClause")}) + stack = append(stack, &stackItem{node: wrapNode(n.ArrayExpression), visitor: v.Field("ArrayExpression")}) + stack = append(stack, &stackItem{node: wrapNode(n.ElementName), visitor: v.Field("ElementName")}) + + case *GQLWithOffsetClause: + stack = append(stack, &stackItem{node: wrapNode(n.OffsetName), visitor: v.Field("OffsetName")}) + + case *GQLLimitClause: + stack = append(stack, &stackItem{node: wrapNode(n.Limit), visitor: v.Field("Limit")}) + + case *GQLOffsetClause: + stack = append(stack, &stackItem{node: wrapNode(n.Offset), visitor: v.Field("Offset")}) + + case *GQLLimitWithOffsetClause: + stack = append(stack, &stackItem{node: wrapNode(n.Limit), visitor: v.Field("Limit")}) + stack = append(stack, &stackItem{node: wrapNode(n.Offset), visitor: v.Field("Offset")}) + + case *GQLLimitStatement: + stack = append(stack, &stackItem{node: wrapNode(n.Count), visitor: v.Field("Count")}) + + case *GQLOffsetStatement: + stack = append(stack, &stackItem{node: wrapNode(n.Count), visitor: v.Field("Count")}) + + case *GQLOrderByStatement: + stack = append(stack, &stackItem{nodes: wrapNodes(n.OrderBySpecificationList), visitor: v.Field("OrderBySpecificationList")}) + + case *GQLOrderBySpecification: + stack = append(stack, &stackItem{node: wrapNode(n.CollationSpecification), visitor: v.Field("CollationSpecification")}) + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *GQLCollationSpecification: + stack = append(stack, &stackItem{node: wrapNode(n.Specification), visitor: v.Field("Specification")}) + + case *GQLWithStatement: + stack = append(stack, &stackItem{node: wrapNode(n.GroupByClause), visitor: v.Field("GroupByClause")}) + stack = append(stack, &stackItem{nodes: wrapNodes(n.ReturnItemList), visitor: v.Field("ReturnItemList")}) + + case *GQLReturnItem: + stack = append(stack, &stackItem{node: wrapNode(n.Item), visitor: v.Field("Item")}) + + case *GQLReturnStatement: + stack = append(stack, &stackItem{node: wrapNode(n.LimitAndOffsetClause), visitor: v.Field("LimitAndOffsetClause")}) + stack = append(stack, &stackItem{node: wrapNode(n.OrderByClause), visitor: v.Field("OrderByClause")}) + stack = append(stack, &stackItem{node: wrapNode(n.GroupByClause), visitor: v.Field("GroupByClause")}) + stack = append(stack, &stackItem{nodes: wrapNodes(n.ReturnItemList), visitor: v.Field("ReturnItemList")}) + + case *GQLLinearGraphVariable: + stack = append(stack, &stackItem{node: wrapNode(n.Value), visitor: v.Field("Value")}) + stack = append(stack, &stackItem{node: wrapNode(n.VariableName), visitor: v.Field("VariableName")}) + + case *GQLLetStatement: + stack = append(stack, &stackItem{nodes: wrapNodes(n.LinearGraphVariableList), visitor: v.Field("LinearGraphVariableList")}) + + case *GQLGraphPattern: + stack = append(stack, &stackItem{node: wrapNode(n.WhereClause), visitor: v.Field("WhereClause")}) + stack = append(stack, &stackItem{nodes: wrapNodes(n.PathPatternList), visitor: v.Field("PathPatternList")}) + + case *GQLTopLevelPathPattern: + stack = append(stack, &stackItem{node: wrapNode(n.PathPattern), visitor: v.Field("PathPattern")}) + stack = append(stack, &stackItem{node: wrapNode(n.Var), visitor: v.Field("Var")}) + + case *GQLFullEdgeAny: + stack = append(stack, &stackItem{node: wrapNode(n.PatternFiller), visitor: v.Field("PatternFiller")}) + + case *GQLFullEdgeLeft: + stack = append(stack, &stackItem{node: wrapNode(n.PatternFiller), visitor: v.Field("PatternFiller")}) + + case *GQLFullEdgeRight: + stack = append(stack, &stackItem{node: wrapNode(n.PatternFiller), visitor: v.Field("PatternFiller")}) + + case *GQLAbbreviatedEdgeAny: + // nothing to do + + case *GQLAbbreviatedEdgeLeft: + // nothing to do + + case *GQLAbbreviatedEdgeRight: + // nothing to do + + case *GQLQuantifiablePathTerm: + stack = append(stack, &stackItem{node: wrapNode(n.Hint), visitor: v.Field("Hint")}) + + case *GQLPathPattern: + stack = append(stack, &stackItem{nodes: wrapNodes(n.PathTermList), visitor: v.Field("PathTermList")}) + + case *GQLWhereClause: + stack = append(stack, &stackItem{node: wrapNode(n.BoolExpression), visitor: v.Field("BoolExpression")}) + + case *GQLPathMode: + stack = append(stack, &stackItem{node: wrapNode(n.PathOrPathsToken), visitor: v.Field("PathOrPathsToken")}) + stack = append(stack, &stackItem{node: wrapNode(n.ModeToken), visitor: v.Field("ModeToken")}) + + case *GQLFixedQuantifier: + stack = append(stack, &stackItem{node: wrapNode(n.Bound), visitor: v.Field("Bound")}) + + case *GQLBoundedQuantifier: + stack = append(stack, &stackItem{node: wrapNode(n.UpperBound), visitor: v.Field("UpperBound")}) + stack = append(stack, &stackItem{node: wrapNode(n.LowerBound), visitor: v.Field("LowerBound")}) + + case *GQLSubpathPattern: + stack = append(stack, &stackItem{node: wrapNode(n.WhereClause), visitor: v.Field("WhereClause")}) + stack = append(stack, &stackItem{node: wrapNode(n.PathPattern), visitor: v.Field("PathPattern")}) + stack = append(stack, &stackItem{node: wrapNode(n.PathMode), visitor: v.Field("PathMode")}) + + case *GQLNodePattern: + stack = append(stack, &stackItem{node: wrapNode(n.PatternFiller), visitor: v.Field("PatternFiller")}) + + case *GQLPatternFiller: + stack = append(stack, &stackItem{node: wrapNode(n.IsLabelCondition), visitor: v.Field("IsLabelCondition")}) + stack = append(stack, &stackItem{node: wrapNode(n.GraphPatternVariable), visitor: v.Field("GraphPatternVariable")}) + stack = append(stack, &stackItem{node: wrapNode(n.Hint), visitor: v.Field("Hint")}) + + case *GQLIsLabelCondition: + // nothing to do + + case *GQLLabelOrExpression: + stack = append(stack, &stackItem{node: wrapNode(n.Right), visitor: v.Field("Right")}) + stack = append(stack, &stackItem{node: wrapNode(n.Left), visitor: v.Field("Left")}) + + case *GQLLabelParenExpression: + stack = append(stack, &stackItem{node: wrapNode(n.LabelExpr), visitor: v.Field("LabelExpr")}) + + case *GQLLabelAndExpression: + stack = append(stack, &stackItem{node: wrapNode(n.Right), visitor: v.Field("Right")}) + stack = append(stack, &stackItem{node: wrapNode(n.Left), visitor: v.Field("Left")}) + + case *GQLLabelNotExpression: + stack = append(stack, &stackItem{node: wrapNode(n.LabelExpression), visitor: v.Field("LabelExpression")}) + + case *GQLWildcardLabel: + // nothing to do + + case *GQLElementLabel: + stack = append(stack, &stackItem{node: wrapNode(n.LabelName), visitor: v.Field("LabelName")}) + + case *GQLPropertyFilters: + stack = append(stack, &stackItem{nodes: wrapNodes(n.PropertyFilterElemList), visitor: v.Field("PropertyFilterElemList")}) + + case *GQLElementProperty: + stack = append(stack, &stackItem{node: wrapNode(n.ElementPropertyValue), visitor: v.Field("ElementPropertyValue")}) + stack = append(stack, &stackItem{node: wrapNode(n.ElementPropertyName), visitor: v.Field("ElementPropertyName")}) + + case *GQLPathSearchPrefix: + // nothing to do } return stack } diff --git a/parser.go b/parser.go index cb5f8eb9..3f8a2632 100644 --- a/parser.go +++ b/parser.go @@ -30,6 +30,48 @@ func (p *Parser) ParseStatement() (ast.Statement, error) { return stmt, nil } +// ParseGQLStatement parses a GQL statement. +func (p *Parser) ParseGQLStatement() (stmt ast.Statement, err error) { + defer func() { + if r := recover(); r != nil { + stmt = nil + if e, ok := r.(*Error); ok { + err = e + } else { + panic(r) + } + } + }() + + p.nextToken() + stmt = p.parseGQLQuery() + if p.Token.Kind != token.TokenEOF { + p.panicfAtToken(&p.Token, "expected token: , but: %s", p.Token.Kind) + } + return +} + +// ParseGQLPathPattern parses a GQL statement. +func (p *Parser) ParseGQLPathPattern() (stmt *ast.GQLPathPattern, err error) { + defer func() { + if r := recover(); r != nil { + stmt = nil + if e, ok := r.(*Error); ok { + err = e + } else { + panic(r) + } + } + }() + + p.nextToken() + stmt = p.parseGQLPathPattern() + if p.Token.Kind != token.TokenEOF { + p.panicfAtToken(&p.Token, "expected token: , but: %s", p.Token.Kind) + } + return +} + // ParseStatements parses SQL statements list separated by semi-colon. func (p *Parser) ParseStatements() ([]ast.Statement, error) { p.nextToken() @@ -179,12 +221,18 @@ func (p *Parser) parseStatementInternal(hint *ast.Hint) (stmt ast.Statement) { return p.parseQueryStatementInternal(hint) case p.Token.IsKeywordLike("INSERT") || p.Token.IsKeywordLike("DELETE") || p.Token.IsKeywordLike("UPDATE"): return p.parseDMLInternal(hint) + // GQL queries are first level statements + // because it is supported by Cloud Spanner's ExecuteSql API. + case p.Token.IsKeywordLike("GRAPH"): + return p.parseGQLQuery() case hint != nil: panic(p.errorfAtPosition(hint.Pos(), p.Token.End, "statement hint is only permitted before query or DML, but got: %s", p.Token.Raw)) case p.Token.Kind == "CREATE" || p.Token.IsKeywordLike("ALTER") || p.Token.IsKeywordLike("DROP") || p.Token.IsKeywordLike("RENAME") || p.Token.IsKeywordLike("GRANT") || p.Token.IsKeywordLike("REVOKE") || p.Token.IsKeywordLike("ANALYZE"): return p.parseDDL() + case p.Token.IsKeywordLike("INSERT") || p.Token.IsKeywordLike("DELETE") || p.Token.IsKeywordLike("UPDATE"): + return p.parseDML() case p.Token.IsKeywordLike("CALL"): return p.parseOtherStatement() } @@ -1032,6 +1080,23 @@ func (p *Parser) parseSimpleTableExpr() ast.TableExpr { return p.parseUnnestSuffix(expr, unnest, rparen) } + if p.Token.Kind == ("GRAPH_TABLE") { + graphTable := p.expect("GRAPH_TABLE").Pos + lparen := p.expect("(").Pos + graphName := p.parseIdent() + query := p.parseGQLMultiLinearQueryStatement() + rparen := p.expect(")").Pos + as := p.tryParseAsAlias(withOptionalAs) + return &ast.GraphTableExpr{ + GraphTable: graphTable, + PropertyGraphName: graphName, + Lparen: lparen, + Rparen: rparen, + Query: query, + As: as, + } + } + if p.Token.Kind == token.TokenIdent { ids := p.parseIdentOrPath() if p.Token.Kind == "(" { @@ -1043,7 +1108,7 @@ func (p *Parser) parseSimpleTableExpr() ast.TableExpr { return p.parsePathTableExprSuffix(&ast.Path{Idents: ids}) } - panic(p.errorfAtToken(&p.Token, "expected token: (, UNNEST, , but: %s", p.Token.Kind)) + panic(p.errorfAtToken(&p.Token, "expected token: (, GRAPH_TABLE, UNNEST, , but: %s", p.Token.Kind)) } func (p *Parser) parseTVFCallExpr(ids []*ast.Ident) *ast.TVFCallExpr { @@ -1436,9 +1501,33 @@ func (p *Parser) parseComparison() ast.Expr { Not: not, Right: false, } - default: - p.panicfAtToken(&p.Token, "expected token: NULL, TRUE, FALSE, but: %s", p.Token.Kind) + case token.TokenIdent: + switch { + case p.Token.IsKeywordLike("SOURCE") || p.Token.IsKeywordLike("DESTINATION"): + var isSource bool + if p.Token.IsKeywordLike("SOURCE") { + isSource = true + } + p.nextToken() + if p.Token.Kind == "OF" { + p.expect("OF") + } + edge := p.parseExpr() + if isSource { + return &ast.IsSourceExpr{ + Node: expr, + Edge: edge, + Not: not, + } + } + return &ast.IsDestinationExpr{ + Node: expr, + Edge: edge, + Not: not, + } + } } + p.panicfAtToken(&p.Token, "expected token: NULL, TRUE, FALSE, SOURCE, DESTINATION, but: %s", p.Token.Kind) default: return expr } @@ -1462,6 +1551,9 @@ func (p *Parser) parseInCondition() ast.InCondition { } } + if p.Token.Kind == "{" { + return p.parseGQLSubQueryInCondition() + } if p.Token.Kind == "(" { lparen := p.Token.Pos p.nextToken() @@ -1771,6 +1863,16 @@ func (p *Parser) parseLit() ast.Expr { } p.nextToken() + if id.IsKeywordLike("VALUE") && p.Token.Kind == "{" { + p.nextToken() + query := p.parseGQLQueryExpr() + rbrace := p.expect("}").Pos + return &ast.ValueGQLSubQuery{ + Array: id.Pos, + Rbrace: rbrace, + Query: query, + } + } switch p.Token.Kind { case token.TokenString: if id.IsKeywordLike("DATE") { @@ -2196,8 +2298,56 @@ func (p *Parser) parseCastExpr() *ast.CastExpr { } } -func (p *Parser) parseExistsSubQuery() *ast.ExistsSubQuery { +func (p *Parser) lookaheadGQLGraphPattern() bool { + // path_search_prefix + return p.Token.IsKeywordLike("ANY") || p.Token.IsKeywordLike("ALL") || // path_search_prefix + p.Token.IsKeywordLike("WALK") || p.Token.IsKeywordLike("TRAIL") || // path_mode + // subpath_pattern or node_pattern + p.Token.Kind == "(" || + // edge_pattern + p.Token.Kind == "<" || p.Token.Kind == "-" + +} + +// lookaheadGQLExistsExprMatch checks the next statement is a MATCH statement followed by "}". +func (p *Parser) lookaheadGQLExistsExprMatch() bool { + lexer := p.Lexer.Clone() + defer func() { + p.Lexer = lexer + }() + + if !p.Token.IsKeywordLike("MATCH") { + return false + } + p.parseGQLMatchStatement() + + return p.Token.Kind == "}" +} + +func (p *Parser) parseGQLExistsExpr() ast.GQLExistsExpr { + if p.lookaheadGQLExistsExprMatch() { + return p.parseGQLMatchStatement() + } else if p.lookaheadGQLGraphPattern() { + return p.parseGQLGraphPattern() + } else { + return p.parseGQLQueryExpr() + } +} + +// parseExistsSubQuery parses EXISTS subquery or EXISTS GQL subquery +func (p *Parser) parseExistsSubQuery() ast.Expr { exists := p.expect("EXISTS").Pos + if p.Token.Kind == "{" { + p.expect("{") + expr := p.parseGQLExistsExpr() + rbrace := p.expect("}").Pos + return &ast.ExistsGQLSubQuery{ + Exists: exists, + Rbrace: rbrace, + Query: expr, + } + } + p.expect("(") query := p.parseQueryExpr() rparen := p.expect(")").Pos @@ -2342,6 +2492,17 @@ func (p *Parser) parseArrayLiteralOrSubQuery() ast.Expr { } } + if p.Token.Kind == "{" { + p.nextToken() + query := p.parseGQLQueryExpr() + rbrace := p.expect("}").Pos + return &ast.ArrayGQLSubQuery{ + Array: pos, + Rbrace: rbrace, + Query: query, + } + } + var t ast.Type if p.Token.Kind == "<" { p.nextToken() @@ -5428,6 +5589,869 @@ func (p *Parser) parseStringValue() ast.StringValue { panic(p.errorfAtToken(&p.Token, "expected token: , , but: %s", p.Token.Kind)) } +// ================================================================================ +// +// GRAPH +// +// ================================================================================ + +func (p *Parser) parseGQLQuery() *ast.GQLGraphQuery { + hint := p.tryParseHint() + return p.parseGQLQueryInternal(hint) +} + +func (p *Parser) parseGQLQueryInternal(hint *ast.Hint) *ast.GQLGraphQuery { + graphClause := p.parseGQLGraphClause() + multiQueryStatement := p.parseGQLMultiLinearQueryStatement() + + return &ast.GQLGraphQuery{Hint: hint, GraphClause: graphClause, MultiLinearQueryStatement: multiQueryStatement} +} + +func (p *Parser) parseGQLSubQueryInCondition() *ast.GQLSubQueryInCondition { + lbrace := p.expect("{").Pos + query := p.parseGQLQueryExpr() + rbrace := p.expect("}").Pos + + return &ast.GQLSubQueryInCondition{Lbrace: lbrace, Rbrace: rbrace, Query: query} +} +func (p *Parser) parseGQLQueryExpr() *ast.GQLQueryExpr { + var graphClause *ast.GQLGraphClause + if p.Token.IsKeywordLike("GRAPH") { + graphClause = p.parseGQLGraphClause() + } + multiQueryStatement := p.parseGQLMultiLinearQueryStatement() + + return &ast.GQLQueryExpr{GraphClause: graphClause, MultiLinearQueryStatement: multiQueryStatement} +} + +func (p *Parser) parseGQLGraphClause() *ast.GQLGraphClause { + graphPos := p.expectKeywordLike("GRAPH").Pos + graphName := p.parseIdent() + return &ast.GQLGraphClause{ + Graph: graphPos, + PropertyGraphName: graphName, + } +} + +func (p *Parser) parseGQLLinearQueryStatement() ast.GQLLinearQueryStatement { + stmt := p.parseGQLSimpleLinearQueryStatement() + if p.Token.Kind != ("UNION") && p.Token.Kind != ("EXCEPT") && p.Token.Kind != ("DISTINCT") { + return stmt + } + var tail []*ast.GQLSimpleLinearQueryStatementWithSetOperator + for p.Token.Kind == ("UNION") || p.Token.Kind == ("EXCEPT") || p.Token.Kind == ("DISTINCT") { + tail = append(tail, p.parseGQLSimpleLinearQueryStatementWithSetOperator()) + } + return &ast.GQLCompositeLinearQueryStatement{HeadSimpleLinearQueryStatement: stmt, TailSimpleLinearQueryStatementList: tail} +} + +func (p *Parser) parseGQLSimpleLinearQueryStatementWithSetOperator() *ast.GQLSimpleLinearQueryStatementWithSetOperator { + pos := p.Token.Pos + var op ast.GQLSetOperatorEnum + switch p.Token.Kind { + case "UNION": + p.nextToken() + op = ast.GQLSetOperatorUnion + case "INTERSECT": + p.nextToken() + op = ast.GQLSetOperatorIntersect + case "EXCEPT": + p.nextToken() + op = ast.GQLSetOperatorExcept + default: + panic("unknown SetOperatorEnum") + } + + allOrDistinct := p.parseAllOrDistinct() + stmt := p.parseGQLSimpleLinearQueryStatement() + + return &ast.GQLSimpleLinearQueryStatementWithSetOperator{ + StartPos: pos, + SetOperator: op, + AllOrDistinct: allOrDistinct, + Statement: stmt, + } +} +func (p *Parser) parseGQLSimpleLinearQueryStatement() *ast.GQLSimpleLinearQueryStatement { + var stmts []ast.GQLPrimitiveQueryStatement + for stmt := p.tryParseGQLPrimitiveQueryStatement(); stmt != nil; stmt = p.tryParseGQLPrimitiveQueryStatement() { + stmts = append(stmts, stmt) + } + + if len(stmts) == 0 { + p.panicfAtToken(&p.Token, "expect one or more GQL statements, but: %v", p.Token.Kind) + } + + if _, ok := stmts[len(stmts)-1].(*ast.GQLReturnStatement); !ok { + p.panicf("the last GQL statement of simple_linear_query_statement must be a RETURN statement") + } + + for _, stmt := range stmts[:len(stmts)-1] { + if _, ok := stmt.(*ast.GQLReturnStatement); ok { + p.panicf("multiple RETURN statements are not permitted in simple_linear_query_statement") + } + } + + return &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: stmts, + } +} + +func (p *Parser) tryParseGQLWithOffsetClause() *ast.GQLWithOffsetClause { + if p.Token.Kind != "WITH" { + return nil + } + with := p.expect("WITH").Pos + offset := p.expectKeywordLike("OFFSET").Pos + offsetName := p.tryParseAsAlias(withRequiredAs) + + return &ast.GQLWithOffsetClause{ + With: with, + Offset: offset, + OffsetName: offsetName, + } +} + +func (p *Parser) parseGQLForStatement() *ast.GQLForStatement { + forPos := p.expect("FOR").Pos + name := p.parseIdent() + p.expect("IN") + expr := p.parseExpr() + + withOffset := p.tryParseGQLWithOffsetClause() + + return &ast.GQLForStatement{ + For: forPos, + ElementName: name, + ArrayExpression: expr, + WithOffsetClause: withOffset, + } +} + +func (p *Parser) tryParseGQLOrderBySpecificationList() []*ast.GQLOrderBySpecification { + var list []*ast.GQLOrderBySpecification + for { + expr := p.parseExpr() + + var collateSpec *ast.GQLCollationSpecification + if p.Token.Kind == "COLLATE" { + collatePos := p.expect("COLLATE").Pos + spec := p.parseStringValue() + collateSpec = &ast.GQLCollationSpecification{ + Collate: collatePos, + Specification: spec, + } + } + + var direction ast.GQLDirectionEnum + directionPos := p.Token.Pos + switch { + case p.Token.Kind == "ASC": + p.nextToken() + direction = ast.GQLSortOrderAsc + case p.Token.Kind == "DESC": + p.nextToken() + direction = ast.GQLSortOrderDesc + case p.Token.Kind == "ASCENDING": + p.nextToken() + direction = ast.GQLSortOrderAscending + case p.Token.Kind == "DESCENDING": + p.nextToken() + direction = ast.GQLSortOrderDescending + default: + directionPos = token.InvalidPos + } + list = append(list, &ast.GQLOrderBySpecification{ + Expr: expr, + DirectionPos: directionPos, + CollationSpecification: collateSpec, + Direction: direction, + }) + if p.Token.Kind != "," { + break + } + } + return list +} + +func (p *Parser) parseGQLOrderByStatement() *ast.GQLOrderByStatement { + order := p.expect("ORDER").Pos + p.expect("BY") + spec := p.tryParseGQLOrderBySpecificationList() + if spec == nil { + p.panicfAtToken(&p.Token, "expect at least one order_by_specification") + } + + return &ast.GQLOrderByStatement{ + Order: order, + OrderBySpecificationList: spec, + } +} + +func (p *Parser) parseGQLLimitStatement() *ast.GQLLimitStatement { + limit := p.expect("LIMIT").Pos + expr := p.parseIntValue() + + return &ast.GQLLimitStatement{ + Limit: limit, + Count: expr, + } +} + +func (p *Parser) parseGQLFilterStatement() *ast.GQLFilterStatement { + filter := p.expectKeywordLike("FILTER").Pos + where := token.InvalidPos + if p.Token.Kind == "WHERE" { + where = p.expect("WHERE").Pos + } + expr := p.parseExpr() + + return &ast.GQLFilterStatement{ + Filter: filter, + Where: where, + Expr: expr, + } +} + +func (p *Parser) parseGQLOffsetStatement() *ast.GQLOffsetStatement { + var pos token.Pos + var isSkip bool + switch { + case p.Token.IsKeywordLike("OFFSET"): + pos = p.expectKeywordLike("OFFSET").Pos + case p.Token.IsKeywordLike("SKIP"): + pos = p.expectKeywordLike("SKIP").Pos + isSkip = true + default: + p.panicfAtToken(&p.Token, `expected "OFFSET" or "SKIP", but: %v`, p.Token.Kind) + } + + count := p.parseIntValue() + + return &ast.GQLOffsetStatement{ + Offset: pos, + IsSkip: isSkip, + Count: count, + } +} + +func (p *Parser) parseGQLReturnItem() *ast.GQLReturnItem { + if p.Token.Kind == "*" { + pos := p.expect("*").Pos + return &ast.GQLReturnItem{ + Item: &ast.Star{ + Star: pos, + }, + } + } + + expr := p.parseExpr() + as := p.tryParseAsAlias(withRequiredAs) + if as != nil { + return &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: expr, + As: as, + }, + } + } + return &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: expr, + }, + } + +} + +func (p *Parser) tryParseGQLPrimitiveQueryStatement() ast.GQLPrimitiveQueryStatement { + switch { + case p.Token.IsKeywordLike("RETURN"): + return p.parseGQLReturnStatement() + case p.Token.IsKeywordLike("LET"): + return p.parseGQLLetStatement() + case p.Token.IsKeywordLike("FILTER"): + return p.parseGQLFilterStatement() + case p.Token.Kind == "ORDER": + return p.parseGQLOrderByStatement() + case p.Token.Kind == "LIMIT": + return p.parseGQLLimitStatement() + case p.Token.IsKeywordLike("SKIP"), p.Token.IsKeywordLike("OFFSET"): + return p.parseGQLOffsetStatement() + case p.Token.Kind == "FOR": + return p.parseGQLForStatement() + case p.Token.IsKeywordLike("OPTIONAL") || p.Token.IsKeywordLike("MATCH"): + return p.parseGQLMatchStatement() + case p.Token.Kind == "WITH": + return p.parseGQLWithStatement() + default: + return nil + } +} + +func (p *Parser) tryParseGQLPathModePathOrPaths() *ast.Ident { + switch { + case p.Token.IsKeywordLike("PATH"), p.Token.IsKeywordLike("PATHS"): + return p.parseIdent() + default: + return nil + } +} + +func (p *Parser) tryParseGQLPathMode() *ast.GQLPathMode { + var pathModeToken *ast.Ident + var mode ast.GQLPathModeEnum + switch { + case p.Token.IsKeywordLike("WALK"): + pathModeToken = p.parseIdent() + mode = ast.GQLPathModeWalk + case p.Token.IsKeywordLike("TRAIL"): + pathModeToken = p.parseIdent() + mode = ast.GQLPathModeTrail + case p.Token.IsKeywordLike("ACYCLIC"): + pathModeToken = p.parseIdent() + mode = ast.GQLPathModeAcyclic + default: + return nil + } + + pathOrPathsToken := p.tryParseGQLPathModePathOrPaths() + return &ast.GQLPathMode{ + ModeToken: pathModeToken, + PathOrPathsToken: pathOrPathsToken, + Mode: mode, + } +} + +func (p *Parser) tryParseGQLPathSearchPrefixOrPathMode() ast.GQLPathSearchPrefixOrPathMode { + startPos := p.Token.Pos + switch { + case p.Token.Kind == "ALL": + end := p.expect("ALL").End + return &ast.GQLPathSearchPrefix{ + StartPos: startPos, + LastEnd: end, + SearchPrefix: ast.GQLPathSearchPrefixAll, + } + case p.Token.Kind == "ANY": + anyEnd := p.expect("ANY").End + if p.Token.IsKeywordLike("SHORTEST") { + shortestEnd := p.expectKeywordLike("SHORTEST").End + return &ast.GQLPathSearchPrefix{ + StartPos: startPos, + LastEnd: shortestEnd, + SearchPrefix: ast.GQLPathSearchPrefixAnyShortest, + } + } else { + return &ast.GQLPathSearchPrefix{ + StartPos: startPos, + LastEnd: anyEnd, + SearchPrefix: ast.GQLPathSearchPrefixAny, + } + } + default: + if pathMode := p.tryParseGQLPathMode(); pathMode != nil { + return pathMode + } + return nil + } + +} + +func (p *Parser) parseGQLMatchStatement() *ast.GQLMatchStatement { + var optionalPos token.Pos + if p.Token.IsKeywordLike("OPTIONAL") { + optionalPos = p.Token.Pos + p.nextToken() + } else { + optionalPos = token.InvalidPos + } + + match := p.expectKeywordLike("MATCH").Pos + hint := p.tryParseHint() + var prefixOrMode = p.tryParseGQLPathSearchPrefixOrPathMode() + pattern := p.parseGQLGraphPattern() + return &ast.GQLMatchStatement{ + Optional: optionalPos, + Match: match, + MatchHint: hint, + PrefixOrMode: prefixOrMode, + GraphPattern: pattern, + } +} + +func (p *Parser) parseGQLLabelPrimary() ast.GQLLabelPrimary { + pos := p.Token.Pos + + if p.Token.Kind == "%" { + p.nextToken() + return &ast.GQLWildcardLabel{ + Percent: pos, + } + } + + ident := p.parseIdent() + return &ast.GQLElementLabel{ + LabelName: ident, + } +} + +func (p *Parser) parseGQLLabelExpressionBinary() ast.GQLLabelExpression { + labelTerm := p.parseLabelUnary() + + switch { + case p.Token.Kind == "|": + p.nextToken() + right := p.parseGQLLabelExpressionBinary() + return &ast.GQLLabelOrExpression{ + Left: labelTerm, + Right: right, + } + case p.Token.Kind == "&": + p.nextToken() + right := p.parseGQLLabelExpressionBinary() + return &ast.GQLLabelAndExpression{ + Left: labelTerm, + Right: right, + } + default: + return labelTerm + } +} + +func (p *Parser) parseLabelUnary() ast.GQLLabelExpression { + switch { + case p.Token.Kind == "!": + notPos := p.expect("!").Pos + expr := p.parseGQLLabelExpressionBinary() + return &ast.GQLLabelNotExpression{Not: notPos, LabelExpression: expr} + case p.Token.Kind == "(": + lparen := p.expect("(").Pos + expr := p.parseGQLLabelExpressionBinary() + rparen := p.expect(")").Pos + return &ast.GQLLabelParenExpression{ + Lparen: lparen, + Rparen: rparen, + LabelExpr: expr, + } + case p.Token.Kind == token.TokenIdent || p.Token.Kind == "%": + return p.parseGQLLabelPrimary() + default: + panic(p.errorfAtToken(&p.Token, `expected token: ",", "(", or "", but: %v`, p.Token.Kind)) + } +} + +func (p *Parser) parseGQLIsLabelCondition() *ast.GQLIsLabelCondition { + + pos := p.Token.Pos + p.nextToken() + labelExpr := p.parseGQLLabelExpressionBinary() + return &ast.GQLIsLabelCondition{ + IsOrColon: pos, + LabelExpression: labelExpr, + } +} +func (p *Parser) parseGQLPatternFiller() *ast.GQLPatternFiller { + hint := p.tryParseHint() + var patternVar *ast.Ident + if p.Token.Kind == token.TokenIdent { + patternVar = p.parseIdent() + } + + var isLabelCondition *ast.GQLIsLabelCondition + if p.Token.Kind == ":" || p.Token.Kind == "IS" { + isLabelCondition = p.parseGQLIsLabelCondition() + } + + filter := p.tryParseGQLPatternFillerFilter() + return &ast.GQLPatternFiller{ + Hint: hint, + GraphPatternVariable: patternVar, + IsLabelCondition: isLabelCondition, + Filter: filter, + } +} + +func (p *Parser) lookaheadGQLSubpathPattern() bool { + lexer := p.Lexer.Clone() + defer func() { + p.Lexer = lexer + }() + + if p.Token.Kind != "(" { + return false + } + p.nextToken() + if p.Token.Kind == "(" || p.Token.Kind == "-" || p.Token.Kind == "<" || p.Token.IsKeywordLike("WALK") || p.Token.IsKeywordLike("TRAIL") { + return true + } + return false +} + +func (p *Parser) parseGQLNodePattern() *ast.GQLNodePattern { + lparen := p.expect("(").Pos + filter := p.parseGQLPatternFiller() + rparen := p.expect(")").Pos + return &ast.GQLNodePattern{ + Lparen: lparen, + Rparen: rparen, + PatternFiller: filter, + } +} + +func (p *Parser) tryParseGQLQuantifier() ast.GQLQuantifier { + if p.Token.Kind != "{" { + return nil + } + + lbrace := p.expect("{").Pos + if p.Token.Kind == "," { + p.nextToken() + upperBound := p.parseIntValue() + rbrace := p.expect("}").Pos + + return &ast.GQLBoundedQuantifier{ + Lbrace: lbrace, + Rbrace: rbrace, + UpperBound: upperBound, + } + } + bound := p.parseIntValue() + if p.Token.Kind != "," { + rbrace := p.expect("}").Pos + return &ast.GQLFixedQuantifier{ + Lbrace: lbrace, + Rbrace: rbrace, + Bound: bound, + } + } + p.expect(",") + upperBound := p.parseIntValue() + rbrace := p.expect("}").Pos + + return &ast.GQLBoundedQuantifier{ + Lbrace: lbrace, + Rbrace: rbrace, + LowerBound: bound, + UpperBound: upperBound, + } +} + +func (p *Parser) tryParseGQLQuantifiablePathTerm() *ast.GQLQuantifiablePathTerm { + hint := p.tryParseHint() + pt := p.tryParseGQLPathTerm() + if pt == nil { + return nil + } + q := p.tryParseGQLQuantifier() + return &ast.GQLQuantifiablePathTerm{ + Hint: hint, + PathTerm: pt, + Quantifier: q, + } +} + +func (p *Parser) tryParseGQLPathTerm() ast.GQLPathTerm { + // TODO + /* + path_term: + { + element_pattern + | subpath_pattern + } + */ + + if p.lookaheadGQLSubpathPattern() { + return p.parseGQLSubPathPattern() + } + if p.Token.Kind == "(" { + return p.parseGQLNodePattern() + } + if p.Token.Kind == "-" || p.Token.Kind == "<" || p.Token.Kind == "->" { + return p.parseGQLEdgePattern() + } + return nil +} + +func (p *Parser) parseGQLSubPathPattern() *ast.GQLSubpathPattern { + lparen := p.expect("(").Pos + pathMode := p.tryParseGQLPathMode() + pattern := p.parseGQLPathPattern() + where := p.tryParseWhere() + rparen := p.expect(")").Pos + + return &ast.GQLSubpathPattern{ + Lparen: lparen, + Rparen: rparen, + PathMode: pathMode, + PathPattern: pattern, + WhereClause: where, + } +} + +// parseGQLFullEdgeBody parses [pattern_fillter]. +// If pattern_filler is empty, second return value is nil. +func (p *Parser) parseGQLFullEdgeBody() (lbrack token.Pos, filler *ast.GQLPatternFiller, rbrack token.Pos) { + lbrack = p.expect("[").Pos + if p.Token.Kind != "]" { + filler = p.parseGQLPatternFiller() + } + rbrack = p.expect("]").Pos + + return lbrack, filler, rbrack +} + +func (p *Parser) parseGQLEdgePattern() ast.GQLEdgePattern { + firstPos := p.Token.Pos + switch p.Token.Kind { + case "<": + p.nextToken() + firstHyphenPos := p.expect("-").Pos + if p.Token.Kind != "[" { + return &ast.GQLAbbreviatedEdgeLeft{ + Lt: firstPos, + Hyphen: firstHyphenPos, + } + } + + lbrack, filler, rbrack := p.parseGQLFullEdgeBody() + lastPos := p.expect("-").Pos + + return &ast.GQLFullEdgeLeft{ + Lt: firstPos, + Lbrack: lbrack, + Rbrack: rbrack, + Hyphen: lastPos, + PatternFiller: filler, + } + case "->": + lastPos := p.expect("->").Pos + + return &ast.GQLAbbreviatedEdgeRight{ + Hyphen: firstPos, + Arrow: lastPos, + } + case "-": + p.nextToken() + switch p.Token.Kind { + case "[": + lbrack, filler, rbrack := p.parseGQLFullEdgeBody() + + switch p.Token.Kind { + case "->": + lastPos := p.Token.Pos + p.nextToken() + return &ast.GQLFullEdgeRight{ + Hyphen: firstPos, + Lbrack: lbrack, + Rbrack: rbrack, + Arrow: lastPos, + PatternFiller: filler, + } + case "-": + lastPos := p.expect("-").Pos + return &ast.GQLFullEdgeAny{ + FirstHyphen: firstPos, + Lbrack: lbrack, + Rbrack: rbrack, + LastHyphen: lastPos, + PatternFiller: filler, + } + default: + panic(p.errorfAtToken(&p.Token, `expected "-", "->", but: %v`, p.Token.AsString)) + } + default: + return &ast.GQLAbbreviatedEdgeAny{Hyphen: firstPos} + } + default: + panic(fmt.Sprintf("not implemented kind: %v %v", p.Token.Kind, p.Token.Raw)) + } +} + +func (p *Parser) parseGQLPathPattern() *ast.GQLPathPattern { + list := []*ast.GQLQuantifiablePathTerm{p.tryParseGQLQuantifiablePathTerm()} + for p.Token.Kind != ")" && !p.Token.IsKeywordLike("WHERE") { + term := p.tryParseGQLQuantifiablePathTerm() + if term == nil { + break + } + list = append(list, term) + } + return &ast.GQLPathPattern{ + PathTermList: list, + } +} + +func (p *Parser) lookaheadGQLPathVariable() bool { + lexer := p.Lexer.Clone() + defer func() { + p.Lexer = lexer + }() + + if p.Token.Kind != token.TokenIdent { + return false + } + p.nextToken() + + return p.Token.Kind == "=" +} + +func (p *Parser) parseGQLTopLevelPathPattern() *ast.GQLTopLevelPathPattern { + var varName *ast.Ident + if p.lookaheadGQLPathVariable() { + varName = p.parseIdent() + p.expect("=") + } + + pathMode := p.tryParseGQLPathSearchPrefixOrPathMode() + pattern := p.parseGQLPathPattern() + return &ast.GQLTopLevelPathPattern{ + Var: varName, + PathSearchPrefixOrPathMode: pathMode, + PathPattern: pattern, + } +} + +func (p *Parser) parseGQLElementProperty() *ast.GQLElementProperty { + ident := p.parseIdent() + _ = p.expect(":") + expr := p.parseExpr() + return &ast.GQLElementProperty{ + ElementPropertyName: ident, + ElementPropertyValue: expr, + } +} +func (p *Parser) parseGQLPropertyFilters() *ast.GQLPropertyFilters { + lbrace := p.expect("{").Pos + + elementPropertyList := []*ast.GQLElementProperty{p.parseGQLElementProperty()} + for p.Token.Kind == "," { + p.nextToken() + elementPropertyList = append(elementPropertyList, p.parseGQLElementProperty()) + } + + rbrace := p.expect("}").Pos + return &ast.GQLPropertyFilters{ + Lbrace: lbrace, + PropertyFilterElemList: elementPropertyList, + Rbrace: rbrace, + } +} + +func (p *Parser) tryParseGQLPatternFillerFilter() ast.GQLPatternFillerFilter { + switch { + case p.Token.Kind == "WHERE": + return p.parseWhere() + case p.Token.Kind == "{": + return p.parseGQLPropertyFilters() + default: + return nil + } +} + +func (p *Parser) parseGQLGraphPattern() *ast.GQLGraphPattern { + patterns := []*ast.GQLTopLevelPathPattern{p.parseGQLTopLevelPathPattern()} + for p.Token.Kind == "," { + p.nextToken() + patterns = append(patterns, p.parseGQLTopLevelPathPattern()) + } + return &ast.GQLGraphPattern{ + PathPatternList: patterns, + WhereClause: p.tryParseWhere(), // TODO + } +} + +/* +*** +GQL statements +*** +*/ + +/* +*** +GQL RETURN statement +*** +*/ +func (p *Parser) parseGQLReturnStatement() *ast.GQLReturnStatement { + ret := p.expectKeywordLike("RETURN").Pos + + allOrDistinct := p.tryParseAllOrDistinct() + returnItems := p.parseGQLReturnItemList() + groupBy := p.tryParseGroupBy() + orderBy := p.tryParseOrderBy() + offset := p.tryParseOffset() + limit := p.tryParseLimit() + + // TODO: refactor + var limitAndOffset ast.GQLLimitAndOffsetClause + switch { + case limit != nil && offset != nil: + limitAndOffset = &ast.GQLLimitWithOffsetClause{ + Offset: offset, + Limit: limit, + } + case limit != nil: + limitAndOffset = &ast.GQLLimitClause{Limit: limit} + case offset != nil: + limitAndOffset = &ast.GQLOffsetClause{Offset: offset} + default: + } + return &ast.GQLReturnStatement{ + Return: ret, + AllOrDistinct: allOrDistinct, + GroupByClause: groupBy, + OrderByClause: orderBy, + LimitAndOffsetClause: limitAndOffset, + ReturnItemList: returnItems, + } +} + +func (p *Parser) parseGQLReturnItemList() []*ast.GQLReturnItem { + return parseCommaSeparatedList(p, p.parseGQLReturnItem) +} + +func (p *Parser) parseGQLWithStatement() *ast.GQLWithStatement { + with := p.expect("WITH").Pos + allOrDistinct := p.tryParseAllOrDistinct() + returnItems := p.parseGQLReturnItemList() + groupBy := p.tryParseGroupBy() + + return &ast.GQLWithStatement{ + With: with, + AllOrDistinct: allOrDistinct, + GroupByClause: groupBy, + ReturnItemList: returnItems} +} + +func (p *Parser) parseGQLLinearGraphVariable() *ast.GQLLinearGraphVariable { + name := p.parseIdent() + p.expect("=") + value := p.parseExpr() + + return &ast.GQLLinearGraphVariable{VariableName: name, Value: value} +} + +func (p *Parser) parseGQLLetStatement() *ast.GQLLetStatement { + let := p.expectKeywordLike("LET").Pos + vars := []*ast.GQLLinearGraphVariable{p.parseGQLLinearGraphVariable()} + + for p.Token.Kind == "," { + p.nextToken() + vars = append(vars, p.parseGQLLinearGraphVariable()) + } + + return &ast.GQLLetStatement{Let: let, LinearGraphVariableList: vars} +} + +func (p *Parser) parseGQLMultiLinearQueryStatement() *ast.GQLMultiLinearQueryStatement { + items := []ast.GQLLinearQueryStatement{p.parseGQLLinearQueryStatement()} + for p.Token.IsKeywordLike("NEXT") { + p.expectKeywordLike("NEXT") + items = append(items, p.parseGQLLinearQueryStatement()) + } + + return &ast.GQLMultiLinearQueryStatement{LinearQueryStatementList: items} +} + // ================================================================================ // // Error Handlers diff --git a/parser_test.go b/parser_test.go index b1c2b480..b94eed4b 100644 --- a/parser_test.go +++ b/parser_test.go @@ -251,6 +251,24 @@ func TestParseQuery(t *testing.T) { }) } +func TestParseGQLStatement(t *testing.T) { + inputPath := "./testdata/input/gql" + resultPath := "./testdata/result/gql" + + testParser(t, inputPath, resultPath, func(p *memefish.Parser) (ast.Node, error) { + return p.ParseGQLStatement() + }) +} + +func TestParseGQLPathPattern(t *testing.T) { + inputPath := "./testdata/input/gql_path_pattern" + resultPath := "./testdata/result/gql_path_pattern" + + testParser(t, inputPath, resultPath, func(p *memefish.Parser) (ast.Node, error) { + return p.ParseGQLPathPattern() + }) +} + func TestParseDDL(t *testing.T) { inputPath := "./testdata/input/ddl" resultPath := "./testdata/result/ddl" diff --git a/testdata/input/gql/filter.sql b/testdata/input/gql/filter.sql new file mode 100644 index 00000000..25401d31 --- /dev/null +++ b/testdata/input/gql/filter.sql @@ -0,0 +1,4 @@ +GRAPH FinGraph +MATCH (p:Person)-[o:Owns]->(a:Account) +FILTER p.birthday < '1990-01-10' +RETURN p.name \ No newline at end of file diff --git a/testdata/input/gql/filter_where.sql b/testdata/input/gql/filter_where.sql new file mode 100644 index 00000000..fba49536 --- /dev/null +++ b/testdata/input/gql/filter_where.sql @@ -0,0 +1,4 @@ +GRAPH FinGraph +MATCH (p:Person)-[o:Owns]->(a:Account) +FILTER WHERE p.birthday < '1990-01-10' +RETURN p.name \ No newline at end of file diff --git a/testdata/input/gql/for_with_offset.sql b/testdata/input/gql/for_with_offset.sql new file mode 100644 index 00000000..985c2d8a --- /dev/null +++ b/testdata/input/gql/for_with_offset.sql @@ -0,0 +1,5 @@ +GRAPH FinGraph +MATCH (p:Person)-[o:Owns]->(a:Account) +FOR element in ["all","some"] WITH OFFSET +RETURN p.name, element as alert_type, offset +ORDER BY p.name, element, offset diff --git a/testdata/input/gql/for_with_offset_as.sql b/testdata/input/gql/for_with_offset_as.sql new file mode 100644 index 00000000..2af7067d --- /dev/null +++ b/testdata/input/gql/for_with_offset_as.sql @@ -0,0 +1,4 @@ +GRAPH FinGraph +MATCH (p:Person) +FOR element in [] WITH OFFSET AS off +RETURN p.name, element, off \ No newline at end of file diff --git a/testdata/input/gql/graph_hints_between.sql b/testdata/input/gql/graph_hints_between.sql new file mode 100644 index 00000000..3ba24047 --- /dev/null +++ b/testdata/input/gql/graph_hints_between.sql @@ -0,0 +1,6 @@ +GRAPH FinGraph +MATCH + (p:Person {id: 1})-[e:Owns]-> + @{JOIN_METHOD=APPLY_JOIN} + ((a:Account)-[s:Transfers]->(oa:Account)) +RETURN oa.id \ No newline at end of file diff --git a/testdata/input/gql/graph_hints_match.sql b/testdata/input/gql/graph_hints_match.sql new file mode 100644 index 00000000..376c3d92 --- /dev/null +++ b/testdata/input/gql/graph_hints_match.sql @@ -0,0 +1,4 @@ +GRAPH FinGraph +MATCH (p:Person {id: 1})-[:Owns]->(a:Account) +MATCH @{JOIN_METHOD=APPLY_JOIN}(a:Account)-[e:Transfers]->(oa:Account) +RETURN oa.id \ No newline at end of file diff --git a/testdata/input/gql/graph_hints_traversal_hint.sql b/testdata/input/gql/graph_hints_traversal_hint.sql new file mode 100644 index 00000000..fd7a62eb --- /dev/null +++ b/testdata/input/gql/graph_hints_traversal_hint.sql @@ -0,0 +1,6 @@ +GRAPH FinGraph +MATCH + (p:Person {id: 1})-[:Owns]->(a:Account), -- path pattern 1 + @{JOIN_METHOD=HASH_JOIN, HASH_JOIN_BUILD_SIDE=BUILD_RIGHT} -- traversal hint + (a:Account)-[e:Transfers]->(c:Account) -- path pattern 2 +RETURN c.id \ No newline at end of file diff --git a/testdata/input/gql/let.sql b/testdata/input/gql/let.sql new file mode 100644 index 00000000..ab4931d9 --- /dev/null +++ b/testdata/input/gql/let.sql @@ -0,0 +1,4 @@ +GRAPH FinGraph +MATCH (source:Account)-[e:Transfers]->(destination:Account) +LET a = source +RETURN a.id AS a_id \ No newline at end of file diff --git a/testdata/input/gql/limit.sql b/testdata/input/gql/limit.sql new file mode 100644 index 00000000..69697528 --- /dev/null +++ b/testdata/input/gql/limit.sql @@ -0,0 +1,5 @@ +GRAPH FinGraph +MATCH (source:Account)-[e:Transfers]->(destination:Account) +ORDER BY source.nick_name +LIMIT 3 +RETURN source.nick_name \ No newline at end of file diff --git a/testdata/input/gql/match.sql b/testdata/input/gql/match.sql new file mode 100644 index 00000000..f790065c --- /dev/null +++ b/testdata/input/gql/match.sql @@ -0,0 +1,3 @@ +GRAPH FinGraph +MATCH (p:Person) +RETURN p.name, p.id \ No newline at end of file diff --git a/testdata/input/gql/match_any_shortest.sql b/testdata/input/gql/match_any_shortest.sql new file mode 100644 index 00000000..b21ee225 --- /dev/null +++ b/testdata/input/gql/match_any_shortest.sql @@ -0,0 +1,5 @@ +GRAPH FinGraph +MATCH ANY SHORTEST (a:Account)-[t:Transfers]->{1, 4} (b:Account) +WHERE a.is_blocked +LET total = SUM(t.amount) +RETURN a.id AS a_id, total, b.id AS b_id \ No newline at end of file diff --git a/testdata/input/gql/match_any_shortest_subpath_trail.sql b/testdata/input/gql/match_any_shortest_subpath_trail.sql new file mode 100644 index 00000000..ac557334 --- /dev/null +++ b/testdata/input/gql/match_any_shortest_subpath_trail.sql @@ -0,0 +1,3 @@ +GRAPH FinGraph +MATCH ANY SHORTEST (TRAIL ->{1,4}) +RETURN COUNT(1) as num_paths \ No newline at end of file diff --git a/testdata/input/gql/match_optional.sql b/testdata/input/gql/match_optional.sql new file mode 100644 index 00000000..4a5007e0 --- /dev/null +++ b/testdata/input/gql/match_optional.sql @@ -0,0 +1,4 @@ +GRAPH FinGraph +MATCH (n:Person) +OPTIONAL MATCH (n:Person)-[:Owns]->(a:Account {is_blocked: TRUE}) +RETURN n.name, a.id AS blocked_account_id \ No newline at end of file diff --git a/testdata/input/gql/match_or_expression.sql b/testdata/input/gql/match_or_expression.sql new file mode 100644 index 00000000..f5bfa49d --- /dev/null +++ b/testdata/input/gql/match_or_expression.sql @@ -0,0 +1,3 @@ +GRAPH FinGraph +MATCH (n:Person|Account) +RETURN n.id, n.name, n.nick_name \ No newline at end of file diff --git a/testdata/input/gql/match_subpath_trail.sql b/testdata/input/gql/match_subpath_trail.sql new file mode 100644 index 00000000..fa4972dd --- /dev/null +++ b/testdata/input/gql/match_subpath_trail.sql @@ -0,0 +1,5 @@ +GRAPH FinGraph +MATCH + (TRAIL (a1:Account)-[t1:Transfers]->{3}(a4:Account)) + -[t4:Transfers]->(a5:Account) +RETURN COUNT(1) as num_paths \ No newline at end of file diff --git a/testdata/input/gql/match_trail_subpath_walk.sql b/testdata/input/gql/match_trail_subpath_walk.sql new file mode 100644 index 00000000..92ee4af4 --- /dev/null +++ b/testdata/input/gql/match_trail_subpath_walk.sql @@ -0,0 +1,3 @@ +GRAPH FinGraph +MATCH TRAIL (WALK (a1:Account)-[t1:Transfers]->{4}(a5:Account)) +RETURN COUNT(1) as num_paths \ No newline at end of file diff --git a/testdata/input/gql/match_where_is_destination_of_return.sql b/testdata/input/gql/match_where_is_destination_of_return.sql new file mode 100644 index 00000000..0cd925de --- /dev/null +++ b/testdata/input/gql/match_where_is_destination_of_return.sql @@ -0,0 +1,4 @@ +GRAPH FinGraph +MATCH (a:Account)-[transfer:Transfers]-(b:Account) +WHERE a IS DESTINATION of transfer +RETURN a.id AS a_id, b.id AS b_id \ No newline at end of file diff --git a/testdata/input/gql/match_where_is_source_of_return.sql b/testdata/input/gql/match_where_is_source_of_return.sql new file mode 100644 index 00000000..cfb0510d --- /dev/null +++ b/testdata/input/gql/match_where_is_source_of_return.sql @@ -0,0 +1,4 @@ +GRAPH FinGraph +MATCH (a:Account)-[transfer:Transfers]-(b:Account) +WHERE a IS SOURCE of transfer +RETURN a.id AS a_id, b.id AS b_id \ No newline at end of file diff --git a/testdata/input/gql/match_where_not_same_return.sql b/testdata/input/gql/match_where_not_same_return.sql new file mode 100644 index 00000000..94f0efa0 --- /dev/null +++ b/testdata/input/gql/match_where_not_same_return.sql @@ -0,0 +1,4 @@ +GRAPH FinGraph +MATCH (src:Account)<-[transfer:Transfers]-(dest:Account) +WHERE NOT SAME(src, dest) +RETURN src.id AS source_id, dest.id AS destination_id \ No newline at end of file diff --git a/testdata/input/gql/match_where_property_exists_return.sql b/testdata/input/gql/match_where_property_exists_return.sql new file mode 100644 index 00000000..23930744 --- /dev/null +++ b/testdata/input/gql/match_where_property_exists_return.sql @@ -0,0 +1,3 @@ +GRAPH FinGraph +MATCH (n:Person|Account WHERE PROPERTY_EXISTS(n, name)) +RETURN n.name \ No newline at end of file diff --git a/testdata/input/gql/next.sql b/testdata/input/gql/next.sql new file mode 100644 index 00000000..94ce2ef0 --- /dev/null +++ b/testdata/input/gql/next.sql @@ -0,0 +1,18 @@ +GRAPH FinGraph +MATCH (:Account)-[:Transfers]->(account:Account) +RETURN account, COUNT(*) AS num_incoming_transfers +GROUP BY account + +NEXT + +MATCH (account:Account)<-[:Owns]-(owner:Person) +RETURN + account.id AS account_id, owner.name AS owner_name, + num_incoming_transfers + +NEXT + +FILTER num_incoming_transfers < 2 +RETURN account_id, owner_name +UNION ALL +RETURN "Bob" AS owner_name, 100 AS account_id \ No newline at end of file diff --git a/testdata/input/gql/next_1.sql b/testdata/input/gql/next_1.sql new file mode 100644 index 00000000..06dc5160 --- /dev/null +++ b/testdata/input/gql/next_1.sql @@ -0,0 +1,11 @@ +GRAPH FinGraph +MATCH (:Account)-[:Transfers]->(account:Account) +RETURN account, COUNT(*) AS num_incoming_transfers +GROUP BY account + +NEXT + +MATCH (account:Account)<-[:Owns]-(owner:Person) +RETURN + account.id AS account_id, owner.name AS owner_name, + num_incoming_transfers \ No newline at end of file diff --git a/testdata/input/gql/offset_1.sql b/testdata/input/gql/offset_1.sql new file mode 100644 index 00000000..030a421a --- /dev/null +++ b/testdata/input/gql/offset_1.sql @@ -0,0 +1,4 @@ +GRAPH FinGraph +MATCH (p:Person) +OFFSET 2 +RETURN p.name, p.id \ No newline at end of file diff --git a/testdata/input/gql/order_by.sql b/testdata/input/gql/order_by.sql new file mode 100644 index 00000000..beb2098b --- /dev/null +++ b/testdata/input/gql/order_by.sql @@ -0,0 +1,5 @@ +GRAPH FinGraph +MATCH (src_account:Account)-[transfer:Transfers]->(dst_account:Account) +ORDER BY transfer.amount DESC +LIMIT 3 +RETURN src_account.id AS account_id, transfer.amount AS transfer_amount \ No newline at end of file diff --git a/testdata/input/gql/order_by_collate.sql b/testdata/input/gql/order_by_collate.sql new file mode 100644 index 00000000..b288cdd5 --- /dev/null +++ b/testdata/input/gql/order_by_collate.sql @@ -0,0 +1 @@ +GRAPH FinGraph MATCH (a:Person) ORDER BY a.name COLLATE 'und:ci' RETURN a.name \ No newline at end of file diff --git a/testdata/input/gql/return.sql b/testdata/input/gql/return.sql new file mode 100644 index 00000000..2b4d6f4a --- /dev/null +++ b/testdata/input/gql/return.sql @@ -0,0 +1,10 @@ +GRAPH FinGraph +MATCH (:Account)-[:Transfers]->(account:Account) +RETURN account, COUNT(*) AS num_incoming_transfers +GROUP BY account + +NEXT + +MATCH (account:Account)<-[:Owns]-(owner:Person) +RETURN owner.name AS owner_name, num_incoming_transfers +ORDER BY num_incoming_transfers DESC \ No newline at end of file diff --git a/testdata/input/gql/return_array_subquery.sql b/testdata/input/gql/return_array_subquery.sql new file mode 100644 index 00000000..3b1ca3e1 --- /dev/null +++ b/testdata/input/gql/return_array_subquery.sql @@ -0,0 +1,9 @@ +GRAPH FinGraph +MATCH (p:Person)-[:Owns]->(account:Account) +RETURN + p.name, account.id AS account_id, + ARRAY { + MATCH (a:Account)-[transfer:Transfers]->(:Account) + WHERE a = account + RETURN transfer.amount AS transfers + } AS transfers \ No newline at end of file diff --git a/testdata/input/gql/return_distinct.sql b/testdata/input/gql/return_distinct.sql new file mode 100644 index 00000000..5a477ceb --- /dev/null +++ b/testdata/input/gql/return_distinct.sql @@ -0,0 +1,3 @@ +GRAPH FinGraph +MATCH (src:Account {id: 7})-[e:Transfers]->{1, 3}(dst:Account) +RETURN DISTINCT ARRAY_LENGTH(e) AS hops, dst.id AS destination_account_id \ No newline at end of file diff --git a/testdata/input/gql/return_exists_graph_pattern.sql b/testdata/input/gql/return_exists_graph_pattern.sql new file mode 100644 index 00000000..cfbb543d --- /dev/null +++ b/testdata/input/gql/return_exists_graph_pattern.sql @@ -0,0 +1,4 @@ +GRAPH FinGraph +RETURN EXISTS { + (p:Person)-[o:Owns]->(a:Account) WHERE p.Name LIKE 'D%' +} AS results \ No newline at end of file diff --git a/testdata/input/gql/return_exists_match.sql b/testdata/input/gql/return_exists_match.sql new file mode 100644 index 00000000..915c52a9 --- /dev/null +++ b/testdata/input/gql/return_exists_match.sql @@ -0,0 +1,5 @@ +GRAPH FinGraph +RETURN EXISTS { + MATCH (p:Person)-[o:Owns]->(a:Account) + WHERE p.Name LIKE 'D%' +} AS results \ No newline at end of file diff --git a/testdata/input/gql/return_exists_query_expr.sql b/testdata/input/gql/return_exists_query_expr.sql new file mode 100644 index 00000000..2af7f450 --- /dev/null +++ b/testdata/input/gql/return_exists_query_expr.sql @@ -0,0 +1,7 @@ +GRAPH FinGraph +RETURN EXISTS { + MATCH (p:Person)-[o:Owns]->(a:Account) + WHERE p.Name LIKE 'D%' + RETURN p.Name + LIMIT 1 +} AS results \ No newline at end of file diff --git a/testdata/input/gql/return_in_gql_subquery.sql b/testdata/input/gql/return_in_gql_subquery.sql new file mode 100644 index 00000000..8dc11778 --- /dev/null +++ b/testdata/input/gql/return_in_gql_subquery.sql @@ -0,0 +1,5 @@ +GRAPH FinGraph +RETURN 'Dana' IN { + MATCH (p:Person)-[o:Owns]->(a:Account) + RETURN p.name +} AS results \ No newline at end of file diff --git a/testdata/input/gql/return_offset_limit.sql b/testdata/input/gql/return_offset_limit.sql new file mode 100644 index 00000000..f1657fbd --- /dev/null +++ b/testdata/input/gql/return_offset_limit.sql @@ -0,0 +1,5 @@ +GRAPH FinGraph +MATCH (p:Person) +RETURN p.name, p.id +OFFSET 1 +LIMIT 1 \ No newline at end of file diff --git a/testdata/input/gql/return_value_subquery.sql b/testdata/input/gql/return_value_subquery.sql new file mode 100644 index 00000000..9992d440 --- /dev/null +++ b/testdata/input/gql/return_value_subquery.sql @@ -0,0 +1,7 @@ +GRAPH FinGraph +RETURN VALUE { + MATCH (p:Person) + WHERE p.name LIKE '%e%' + RETURN p.name + LIMIT 1 +} AS results \ No newline at end of file diff --git a/testdata/input/gql/skip_1.sql b/testdata/input/gql/skip_1.sql new file mode 100644 index 00000000..a5fb230d --- /dev/null +++ b/testdata/input/gql/skip_1.sql @@ -0,0 +1,4 @@ +GRAPH FinGraph +MATCH (p:Person) +SKIP 2 +RETURN p.name, p.id \ No newline at end of file diff --git a/testdata/input/gql/with_1.sql b/testdata/input/gql/with_1.sql new file mode 100644 index 00000000..cee54218 --- /dev/null +++ b/testdata/input/gql/with_1.sql @@ -0,0 +1,4 @@ +GRAPH FinGraph +MATCH (:Account)-[:Transfers]->(account:Account) +WITH account, COUNT(*) AS num_incoming_transfers GROUP BY account +RETURN account.id AS account_id, num_incoming_transfers \ No newline at end of file diff --git a/testdata/input/gql_path_pattern/abbr_edge_any.sql b/testdata/input/gql_path_pattern/abbr_edge_any.sql new file mode 100644 index 00000000..69db6bb6 --- /dev/null +++ b/testdata/input/gql_path_pattern/abbr_edge_any.sql @@ -0,0 +1 @@ + - \ No newline at end of file diff --git a/testdata/input/gql_path_pattern/abbreviated_edge_left.sql b/testdata/input/gql_path_pattern/abbreviated_edge_left.sql new file mode 100644 index 00000000..1c150fa8 --- /dev/null +++ b/testdata/input/gql_path_pattern/abbreviated_edge_left.sql @@ -0,0 +1 @@ + <- \ No newline at end of file diff --git a/testdata/input/gql_path_pattern/abbreviated_edge_right.sql b/testdata/input/gql_path_pattern/abbreviated_edge_right.sql new file mode 100644 index 00000000..d882b4a5 --- /dev/null +++ b/testdata/input/gql_path_pattern/abbreviated_edge_right.sql @@ -0,0 +1 @@ + -> \ No newline at end of file diff --git a/testdata/input/gql_path_pattern/full_edge_any.sql b/testdata/input/gql_path_pattern/full_edge_any.sql new file mode 100644 index 00000000..7b7fcf96 --- /dev/null +++ b/testdata/input/gql_path_pattern/full_edge_any.sql @@ -0,0 +1 @@ + -[a]- \ No newline at end of file diff --git a/testdata/input/gql_path_pattern/full_edge_left.sql b/testdata/input/gql_path_pattern/full_edge_left.sql new file mode 100644 index 00000000..0c9f97ed --- /dev/null +++ b/testdata/input/gql_path_pattern/full_edge_left.sql @@ -0,0 +1 @@ + <-[a]- \ No newline at end of file diff --git a/testdata/input/gql_path_pattern/full_edge_right.sql b/testdata/input/gql_path_pattern/full_edge_right.sql new file mode 100644 index 00000000..aae652dc --- /dev/null +++ b/testdata/input/gql_path_pattern/full_edge_right.sql @@ -0,0 +1 @@ + -[]-> \ No newline at end of file diff --git a/testdata/input/gql_path_pattern/node_pattern_unnamed.sql b/testdata/input/gql_path_pattern/node_pattern_unnamed.sql new file mode 100644 index 00000000..2ef69aa2 --- /dev/null +++ b/testdata/input/gql_path_pattern/node_pattern_unnamed.sql @@ -0,0 +1 @@ + () \ No newline at end of file diff --git a/testdata/input/gql_path_pattern/pattern_1.sql b/testdata/input/gql_path_pattern/pattern_1.sql new file mode 100644 index 00000000..5eec94ff --- /dev/null +++ b/testdata/input/gql_path_pattern/pattern_1.sql @@ -0,0 +1 @@ +(p:%) \ No newline at end of file diff --git a/testdata/input/gql_path_pattern/pattern_2.sql b/testdata/input/gql_path_pattern/pattern_2.sql new file mode 100644 index 00000000..8f2deb47 --- /dev/null +++ b/testdata/input/gql_path_pattern/pattern_2.sql @@ -0,0 +1 @@ +(p:(Singer|(!Writer&!Producer))) \ No newline at end of file diff --git a/testdata/input/gql_path_pattern/quantified_subpath_lower_default.sql b/testdata/input/gql_path_pattern/quantified_subpath_lower_default.sql new file mode 100644 index 00000000..b77e4b30 --- /dev/null +++ b/testdata/input/gql_path_pattern/quantified_subpath_lower_default.sql @@ -0,0 +1 @@ +(source_person:Person)((p:Person)-[k:Knows]->(f:Person)){, 4}(dest_person:Person) \ No newline at end of file diff --git a/testdata/input/query/select_from_graph_table.sql b/testdata/input/query/select_from_graph_table.sql new file mode 100644 index 00000000..04f0a5a4 --- /dev/null +++ b/testdata/input/query/select_from_graph_table.sql @@ -0,0 +1 @@ +SELECT * FROM GRAPH_TABLE(FinGraph RETURN 1 AS a) \ No newline at end of file diff --git a/testdata/input/query/select_in_gql_subquery.sql b/testdata/input/query/select_in_gql_subquery.sql new file mode 100644 index 00000000..d7f84916 --- /dev/null +++ b/testdata/input/query/select_in_gql_subquery.sql @@ -0,0 +1,5 @@ +SELECT 'Dana' IN { + GRAPH FinGraph + MATCH (p:Person)-[o:Owns]->(a:Account) + RETURN p.name +} AS results \ No newline at end of file diff --git a/testdata/result/gql/filter.sql.txt b/testdata/result/gql/filter.sql.txt new file mode 100644 index 00000000..3a0b1595 --- /dev/null +++ b/testdata/result/gql/filter.sql.txt @@ -0,0 +1,160 @@ +--- filter.sql +GRAPH FinGraph +MATCH (p:Person)-[o:Owns]->(a:Account) +FILTER p.birthday < '1990-01-10' +RETURN p.name +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "p", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 23, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 24, + NameEnd: 30, + Name: "Person", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 31, + Lbrack: 32, + Rbrack: 39, + Arrow: 40, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 33, + NameEnd: 34, + Name: "o", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 34, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 35, + NameEnd: 39, + Name: "Owns", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 42, + Rparen: 52, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 43, + NameEnd: 44, + Name: "a", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 44, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 45, + NameEnd: 52, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLFilterStatement{ + Filter: 54, + Where: -1, + Expr: &ast.BinaryExpr{ + Op: "<", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 61, + NameEnd: 62, + Name: "p", + }, + &ast.Ident{ + NamePos: 63, + NameEnd: 71, + Name: "birthday", + }, + }, + }, + Right: &ast.StringLiteral{ + ValuePos: 74, + ValueEnd: 86, + Value: "1990-01-10", + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 87, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 94, + NameEnd: 95, + Name: "p", + }, + &ast.Ident{ + NamePos: 96, + NameEnd: 100, + Name: "name", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (p:Person)-[o:Owns]->(a:Account) FILTER p.birthday < "1990-01-10" RETURN p.name diff --git a/testdata/result/gql/filter_where.sql.txt b/testdata/result/gql/filter_where.sql.txt new file mode 100644 index 00000000..e3760f64 --- /dev/null +++ b/testdata/result/gql/filter_where.sql.txt @@ -0,0 +1,160 @@ +--- filter_where.sql +GRAPH FinGraph +MATCH (p:Person)-[o:Owns]->(a:Account) +FILTER WHERE p.birthday < '1990-01-10' +RETURN p.name +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "p", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 23, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 24, + NameEnd: 30, + Name: "Person", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 31, + Lbrack: 32, + Rbrack: 39, + Arrow: 40, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 33, + NameEnd: 34, + Name: "o", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 34, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 35, + NameEnd: 39, + Name: "Owns", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 42, + Rparen: 52, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 43, + NameEnd: 44, + Name: "a", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 44, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 45, + NameEnd: 52, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLFilterStatement{ + Filter: 54, + Where: 61, + Expr: &ast.BinaryExpr{ + Op: "<", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 67, + NameEnd: 68, + Name: "p", + }, + &ast.Ident{ + NamePos: 69, + NameEnd: 77, + Name: "birthday", + }, + }, + }, + Right: &ast.StringLiteral{ + ValuePos: 80, + ValueEnd: 92, + Value: "1990-01-10", + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 93, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 100, + NameEnd: 101, + Name: "p", + }, + &ast.Ident{ + NamePos: 102, + NameEnd: 106, + Name: "name", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (p:Person)-[o:Owns]->(a:Account) FILTER WHERE p.birthday < "1990-01-10" RETURN p.name diff --git a/testdata/result/gql/for_with_offset.sql.txt b/testdata/result/gql/for_with_offset.sql.txt new file mode 100644 index 00000000..00bf9fb1 --- /dev/null +++ b/testdata/result/gql/for_with_offset.sql.txt @@ -0,0 +1,229 @@ +--- for_with_offset.sql +GRAPH FinGraph +MATCH (p:Person)-[o:Owns]->(a:Account) +FOR element in ["all","some"] WITH OFFSET +RETURN p.name, element as alert_type, offset +ORDER BY p.name, element, offset + +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "p", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 23, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 24, + NameEnd: 30, + Name: "Person", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 31, + Lbrack: 32, + Rbrack: 39, + Arrow: 40, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 33, + NameEnd: 34, + Name: "o", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 34, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 35, + NameEnd: 39, + Name: "Owns", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 42, + Rparen: 52, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 43, + NameEnd: 44, + Name: "a", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 44, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 45, + NameEnd: 52, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLForStatement{ + For: 54, + ElementName: &ast.Ident{ + NamePos: 58, + NameEnd: 65, + Name: "element", + }, + ArrayExpression: &ast.ArrayLiteral{ + Array: -1, + Lbrack: 69, + Rbrack: 82, + Values: []ast.Expr{ + &ast.StringLiteral{ + ValuePos: 70, + ValueEnd: 75, + Value: "all", + }, + &ast.StringLiteral{ + ValuePos: 76, + ValueEnd: 82, + Value: "some", + }, + }, + }, + WithOffsetClause: &ast.GQLWithOffsetClause{ + With: 84, + Offset: 89, + }, + }, + &ast.GQLReturnStatement{ + Return: 96, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 103, + NameEnd: 104, + Name: "p", + }, + &ast.Ident{ + NamePos: 105, + NameEnd: 109, + Name: "name", + }, + }, + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.Ident{ + NamePos: 111, + NameEnd: 118, + Name: "element", + }, + As: &ast.AsAlias{ + As: 119, + Alias: &ast.Ident{ + NamePos: 122, + NameEnd: 132, + Name: "alert_type", + }, + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Ident{ + NamePos: 134, + NameEnd: 140, + Name: "offset", + }, + }, + }, + }, + OrderByClause: &ast.OrderBy{ + Order: 141, + Items: []*ast.OrderByItem{ + &ast.OrderByItem{ + DirPos: -1, + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 150, + NameEnd: 151, + Name: "p", + }, + &ast.Ident{ + NamePos: 152, + NameEnd: 156, + Name: "name", + }, + }, + }, + }, + &ast.OrderByItem{ + DirPos: -1, + Expr: &ast.Ident{ + NamePos: 158, + NameEnd: 165, + Name: "element", + }, + }, + &ast.OrderByItem{ + DirPos: -1, + Expr: &ast.Ident{ + NamePos: 167, + NameEnd: 173, + Name: "offset", + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (p:Person)-[o:Owns]->(a:Account) FOR element IN ["all", "some"] WITH OFFSET RETURN p.name, element AS alert_type, offset ORDER BY p.name, element, offset diff --git a/testdata/result/gql/for_with_offset_as.sql.txt b/testdata/result/gql/for_with_offset_as.sql.txt new file mode 100644 index 00000000..16d22eed --- /dev/null +++ b/testdata/result/gql/for_with_offset_as.sql.txt @@ -0,0 +1,129 @@ +--- for_with_offset_as.sql +GRAPH FinGraph +MATCH (p:Person) +FOR element in [] WITH OFFSET AS off +RETURN p.name, element, off +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "p", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 23, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 24, + NameEnd: 30, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLForStatement{ + For: 32, + ElementName: &ast.Ident{ + NamePos: 36, + NameEnd: 43, + Name: "element", + }, + ArrayExpression: &ast.ArrayLiteral{ + Array: -1, + Lbrack: 47, + Rbrack: 48, + }, + WithOffsetClause: &ast.GQLWithOffsetClause{ + With: 50, + Offset: 55, + OffsetName: &ast.AsAlias{ + As: 62, + Alias: &ast.Ident{ + NamePos: 65, + NameEnd: 68, + Name: "off", + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 69, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 76, + NameEnd: 77, + Name: "p", + }, + &ast.Ident{ + NamePos: 78, + NameEnd: 82, + Name: "name", + }, + }, + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Ident{ + NamePos: 84, + NameEnd: 91, + Name: "element", + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Ident{ + NamePos: 93, + NameEnd: 96, + Name: "off", + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (p:Person) FOR element IN [] WITH OFFSET AS off RETURN p.name, element, off diff --git a/testdata/result/gql/graph_hints_between.sql.txt b/testdata/result/gql/graph_hints_between.sql.txt new file mode 100644 index 00000000..1b94fb2d --- /dev/null +++ b/testdata/result/gql/graph_hints_between.sql.txt @@ -0,0 +1,235 @@ +--- graph_hints_between.sql +GRAPH FinGraph +MATCH + (p:Person {id: 1})-[e:Owns]-> + @{JOIN_METHOD=APPLY_JOIN} + ((a:Account)-[s:Transfers]->(oa:Account)) +RETURN oa.id +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 23, + Rparen: 40, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 24, + NameEnd: 25, + Name: "p", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 25, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 26, + NameEnd: 32, + Name: "Person", + }, + }, + }, + Filter: &ast.GQLPropertyFilters{ + Lbrace: 33, + PropertyFilterElemList: []*ast.GQLElementProperty{ + &ast.GQLElementProperty{ + ElementPropertyName: &ast.Ident{ + NamePos: 34, + NameEnd: 36, + Name: "id", + }, + ElementPropertyValue: &ast.IntLiteral{ + ValuePos: 38, + ValueEnd: 39, + Base: 10, + Value: "1", + }, + }, + }, + Rbrace: 39, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 41, + Lbrack: 42, + Rbrack: 49, + Arrow: 50, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 43, + NameEnd: 44, + Name: "e", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 44, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 45, + NameEnd: 49, + Name: "Owns", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + Hint: &ast.Hint{ + Atmark: 55, + Rbrace: 79, + Records: []*ast.HintRecord{ + &ast.HintRecord{ + Key: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 57, + NameEnd: 68, + Name: "JOIN_METHOD", + }, + }, + }, + Value: &ast.Ident{ + NamePos: 69, + NameEnd: 79, + Name: "APPLY_JOIN", + }, + }, + }, + }, + PathTerm: &ast.GQLSubpathPattern{ + Lparen: 83, + Rparen: 123, + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 84, + Rparen: 94, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 85, + NameEnd: 86, + Name: "a", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 86, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 87, + NameEnd: 94, + Name: "Account", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 95, + Lbrack: 96, + Rbrack: 108, + Arrow: 109, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 97, + NameEnd: 98, + Name: "s", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 98, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 99, + NameEnd: 108, + Name: "Transfers", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 111, + Rparen: 122, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 112, + NameEnd: 114, + Name: "oa", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 114, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 115, + NameEnd: 122, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 125, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 132, + NameEnd: 134, + Name: "oa", + }, + &ast.Ident{ + NamePos: 135, + NameEnd: 137, + Name: "id", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (p:Person {id: 1})-[e:Owns]->@{JOIN_METHOD=APPLY_JOIN}((a:Account)-[s:Transfers]->(oa:Account)) RETURN oa.id diff --git a/testdata/result/gql/graph_hints_match.sql.txt b/testdata/result/gql/graph_hints_match.sql.txt new file mode 100644 index 00000000..2954b71c --- /dev/null +++ b/testdata/result/gql/graph_hints_match.sql.txt @@ -0,0 +1,255 @@ +--- graph_hints_match.sql +GRAPH FinGraph +MATCH (p:Person {id: 1})-[:Owns]->(a:Account) +MATCH @{JOIN_METHOD=APPLY_JOIN}(a:Account)-[e:Transfers]->(oa:Account) +RETURN oa.id +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 38, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "p", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 23, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 24, + NameEnd: 30, + Name: "Person", + }, + }, + }, + Filter: &ast.GQLPropertyFilters{ + Lbrace: 31, + PropertyFilterElemList: []*ast.GQLElementProperty{ + &ast.GQLElementProperty{ + ElementPropertyName: &ast.Ident{ + NamePos: 32, + NameEnd: 34, + Name: "id", + }, + ElementPropertyValue: &ast.IntLiteral{ + ValuePos: 36, + ValueEnd: 37, + Base: 10, + Value: "1", + }, + }, + }, + Rbrace: 37, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 39, + Lbrack: 40, + Rbrack: 46, + Arrow: 47, + PatternFiller: &ast.GQLPatternFiller{ + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 41, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 42, + NameEnd: 46, + Name: "Owns", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 49, + Rparen: 59, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 50, + NameEnd: 51, + Name: "a", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 51, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 52, + NameEnd: 59, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLMatchStatement{ + Optional: -1, + Match: 61, + MatchHint: &ast.Hint{ + Atmark: 67, + Rbrace: 91, + Records: []*ast.HintRecord{ + &ast.HintRecord{ + Key: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 69, + NameEnd: 80, + Name: "JOIN_METHOD", + }, + }, + }, + Value: &ast.Ident{ + NamePos: 81, + NameEnd: 91, + Name: "APPLY_JOIN", + }, + }, + }, + }, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 92, + Rparen: 102, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 93, + NameEnd: 94, + Name: "a", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 94, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 95, + NameEnd: 102, + Name: "Account", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 103, + Lbrack: 104, + Rbrack: 116, + Arrow: 117, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 105, + NameEnd: 106, + Name: "e", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 106, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 107, + NameEnd: 116, + Name: "Transfers", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 119, + Rparen: 130, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 120, + NameEnd: 122, + Name: "oa", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 122, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 123, + NameEnd: 130, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 132, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 139, + NameEnd: 141, + Name: "oa", + }, + &ast.Ident{ + NamePos: 142, + NameEnd: 144, + Name: "id", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (p:Person {id: 1})-[:Owns]->(a:Account) MATCH@{JOIN_METHOD=APPLY_JOIN} (a:Account)-[e:Transfers]->(oa:Account) RETURN oa.id diff --git a/testdata/result/gql/graph_hints_traversal_hint.sql.txt b/testdata/result/gql/graph_hints_traversal_hint.sql.txt new file mode 100644 index 00000000..f4c1ec2a --- /dev/null +++ b/testdata/result/gql/graph_hints_traversal_hint.sql.txt @@ -0,0 +1,265 @@ +--- graph_hints_traversal_hint.sql +GRAPH FinGraph +MATCH + (p:Person {id: 1})-[:Owns]->(a:Account), -- path pattern 1 + @{JOIN_METHOD=HASH_JOIN, HASH_JOIN_BUILD_SIDE=BUILD_RIGHT} -- traversal hint + (a:Account)-[e:Transfers]->(c:Account) -- path pattern 2 +RETURN c.id +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 23, + Rparen: 40, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 24, + NameEnd: 25, + Name: "p", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 25, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 26, + NameEnd: 32, + Name: "Person", + }, + }, + }, + Filter: &ast.GQLPropertyFilters{ + Lbrace: 33, + PropertyFilterElemList: []*ast.GQLElementProperty{ + &ast.GQLElementProperty{ + ElementPropertyName: &ast.Ident{ + NamePos: 34, + NameEnd: 36, + Name: "id", + }, + ElementPropertyValue: &ast.IntLiteral{ + ValuePos: 38, + ValueEnd: 39, + Base: 10, + Value: "1", + }, + }, + }, + Rbrace: 39, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 41, + Lbrack: 42, + Rbrack: 48, + Arrow: 49, + PatternFiller: &ast.GQLPatternFiller{ + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 43, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 44, + NameEnd: 48, + Name: "Owns", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 51, + Rparen: 61, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 52, + NameEnd: 53, + Name: "a", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 53, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 54, + NameEnd: 61, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + Hint: &ast.Hint{ + Atmark: 102, + Rbrace: 159, + Records: []*ast.HintRecord{ + &ast.HintRecord{ + Key: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 104, + NameEnd: 115, + Name: "JOIN_METHOD", + }, + }, + }, + Value: &ast.Ident{ + NamePos: 116, + NameEnd: 125, + Name: "HASH_JOIN", + }, + }, + &ast.HintRecord{ + Key: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 127, + NameEnd: 147, + Name: "HASH_JOIN_BUILD_SIDE", + }, + }, + }, + Value: &ast.Ident{ + NamePos: 148, + NameEnd: 159, + Name: "BUILD_RIGHT", + }, + }, + }, + }, + PathTerm: &ast.GQLNodePattern{ + Lparen: 181, + Rparen: 191, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 182, + NameEnd: 183, + Name: "a", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 183, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 184, + NameEnd: 191, + Name: "Account", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 192, + Lbrack: 193, + Rbrack: 205, + Arrow: 206, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 194, + NameEnd: 195, + Name: "e", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 195, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 196, + NameEnd: 205, + Name: "Transfers", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 208, + Rparen: 218, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 209, + NameEnd: 210, + Name: "c", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 210, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 211, + NameEnd: 218, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 258, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 265, + NameEnd: 266, + Name: "c", + }, + &ast.Ident{ + NamePos: 267, + NameEnd: 269, + Name: "id", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (p:Person {id: 1})-[:Owns]->(a:Account), @{JOIN_METHOD=HASH_JOIN, HASH_JOIN_BUILD_SIDE=BUILD_RIGHT}(a:Account)-[e:Transfers]->(c:Account) RETURN c.id diff --git a/testdata/result/gql/let.sql.txt b/testdata/result/gql/let.sql.txt new file mode 100644 index 00000000..cd7d35ce --- /dev/null +++ b/testdata/result/gql/let.sql.txt @@ -0,0 +1,159 @@ +--- let.sql +GRAPH FinGraph +MATCH (source:Account)-[e:Transfers]->(destination:Account) +LET a = source +RETURN a.id AS a_id +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 36, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 22, + NameEnd: 28, + Name: "source", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 28, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 29, + NameEnd: 36, + Name: "Account", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 37, + Lbrack: 38, + Rbrack: 50, + Arrow: 51, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 39, + NameEnd: 40, + Name: "e", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 40, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 41, + NameEnd: 50, + Name: "Transfers", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 53, + Rparen: 73, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 54, + NameEnd: 65, + Name: "destination", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 65, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 66, + NameEnd: 73, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLLetStatement{ + Let: 75, + LinearGraphVariableList: []*ast.GQLLinearGraphVariable{ + &ast.GQLLinearGraphVariable{ + VariableName: &ast.Ident{ + NamePos: 79, + NameEnd: 80, + Name: "a", + }, + Value: &ast.Ident{ + NamePos: 83, + NameEnd: 89, + Name: "source", + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 90, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 97, + NameEnd: 98, + Name: "a", + }, + &ast.Ident{ + NamePos: 99, + NameEnd: 101, + Name: "id", + }, + }, + }, + As: &ast.AsAlias{ + As: 102, + Alias: &ast.Ident{ + NamePos: 105, + NameEnd: 109, + Name: "a_id", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (source:Account)-[e:Transfers]->(destination:Account) LET a = source RETURN a.id AS a_id diff --git a/testdata/result/gql/limit.sql.txt b/testdata/result/gql/limit.sql.txt new file mode 100644 index 00000000..e7908020 --- /dev/null +++ b/testdata/result/gql/limit.sql.txt @@ -0,0 +1,166 @@ +--- limit.sql +GRAPH FinGraph +MATCH (source:Account)-[e:Transfers]->(destination:Account) +ORDER BY source.nick_name +LIMIT 3 +RETURN source.nick_name +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 36, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 22, + NameEnd: 28, + Name: "source", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 28, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 29, + NameEnd: 36, + Name: "Account", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 37, + Lbrack: 38, + Rbrack: 50, + Arrow: 51, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 39, + NameEnd: 40, + Name: "e", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 40, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 41, + NameEnd: 50, + Name: "Transfers", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 53, + Rparen: 73, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 54, + NameEnd: 65, + Name: "destination", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 65, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 66, + NameEnd: 73, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLOrderByStatement{ + Order: 75, + OrderBySpecificationList: []*ast.GQLOrderBySpecification{ + &ast.GQLOrderBySpecification{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 84, + NameEnd: 90, + Name: "source", + }, + &ast.Ident{ + NamePos: 91, + NameEnd: 100, + Name: "nick_name", + }, + }, + }, + DirectionPos: -1, + }, + }, + }, + &ast.GQLLimitStatement{ + Limit: 101, + Count: &ast.IntLiteral{ + ValuePos: 107, + ValueEnd: 108, + Base: 10, + Value: "3", + }, + }, + &ast.GQLReturnStatement{ + Return: 109, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 116, + NameEnd: 122, + Name: "source", + }, + &ast.Ident{ + NamePos: 123, + NameEnd: 132, + Name: "nick_name", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (source:Account)-[e:Transfers]->(destination:Account) ORDER BY source.nick_name LIMIT 3 RETURN source.nick_name diff --git a/testdata/result/gql/match.sql.txt b/testdata/result/gql/match.sql.txt new file mode 100644 index 00000000..614b8a46 --- /dev/null +++ b/testdata/result/gql/match.sql.txt @@ -0,0 +1,103 @@ +--- match.sql +GRAPH FinGraph +MATCH (p:Person) +RETURN p.name, p.id +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "p", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 23, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 24, + NameEnd: 30, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 32, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 39, + NameEnd: 40, + Name: "p", + }, + &ast.Ident{ + NamePos: 41, + NameEnd: 45, + Name: "name", + }, + }, + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 47, + NameEnd: 48, + Name: "p", + }, + &ast.Ident{ + NamePos: 49, + NameEnd: 51, + Name: "id", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (p:Person) RETURN p.name, p.id diff --git a/testdata/result/gql/match_any_shortest.sql.txt b/testdata/result/gql/match_any_shortest.sql.txt new file mode 100644 index 00000000..ee2c5d10 --- /dev/null +++ b/testdata/result/gql/match_any_shortest.sql.txt @@ -0,0 +1,258 @@ +--- match_any_shortest.sql +GRAPH FinGraph +MATCH ANY SHORTEST (a:Account)-[t:Transfers]->{1, 4} (b:Account) +WHERE a.is_blocked +LET total = SUM(t.amount) +RETURN a.id AS a_id, total, b.id AS b_id +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + PrefixOrMode: &ast.GQLPathSearchPrefix{ + StartPos: 21, + LastEnd: 33, + SearchPrefix: "ANY SHORTEST", + }, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 34, + Rparen: 44, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 35, + NameEnd: 36, + Name: "a", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 36, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 37, + NameEnd: 44, + Name: "Account", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 45, + Lbrack: 46, + Rbrack: 58, + Arrow: 59, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 47, + NameEnd: 48, + Name: "t", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 48, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 49, + NameEnd: 58, + Name: "Transfers", + }, + }, + }, + }, + }, + Quantifier: &ast.GQLBoundedQuantifier{ + Lbrace: 61, + Rbrace: 66, + LowerBound: &ast.IntLiteral{ + ValuePos: 62, + ValueEnd: 63, + Base: 10, + Value: "1", + }, + UpperBound: &ast.IntLiteral{ + ValuePos: 65, + ValueEnd: 66, + Base: 10, + Value: "4", + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 68, + Rparen: 78, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 69, + NameEnd: 70, + Name: "b", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 70, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 71, + NameEnd: 78, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + WhereClause: &ast.Where{ + Where: 80, + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 86, + NameEnd: 87, + Name: "a", + }, + &ast.Ident{ + NamePos: 88, + NameEnd: 98, + Name: "is_blocked", + }, + }, + }, + }, + }, + }, + &ast.GQLLetStatement{ + Let: 99, + LinearGraphVariableList: []*ast.GQLLinearGraphVariable{ + &ast.GQLLinearGraphVariable{ + VariableName: &ast.Ident{ + NamePos: 103, + NameEnd: 108, + Name: "total", + }, + Value: &ast.CallExpr{ + Rparen: 123, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 111, + NameEnd: 114, + Name: "SUM", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 115, + NameEnd: 116, + Name: "t", + }, + &ast.Ident{ + NamePos: 117, + NameEnd: 123, + Name: "amount", + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 125, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 132, + NameEnd: 133, + Name: "a", + }, + &ast.Ident{ + NamePos: 134, + NameEnd: 136, + Name: "id", + }, + }, + }, + As: &ast.AsAlias{ + As: 137, + Alias: &ast.Ident{ + NamePos: 140, + NameEnd: 144, + Name: "a_id", + }, + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Ident{ + NamePos: 146, + NameEnd: 151, + Name: "total", + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 153, + NameEnd: 154, + Name: "b", + }, + &ast.Ident{ + NamePos: 155, + NameEnd: 157, + Name: "id", + }, + }, + }, + As: &ast.AsAlias{ + As: 158, + Alias: &ast.Ident{ + NamePos: 161, + NameEnd: 165, + Name: "b_id", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH ANY SHORTEST (a:Account)-[t:Transfers]->{1,4}(b:Account) WHERE a.is_blocked LET total = SUM(t.amount) RETURN a.id AS a_id, total, b.id AS b_id diff --git a/testdata/result/gql/match_any_shortest_subpath_trail.sql.txt b/testdata/result/gql/match_any_shortest_subpath_trail.sql.txt new file mode 100644 index 00000000..f4d6d78d --- /dev/null +++ b/testdata/result/gql/match_any_shortest_subpath_trail.sql.txt @@ -0,0 +1,123 @@ +--- match_any_shortest_subpath_trail.sql +GRAPH FinGraph +MATCH ANY SHORTEST (TRAIL ->{1,4}) +RETURN COUNT(1) as num_paths +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + PrefixOrMode: &ast.GQLPathSearchPrefix{ + StartPos: 21, + LastEnd: 33, + SearchPrefix: "ANY SHORTEST", + }, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLSubpathPattern{ + Lparen: 34, + Rparen: 48, + PathMode: &ast.GQLPathMode{ + Mode: "TRAIL", + ModeToken: &ast.Ident{ + NamePos: 35, + NameEnd: 40, + Name: "TRAIL", + }, + }, + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLAbbreviatedEdgeRight{ + Hyphen: 41, + Arrow: 41, + }, + Quantifier: &ast.GQLBoundedQuantifier{ + Lbrace: 43, + Rbrace: 47, + LowerBound: &ast.IntLiteral{ + ValuePos: 44, + ValueEnd: 45, + Base: 10, + Value: "1", + }, + UpperBound: &ast.IntLiteral{ + ValuePos: 46, + ValueEnd: 47, + Base: 10, + Value: "4", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 50, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.CallExpr{ + Rparen: 64, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 57, + NameEnd: 62, + Name: "COUNT", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 63, + ValueEnd: 64, + Base: 10, + Value: "1", + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 66, + Alias: &ast.Ident{ + NamePos: 69, + NameEnd: 78, + Name: "num_paths", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH ANY SHORTEST (TRAIL ->{1,4}) RETURN COUNT(1) AS num_paths diff --git a/testdata/result/gql/match_optional.sql.txt b/testdata/result/gql/match_optional.sql.txt new file mode 100644 index 00000000..f9b3d2cd --- /dev/null +++ b/testdata/result/gql/match_optional.sql.txt @@ -0,0 +1,209 @@ +--- match_optional.sql +GRAPH FinGraph +MATCH (n:Person) +OPTIONAL MATCH (n:Person)-[:Owns]->(a:Account {is_blocked: TRUE}) +RETURN n.name, a.id AS blocked_account_id +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "n", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 23, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 24, + NameEnd: 30, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLMatchStatement{ + Optional: 32, + Match: 41, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 47, + Rparen: 56, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 48, + NameEnd: 49, + Name: "n", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 49, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 50, + NameEnd: 56, + Name: "Person", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 57, + Lbrack: 58, + Rbrack: 64, + Arrow: 65, + PatternFiller: &ast.GQLPatternFiller{ + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 59, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 60, + NameEnd: 64, + Name: "Owns", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 67, + Rparen: 96, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 68, + NameEnd: 69, + Name: "a", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 69, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 70, + NameEnd: 77, + Name: "Account", + }, + }, + }, + Filter: &ast.GQLPropertyFilters{ + Lbrace: 78, + PropertyFilterElemList: []*ast.GQLElementProperty{ + &ast.GQLElementProperty{ + ElementPropertyName: &ast.Ident{ + NamePos: 79, + NameEnd: 89, + Name: "is_blocked", + }, + ElementPropertyValue: &ast.BoolLiteral{ + ValuePos: 91, + Value: true, + }, + }, + }, + Rbrace: 95, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 98, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 105, + NameEnd: 106, + Name: "n", + }, + &ast.Ident{ + NamePos: 107, + NameEnd: 111, + Name: "name", + }, + }, + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 113, + NameEnd: 114, + Name: "a", + }, + &ast.Ident{ + NamePos: 115, + NameEnd: 117, + Name: "id", + }, + }, + }, + As: &ast.AsAlias{ + As: 118, + Alias: &ast.Ident{ + NamePos: 121, + NameEnd: 139, + Name: "blocked_account_id", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (n:Person) OPTIONAL MATCH (n:Person)-[:Owns]->(a:Account {is_blocked: TRUE}) RETURN n.name, a.id AS blocked_account_id diff --git a/testdata/result/gql/match_or_expression.sql.txt b/testdata/result/gql/match_or_expression.sql.txt new file mode 100644 index 00000000..be3b3fc8 --- /dev/null +++ b/testdata/result/gql/match_or_expression.sql.txt @@ -0,0 +1,130 @@ +--- match_or_expression.sql +GRAPH FinGraph +MATCH (n:Person|Account) +RETURN n.id, n.name, n.nick_name +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 38, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "n", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 23, + LabelExpression: &ast.GQLLabelOrExpression{ + Left: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 24, + NameEnd: 30, + Name: "Person", + }, + }, + Right: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 31, + NameEnd: 38, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 40, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 47, + NameEnd: 48, + Name: "n", + }, + &ast.Ident{ + NamePos: 49, + NameEnd: 51, + Name: "id", + }, + }, + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 53, + NameEnd: 54, + Name: "n", + }, + &ast.Ident{ + NamePos: 55, + NameEnd: 59, + Name: "name", + }, + }, + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 61, + NameEnd: 62, + Name: "n", + }, + &ast.Ident{ + NamePos: 63, + NameEnd: 72, + Name: "nick_name", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (n:Person|Account) RETURN n.id, n.name, n.nick_name diff --git a/testdata/result/gql/match_subpath_trail.sql.txt b/testdata/result/gql/match_subpath_trail.sql.txt new file mode 100644 index 00000000..bb820ff0 --- /dev/null +++ b/testdata/result/gql/match_subpath_trail.sql.txt @@ -0,0 +1,227 @@ +--- match_subpath_trail.sql +GRAPH FinGraph +MATCH + (TRAIL (a1:Account)-[t1:Transfers]->{3}(a4:Account)) + -[t4:Transfers]->(a5:Account) +RETURN COUNT(1) as num_paths +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLSubpathPattern{ + Lparen: 23, + Rparen: 74, + PathMode: &ast.GQLPathMode{ + Mode: "TRAIL", + ModeToken: &ast.Ident{ + NamePos: 24, + NameEnd: 29, + Name: "TRAIL", + }, + }, + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 30, + Rparen: 41, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 31, + NameEnd: 33, + Name: "a1", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 33, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 34, + NameEnd: 41, + Name: "Account", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 42, + Lbrack: 43, + Rbrack: 56, + Arrow: 57, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 44, + NameEnd: 46, + Name: "t1", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 46, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 47, + NameEnd: 56, + Name: "Transfers", + }, + }, + }, + }, + }, + Quantifier: &ast.GQLFixedQuantifier{ + Lbrace: 59, + Rbrace: 61, + Bound: &ast.IntLiteral{ + ValuePos: 60, + ValueEnd: 61, + Base: 10, + Value: "3", + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 62, + Rparen: 73, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 63, + NameEnd: 65, + Name: "a4", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 65, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 66, + NameEnd: 73, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 78, + Lbrack: 79, + Rbrack: 92, + Arrow: 93, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 80, + NameEnd: 82, + Name: "t4", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 82, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 83, + NameEnd: 92, + Name: "Transfers", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 95, + Rparen: 106, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 96, + NameEnd: 98, + Name: "a5", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 98, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 99, + NameEnd: 106, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 108, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.CallExpr{ + Rparen: 122, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 115, + NameEnd: 120, + Name: "COUNT", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 121, + ValueEnd: 122, + Base: 10, + Value: "1", + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 124, + Alias: &ast.Ident{ + NamePos: 127, + NameEnd: 136, + Name: "num_paths", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (TRAIL (a1:Account)-[t1:Transfers]->{3}(a4:Account))-[t4:Transfers]->(a5:Account) RETURN COUNT(1) AS num_paths diff --git a/testdata/result/gql/match_trail_subpath_walk.sql.txt b/testdata/result/gql/match_trail_subpath_walk.sql.txt new file mode 100644 index 00000000..b75e94e3 --- /dev/null +++ b/testdata/result/gql/match_trail_subpath_walk.sql.txt @@ -0,0 +1,185 @@ +--- match_trail_subpath_walk.sql +GRAPH FinGraph +MATCH TRAIL (WALK (a1:Account)-[t1:Transfers]->{4}(a5:Account)) +RETURN COUNT(1) as num_paths +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + PrefixOrMode: &ast.GQLPathMode{ + Mode: "TRAIL", + ModeToken: &ast.Ident{ + NamePos: 21, + NameEnd: 26, + Name: "TRAIL", + }, + }, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLSubpathPattern{ + Lparen: 27, + Rparen: 77, + PathMode: &ast.GQLPathMode{ + Mode: "WALK", + ModeToken: &ast.Ident{ + NamePos: 28, + NameEnd: 32, + Name: "WALK", + }, + }, + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 33, + Rparen: 44, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 34, + NameEnd: 36, + Name: "a1", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 36, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 37, + NameEnd: 44, + Name: "Account", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 45, + Lbrack: 46, + Rbrack: 59, + Arrow: 60, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 47, + NameEnd: 49, + Name: "t1", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 49, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 50, + NameEnd: 59, + Name: "Transfers", + }, + }, + }, + }, + }, + Quantifier: &ast.GQLFixedQuantifier{ + Lbrace: 62, + Rbrace: 64, + Bound: &ast.IntLiteral{ + ValuePos: 63, + ValueEnd: 64, + Base: 10, + Value: "4", + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 65, + Rparen: 76, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 66, + NameEnd: 68, + Name: "a5", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 68, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 69, + NameEnd: 76, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 79, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.CallExpr{ + Rparen: 93, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 86, + NameEnd: 91, + Name: "COUNT", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 92, + ValueEnd: 93, + Base: 10, + Value: "1", + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 95, + Alias: &ast.Ident{ + NamePos: 98, + NameEnd: 107, + Name: "num_paths", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH TRAIL (WALK (a1:Account)-[t1:Transfers]->{4}(a5:Account)) RETURN COUNT(1) AS num_paths diff --git a/testdata/result/gql/match_where_is_destination_of_return.sql.txt b/testdata/result/gql/match_where_is_destination_of_return.sql.txt new file mode 100644 index 00000000..72b80631 --- /dev/null +++ b/testdata/result/gql/match_where_is_destination_of_return.sql.txt @@ -0,0 +1,183 @@ +--- match_where_is_destination_of_return.sql +GRAPH FinGraph +MATCH (a:Account)-[transfer:Transfers]-(b:Account) +WHERE a IS DESTINATION of transfer +RETURN a.id AS a_id, b.id AS b_id +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 31, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "a", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 23, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 24, + NameEnd: 31, + Name: "Account", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeAny{ + FirstHyphen: 32, + LastHyphen: 53, + Lbrack: 33, + Rbrack: 52, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 34, + NameEnd: 42, + Name: "transfer", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 42, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 43, + NameEnd: 52, + Name: "Transfers", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 54, + Rparen: 64, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 55, + NameEnd: 56, + Name: "b", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 56, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 57, + NameEnd: 64, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + WhereClause: &ast.Where{ + Where: 66, + Expr: &ast.IsDestinationExpr{ + Node: &ast.Ident{ + NamePos: 72, + NameEnd: 73, + Name: "a", + }, + Edge: &ast.Ident{ + NamePos: 92, + NameEnd: 100, + Name: "transfer", + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 101, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 108, + NameEnd: 109, + Name: "a", + }, + &ast.Ident{ + NamePos: 110, + NameEnd: 112, + Name: "id", + }, + }, + }, + As: &ast.AsAlias{ + As: 113, + Alias: &ast.Ident{ + NamePos: 116, + NameEnd: 120, + Name: "a_id", + }, + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 122, + NameEnd: 123, + Name: "b", + }, + &ast.Ident{ + NamePos: 124, + NameEnd: 126, + Name: "id", + }, + }, + }, + As: &ast.AsAlias{ + As: 127, + Alias: &ast.Ident{ + NamePos: 130, + NameEnd: 134, + Name: "b_id", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (a:Account)-[transfer:Transfers]-(b:Account) WHERE a IS DESTINATION OF transfer RETURN a.id AS a_id, b.id AS b_id diff --git a/testdata/result/gql/match_where_is_source_of_return.sql.txt b/testdata/result/gql/match_where_is_source_of_return.sql.txt new file mode 100644 index 00000000..0970ea1d --- /dev/null +++ b/testdata/result/gql/match_where_is_source_of_return.sql.txt @@ -0,0 +1,183 @@ +--- match_where_is_source_of_return.sql +GRAPH FinGraph +MATCH (a:Account)-[transfer:Transfers]-(b:Account) +WHERE a IS SOURCE of transfer +RETURN a.id AS a_id, b.id AS b_id +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 31, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "a", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 23, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 24, + NameEnd: 31, + Name: "Account", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeAny{ + FirstHyphen: 32, + LastHyphen: 53, + Lbrack: 33, + Rbrack: 52, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 34, + NameEnd: 42, + Name: "transfer", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 42, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 43, + NameEnd: 52, + Name: "Transfers", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 54, + Rparen: 64, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 55, + NameEnd: 56, + Name: "b", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 56, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 57, + NameEnd: 64, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + WhereClause: &ast.Where{ + Where: 66, + Expr: &ast.IsSourceExpr{ + Node: &ast.Ident{ + NamePos: 72, + NameEnd: 73, + Name: "a", + }, + Edge: &ast.Ident{ + NamePos: 87, + NameEnd: 95, + Name: "transfer", + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 96, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 103, + NameEnd: 104, + Name: "a", + }, + &ast.Ident{ + NamePos: 105, + NameEnd: 107, + Name: "id", + }, + }, + }, + As: &ast.AsAlias{ + As: 108, + Alias: &ast.Ident{ + NamePos: 111, + NameEnd: 115, + Name: "a_id", + }, + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 117, + NameEnd: 118, + Name: "b", + }, + &ast.Ident{ + NamePos: 119, + NameEnd: 121, + Name: "id", + }, + }, + }, + As: &ast.AsAlias{ + As: 122, + Alias: &ast.Ident{ + NamePos: 125, + NameEnd: 129, + Name: "b_id", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (a:Account)-[transfer:Transfers]-(b:Account) WHERE a IS SOURCE OF transfer RETURN a.id AS a_id, b.id AS b_id diff --git a/testdata/result/gql/match_where_not_same_return.sql.txt b/testdata/result/gql/match_where_not_same_return.sql.txt new file mode 100644 index 00000000..c9e02e96 --- /dev/null +++ b/testdata/result/gql/match_where_not_same_return.sql.txt @@ -0,0 +1,203 @@ +--- match_where_not_same_return.sql +GRAPH FinGraph +MATCH (src:Account)<-[transfer:Transfers]-(dest:Account) +WHERE NOT SAME(src, dest) +RETURN src.id AS source_id, dest.id AS destination_id +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 33, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 22, + NameEnd: 25, + Name: "src", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 25, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 26, + NameEnd: 33, + Name: "Account", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeLeft{ + Lt: 34, + Lbrack: 36, + Rbrack: 55, + Hyphen: 56, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 37, + NameEnd: 45, + Name: "transfer", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 45, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 46, + NameEnd: 55, + Name: "Transfers", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 57, + Rparen: 70, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 58, + NameEnd: 62, + Name: "dest", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 62, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 63, + NameEnd: 70, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + WhereClause: &ast.Where{ + Where: 72, + Expr: &ast.UnaryExpr{ + OpPos: 78, + Op: "NOT", + Expr: &ast.CallExpr{ + Rparen: 96, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 82, + NameEnd: 86, + Name: "SAME", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.Ident{ + NamePos: 87, + NameEnd: 90, + Name: "src", + }, + }, + &ast.ExprArg{ + Expr: &ast.Ident{ + NamePos: 92, + NameEnd: 96, + Name: "dest", + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 98, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 105, + NameEnd: 108, + Name: "src", + }, + &ast.Ident{ + NamePos: 109, + NameEnd: 111, + Name: "id", + }, + }, + }, + As: &ast.AsAlias{ + As: 112, + Alias: &ast.Ident{ + NamePos: 115, + NameEnd: 124, + Name: "source_id", + }, + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 126, + NameEnd: 130, + Name: "dest", + }, + &ast.Ident{ + NamePos: 131, + NameEnd: 133, + Name: "id", + }, + }, + }, + As: &ast.AsAlias{ + As: 134, + Alias: &ast.Ident{ + NamePos: 137, + NameEnd: 151, + Name: "destination_id", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (src:Account)<-[transfer:Transfers]-(dest:Account) WHERE NOT SAME(src, dest) RETURN src.id AS source_id, dest.id AS destination_id diff --git a/testdata/result/gql/match_where_property_exists_return.sql.txt b/testdata/result/gql/match_where_property_exists_return.sql.txt new file mode 100644 index 00000000..1bf27c9a --- /dev/null +++ b/testdata/result/gql/match_where_property_exists_return.sql.txt @@ -0,0 +1,125 @@ +--- match_where_property_exists_return.sql +GRAPH FinGraph +MATCH (n:Person|Account WHERE PROPERTY_EXISTS(n, name)) +RETURN n.name +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 69, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "n", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 23, + LabelExpression: &ast.GQLLabelOrExpression{ + Left: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 24, + NameEnd: 30, + Name: "Person", + }, + }, + Right: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 31, + NameEnd: 38, + Name: "Account", + }, + }, + }, + }, + Filter: &ast.Where{ + Where: 39, + Expr: &ast.CallExpr{ + Rparen: 68, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 45, + NameEnd: 60, + Name: "PROPERTY_EXISTS", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.Ident{ + NamePos: 61, + NameEnd: 62, + Name: "n", + }, + }, + &ast.ExprArg{ + Expr: &ast.Ident{ + NamePos: 64, + NameEnd: 68, + Name: "name", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 71, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 78, + NameEnd: 79, + Name: "n", + }, + &ast.Ident{ + NamePos: 80, + NameEnd: 84, + Name: "name", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (n:Person|Account WHERE PROPERTY_EXISTS(n, name)) RETURN n.name diff --git a/testdata/result/gql/next.sql.txt b/testdata/result/gql/next.sql.txt new file mode 100644 index 00000000..1113ae75 --- /dev/null +++ b/testdata/result/gql/next.sql.txt @@ -0,0 +1,403 @@ +--- next.sql +GRAPH FinGraph +MATCH (:Account)-[:Transfers]->(account:Account) +RETURN account, COUNT(*) AS num_incoming_transfers +GROUP BY account + +NEXT + +MATCH (account:Account)<-[:Owns]-(owner:Person) +RETURN + account.id AS account_id, owner.name AS owner_name, + num_incoming_transfers + +NEXT + +FILTER num_incoming_transfers < 2 +RETURN account_id, owner_name +UNION ALL +RETURN "Bob" AS owner_name, 100 AS account_id +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + PatternFiller: &ast.GQLPatternFiller{ + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 22, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 23, + NameEnd: 30, + Name: "Account", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 31, + Lbrack: 32, + Rbrack: 43, + Arrow: 44, + PatternFiller: &ast.GQLPatternFiller{ + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 33, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 34, + NameEnd: 43, + Name: "Transfers", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 46, + Rparen: 62, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 47, + NameEnd: 54, + Name: "account", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 54, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 55, + NameEnd: 62, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 64, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Ident{ + NamePos: 71, + NameEnd: 78, + Name: "account", + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.CountStarExpr{ + Count: 80, + Rparen: 87, + }, + As: &ast.AsAlias{ + As: 89, + Alias: &ast.Ident{ + NamePos: 92, + NameEnd: 114, + Name: "num_incoming_transfers", + }, + }, + }, + }, + }, + GroupByClause: &ast.GroupBy{ + Group: 115, + Exprs: []ast.Expr{ + &ast.Ident{ + NamePos: 124, + NameEnd: 131, + Name: "account", + }, + }, + }, + }, + }, + }, + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 139, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 145, + Rparen: 161, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 146, + NameEnd: 153, + Name: "account", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 153, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 154, + NameEnd: 161, + Name: "Account", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeLeft{ + Lt: 162, + Lbrack: 164, + Rbrack: 170, + Hyphen: 171, + PatternFiller: &ast.GQLPatternFiller{ + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 165, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 166, + NameEnd: 170, + Name: "Owns", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 172, + Rparen: 185, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 173, + NameEnd: 178, + Name: "owner", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 178, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 179, + NameEnd: 185, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 187, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 196, + NameEnd: 203, + Name: "account", + }, + &ast.Ident{ + NamePos: 204, + NameEnd: 206, + Name: "id", + }, + }, + }, + As: &ast.AsAlias{ + As: 207, + Alias: &ast.Ident{ + NamePos: 210, + NameEnd: 220, + Name: "account_id", + }, + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 222, + NameEnd: 227, + Name: "owner", + }, + &ast.Ident{ + NamePos: 228, + NameEnd: 232, + Name: "name", + }, + }, + }, + As: &ast.AsAlias{ + As: 233, + Alias: &ast.Ident{ + NamePos: 236, + NameEnd: 246, + Name: "owner_name", + }, + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Ident{ + NamePos: 250, + NameEnd: 272, + Name: "num_incoming_transfers", + }, + }, + }, + }, + }, + }, + }, + &ast.GQLCompositeLinearQueryStatement{ + HeadSimpleLinearQueryStatement: &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLFilterStatement{ + Filter: 280, + Where: -1, + Expr: &ast.BinaryExpr{ + Op: "<", + Left: &ast.Ident{ + NamePos: 287, + NameEnd: 309, + Name: "num_incoming_transfers", + }, + Right: &ast.IntLiteral{ + ValuePos: 312, + ValueEnd: 313, + Base: 10, + Value: "2", + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 314, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Ident{ + NamePos: 321, + NameEnd: 331, + Name: "account_id", + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Ident{ + NamePos: 333, + NameEnd: 343, + Name: "owner_name", + }, + }, + }, + }, + }, + }, + }, + TailSimpleLinearQueryStatementList: []*ast.GQLSimpleLinearQueryStatementWithSetOperator{ + &ast.GQLSimpleLinearQueryStatementWithSetOperator{ + StartPos: 344, + SetOperator: "UNION", + AllOrDistinct: "ALL", + Statement: &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLReturnStatement{ + Return: 354, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.StringLiteral{ + ValuePos: 361, + ValueEnd: 366, + Value: "Bob", + }, + As: &ast.AsAlias{ + As: 367, + Alias: &ast.Ident{ + NamePos: 370, + NameEnd: 380, + Name: "owner_name", + }, + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.IntLiteral{ + ValuePos: 382, + ValueEnd: 385, + Base: 10, + Value: "100", + }, + As: &ast.AsAlias{ + As: 386, + Alias: &ast.Ident{ + NamePos: 389, + NameEnd: 399, + Name: "account_id", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (:Account)-[:Transfers]->(account:Account) RETURN account, COUNT(*) AS num_incoming_transfers GROUP BY account NEXT MATCH (account:Account)<-[:Owns]-(owner:Person) RETURN account.id AS account_id, owner.name AS owner_name, num_incoming_transfers NEXT FILTER num_incoming_transfers < 2 RETURN account_id, owner_name UNION ALL RETURN "Bob" AS owner_name, 100 AS account_id diff --git a/testdata/result/gql/next_1.sql.txt b/testdata/result/gql/next_1.sql.txt new file mode 100644 index 00000000..d9a689c8 --- /dev/null +++ b/testdata/result/gql/next_1.sql.txt @@ -0,0 +1,298 @@ +--- next_1.sql +GRAPH FinGraph +MATCH (:Account)-[:Transfers]->(account:Account) +RETURN account, COUNT(*) AS num_incoming_transfers +GROUP BY account + +NEXT + +MATCH (account:Account)<-[:Owns]-(owner:Person) +RETURN + account.id AS account_id, owner.name AS owner_name, + num_incoming_transfers +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + PatternFiller: &ast.GQLPatternFiller{ + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 22, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 23, + NameEnd: 30, + Name: "Account", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 31, + Lbrack: 32, + Rbrack: 43, + Arrow: 44, + PatternFiller: &ast.GQLPatternFiller{ + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 33, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 34, + NameEnd: 43, + Name: "Transfers", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 46, + Rparen: 62, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 47, + NameEnd: 54, + Name: "account", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 54, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 55, + NameEnd: 62, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 64, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Ident{ + NamePos: 71, + NameEnd: 78, + Name: "account", + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.CountStarExpr{ + Count: 80, + Rparen: 87, + }, + As: &ast.AsAlias{ + As: 89, + Alias: &ast.Ident{ + NamePos: 92, + NameEnd: 114, + Name: "num_incoming_transfers", + }, + }, + }, + }, + }, + GroupByClause: &ast.GroupBy{ + Group: 115, + Exprs: []ast.Expr{ + &ast.Ident{ + NamePos: 124, + NameEnd: 131, + Name: "account", + }, + }, + }, + }, + }, + }, + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 139, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 145, + Rparen: 161, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 146, + NameEnd: 153, + Name: "account", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 153, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 154, + NameEnd: 161, + Name: "Account", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeLeft{ + Lt: 162, + Lbrack: 164, + Rbrack: 170, + Hyphen: 171, + PatternFiller: &ast.GQLPatternFiller{ + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 165, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 166, + NameEnd: 170, + Name: "Owns", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 172, + Rparen: 185, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 173, + NameEnd: 178, + Name: "owner", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 178, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 179, + NameEnd: 185, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 187, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 196, + NameEnd: 203, + Name: "account", + }, + &ast.Ident{ + NamePos: 204, + NameEnd: 206, + Name: "id", + }, + }, + }, + As: &ast.AsAlias{ + As: 207, + Alias: &ast.Ident{ + NamePos: 210, + NameEnd: 220, + Name: "account_id", + }, + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 222, + NameEnd: 227, + Name: "owner", + }, + &ast.Ident{ + NamePos: 228, + NameEnd: 232, + Name: "name", + }, + }, + }, + As: &ast.AsAlias{ + As: 233, + Alias: &ast.Ident{ + NamePos: 236, + NameEnd: 246, + Name: "owner_name", + }, + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Ident{ + NamePos: 250, + NameEnd: 272, + Name: "num_incoming_transfers", + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (:Account)-[:Transfers]->(account:Account) RETURN account, COUNT(*) AS num_incoming_transfers GROUP BY account NEXT MATCH (account:Account)<-[:Owns]-(owner:Person) RETURN account.id AS account_id, owner.name AS owner_name, num_incoming_transfers diff --git a/testdata/result/gql/offset_1.sql.txt b/testdata/result/gql/offset_1.sql.txt new file mode 100644 index 00000000..81f6a471 --- /dev/null +++ b/testdata/result/gql/offset_1.sql.txt @@ -0,0 +1,113 @@ +--- offset_1.sql +GRAPH FinGraph +MATCH (p:Person) +OFFSET 2 +RETURN p.name, p.id +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "p", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 23, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 24, + NameEnd: 30, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLOffsetStatement{ + Offset: 32, + Count: &ast.IntLiteral{ + ValuePos: 39, + ValueEnd: 40, + Base: 10, + Value: "2", + }, + }, + &ast.GQLReturnStatement{ + Return: 41, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 48, + NameEnd: 49, + Name: "p", + }, + &ast.Ident{ + NamePos: 50, + NameEnd: 54, + Name: "name", + }, + }, + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 56, + NameEnd: 57, + Name: "p", + }, + &ast.Ident{ + NamePos: 58, + NameEnd: 60, + Name: "id", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (p:Person) OFFSET 2 RETURN p.name, p.id diff --git a/testdata/result/gql/order_by.sql.txt b/testdata/result/gql/order_by.sql.txt new file mode 100644 index 00000000..2e6d8373 --- /dev/null +++ b/testdata/result/gql/order_by.sql.txt @@ -0,0 +1,201 @@ +--- order_by.sql +GRAPH FinGraph +MATCH (src_account:Account)-[transfer:Transfers]->(dst_account:Account) +ORDER BY transfer.amount DESC +LIMIT 3 +RETURN src_account.id AS account_id, transfer.amount AS transfer_amount +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 41, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 22, + NameEnd: 33, + Name: "src_account", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 33, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 34, + NameEnd: 41, + Name: "Account", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 42, + Lbrack: 43, + Rbrack: 62, + Arrow: 63, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 44, + NameEnd: 52, + Name: "transfer", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 52, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 53, + NameEnd: 62, + Name: "Transfers", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 65, + Rparen: 85, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 66, + NameEnd: 77, + Name: "dst_account", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 77, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 78, + NameEnd: 85, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLOrderByStatement{ + Order: 87, + OrderBySpecificationList: []*ast.GQLOrderBySpecification{ + &ast.GQLOrderBySpecification{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 96, + NameEnd: 104, + Name: "transfer", + }, + &ast.Ident{ + NamePos: 105, + NameEnd: 111, + Name: "amount", + }, + }, + }, + DirectionPos: 112, + Direction: "DESC", + }, + }, + }, + &ast.GQLLimitStatement{ + Limit: 117, + Count: &ast.IntLiteral{ + ValuePos: 123, + ValueEnd: 124, + Base: 10, + Value: "3", + }, + }, + &ast.GQLReturnStatement{ + Return: 125, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 132, + NameEnd: 143, + Name: "src_account", + }, + &ast.Ident{ + NamePos: 144, + NameEnd: 146, + Name: "id", + }, + }, + }, + As: &ast.AsAlias{ + As: 147, + Alias: &ast.Ident{ + NamePos: 150, + NameEnd: 160, + Name: "account_id", + }, + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 162, + NameEnd: 170, + Name: "transfer", + }, + &ast.Ident{ + NamePos: 171, + NameEnd: 177, + Name: "amount", + }, + }, + }, + As: &ast.AsAlias{ + As: 178, + Alias: &ast.Ident{ + NamePos: 181, + NameEnd: 196, + Name: "transfer_amount", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (src_account:Account)-[transfer:Transfers]->(dst_account:Account) ORDER BY transfer.amount DESC LIMIT 3 RETURN src_account.id AS account_id, transfer.amount AS transfer_amount diff --git a/testdata/result/gql/order_by_collate.sql.txt b/testdata/result/gql/order_by_collate.sql.txt new file mode 100644 index 00000000..93f121d3 --- /dev/null +++ b/testdata/result/gql/order_by_collate.sql.txt @@ -0,0 +1,113 @@ +--- order_by_collate.sql +GRAPH FinGraph MATCH (a:Person) ORDER BY a.name COLLATE 'und:ci' RETURN a.name +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "a", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 23, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 24, + NameEnd: 30, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLOrderByStatement{ + Order: 32, + OrderBySpecificationList: []*ast.GQLOrderBySpecification{ + &ast.GQLOrderBySpecification{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 41, + NameEnd: 42, + Name: "a", + }, + &ast.Ident{ + NamePos: 43, + NameEnd: 47, + Name: "name", + }, + }, + }, + CollationSpecification: &ast.GQLCollationSpecification{ + Collate: 48, + Specification: &ast.StringLiteral{ + ValuePos: 56, + ValueEnd: 64, + Value: "und:ci", + }, + }, + DirectionPos: -1, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 65, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 72, + NameEnd: 73, + Name: "a", + }, + &ast.Ident{ + NamePos: 74, + NameEnd: 78, + Name: "name", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (a:Person) ORDER BY a.name COLLATE "und:ci" RETURN a.name diff --git a/testdata/result/gql/return.sql.txt b/testdata/result/gql/return.sql.txt new file mode 100644 index 00000000..f4283f44 --- /dev/null +++ b/testdata/result/gql/return.sql.txt @@ -0,0 +1,285 @@ +--- return.sql +GRAPH FinGraph +MATCH (:Account)-[:Transfers]->(account:Account) +RETURN account, COUNT(*) AS num_incoming_transfers +GROUP BY account + +NEXT + +MATCH (account:Account)<-[:Owns]-(owner:Person) +RETURN owner.name AS owner_name, num_incoming_transfers +ORDER BY num_incoming_transfers DESC +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + PatternFiller: &ast.GQLPatternFiller{ + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 22, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 23, + NameEnd: 30, + Name: "Account", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 31, + Lbrack: 32, + Rbrack: 43, + Arrow: 44, + PatternFiller: &ast.GQLPatternFiller{ + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 33, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 34, + NameEnd: 43, + Name: "Transfers", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 46, + Rparen: 62, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 47, + NameEnd: 54, + Name: "account", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 54, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 55, + NameEnd: 62, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 64, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Ident{ + NamePos: 71, + NameEnd: 78, + Name: "account", + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.CountStarExpr{ + Count: 80, + Rparen: 87, + }, + As: &ast.AsAlias{ + As: 89, + Alias: &ast.Ident{ + NamePos: 92, + NameEnd: 114, + Name: "num_incoming_transfers", + }, + }, + }, + }, + }, + GroupByClause: &ast.GroupBy{ + Group: 115, + Exprs: []ast.Expr{ + &ast.Ident{ + NamePos: 124, + NameEnd: 131, + Name: "account", + }, + }, + }, + }, + }, + }, + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 139, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 145, + Rparen: 161, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 146, + NameEnd: 153, + Name: "account", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 153, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 154, + NameEnd: 161, + Name: "Account", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeLeft{ + Lt: 162, + Lbrack: 164, + Rbrack: 170, + Hyphen: 171, + PatternFiller: &ast.GQLPatternFiller{ + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 165, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 166, + NameEnd: 170, + Name: "Owns", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 172, + Rparen: 185, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 173, + NameEnd: 178, + Name: "owner", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 178, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 179, + NameEnd: 185, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 187, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 194, + NameEnd: 199, + Name: "owner", + }, + &ast.Ident{ + NamePos: 200, + NameEnd: 204, + Name: "name", + }, + }, + }, + As: &ast.AsAlias{ + As: 205, + Alias: &ast.Ident{ + NamePos: 208, + NameEnd: 218, + Name: "owner_name", + }, + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Ident{ + NamePos: 220, + NameEnd: 242, + Name: "num_incoming_transfers", + }, + }, + }, + }, + OrderByClause: &ast.OrderBy{ + Order: 243, + Items: []*ast.OrderByItem{ + &ast.OrderByItem{ + DirPos: 275, + Expr: &ast.Ident{ + NamePos: 252, + NameEnd: 274, + Name: "num_incoming_transfers", + }, + Dir: "DESC", + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (:Account)-[:Transfers]->(account:Account) RETURN account, COUNT(*) AS num_incoming_transfers GROUP BY account NEXT MATCH (account:Account)<-[:Owns]-(owner:Person) RETURN owner.name AS owner_name, num_incoming_transfers ORDER BY num_incoming_transfers DESC diff --git a/testdata/result/gql/return_array_subquery.sql.txt b/testdata/result/gql/return_array_subquery.sql.txt new file mode 100644 index 00000000..995c812c --- /dev/null +++ b/testdata/result/gql/return_array_subquery.sql.txt @@ -0,0 +1,313 @@ +--- return_array_subquery.sql +GRAPH FinGraph +MATCH (p:Person)-[:Owns]->(account:Account) +RETURN + p.name, account.id AS account_id, + ARRAY { + MATCH (a:Account)-[transfer:Transfers]->(:Account) + WHERE a = account + RETURN transfer.amount AS transfers + } AS transfers +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "p", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 23, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 24, + NameEnd: 30, + Name: "Person", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 31, + Lbrack: 32, + Rbrack: 38, + Arrow: 39, + PatternFiller: &ast.GQLPatternFiller{ + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 33, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 34, + NameEnd: 38, + Name: "Owns", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 41, + Rparen: 57, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 42, + NameEnd: 49, + Name: "account", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 49, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 50, + NameEnd: 57, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 59, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 67, + NameEnd: 68, + Name: "p", + }, + &ast.Ident{ + NamePos: 69, + NameEnd: 73, + Name: "name", + }, + }, + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 75, + NameEnd: 82, + Name: "account", + }, + &ast.Ident{ + NamePos: 83, + NameEnd: 85, + Name: "id", + }, + }, + }, + As: &ast.AsAlias{ + As: 86, + Alias: &ast.Ident{ + NamePos: 89, + NameEnd: 99, + Name: "account_id", + }, + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.ArrayGQLSubQuery{ + Array: 102, + Rbrace: 225, + Query: &ast.GQLQueryExpr{ + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 113, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 119, + Rparen: 129, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 120, + NameEnd: 121, + Name: "a", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 121, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 122, + NameEnd: 129, + Name: "Account", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 130, + Lbrack: 131, + Rbrack: 150, + Arrow: 151, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 132, + NameEnd: 140, + Name: "transfer", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 140, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 141, + NameEnd: 150, + Name: "Transfers", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 153, + Rparen: 162, + PatternFiller: &ast.GQLPatternFiller{ + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 154, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 155, + NameEnd: 162, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + WhereClause: &ast.Where{ + Where: 167, + Expr: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Ident{ + NamePos: 173, + NameEnd: 174, + Name: "a", + }, + Right: &ast.Ident{ + NamePos: 177, + NameEnd: 184, + Name: "account", + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 188, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 195, + NameEnd: 203, + Name: "transfer", + }, + &ast.Ident{ + NamePos: 204, + NameEnd: 210, + Name: "amount", + }, + }, + }, + As: &ast.AsAlias{ + As: 211, + Alias: &ast.Ident{ + NamePos: 214, + NameEnd: 223, + Name: "transfers", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 227, + Alias: &ast.Ident{ + NamePos: 230, + NameEnd: 239, + Name: "transfers", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (p:Person)-[:Owns]->(account:Account) RETURN p.name, account.id AS account_id, ARRAY {MATCH (a:Account)-[transfer:Transfers]->(:Account) WHERE a = account RETURN transfer.amount AS transfers} AS transfers diff --git a/testdata/result/gql/return_distinct.sql.txt b/testdata/result/gql/return_distinct.sql.txt new file mode 100644 index 00000000..32c1e9cf --- /dev/null +++ b/testdata/result/gql/return_distinct.sql.txt @@ -0,0 +1,210 @@ +--- return_distinct.sql +GRAPH FinGraph +MATCH (src:Account {id: 7})-[e:Transfers]->{1, 3}(dst:Account) +RETURN DISTINCT ARRAY_LENGTH(e) AS hops, dst.id AS destination_account_id +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 41, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 22, + NameEnd: 25, + Name: "src", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 25, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 26, + NameEnd: 33, + Name: "Account", + }, + }, + }, + Filter: &ast.GQLPropertyFilters{ + Lbrace: 34, + PropertyFilterElemList: []*ast.GQLElementProperty{ + &ast.GQLElementProperty{ + ElementPropertyName: &ast.Ident{ + NamePos: 35, + NameEnd: 37, + Name: "id", + }, + ElementPropertyValue: &ast.IntLiteral{ + ValuePos: 39, + ValueEnd: 40, + Base: 10, + Value: "7", + }, + }, + }, + Rbrace: 40, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 42, + Lbrack: 43, + Rbrack: 55, + Arrow: 56, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 44, + NameEnd: 45, + Name: "e", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 45, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 46, + NameEnd: 55, + Name: "Transfers", + }, + }, + }, + }, + }, + Quantifier: &ast.GQLBoundedQuantifier{ + Lbrace: 58, + Rbrace: 63, + LowerBound: &ast.IntLiteral{ + ValuePos: 59, + ValueEnd: 60, + Base: 10, + Value: "1", + }, + UpperBound: &ast.IntLiteral{ + ValuePos: 62, + ValueEnd: 63, + Base: 10, + Value: "3", + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 64, + Rparen: 76, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 65, + NameEnd: 68, + Name: "dst", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 68, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 69, + NameEnd: 76, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 78, + AllOrDistinct: "DISTINCT", + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.CallExpr{ + Rparen: 108, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 94, + NameEnd: 106, + Name: "ARRAY_LENGTH", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.Ident{ + NamePos: 107, + NameEnd: 108, + Name: "e", + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 110, + Alias: &ast.Ident{ + NamePos: 113, + NameEnd: 117, + Name: "hops", + }, + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 119, + NameEnd: 122, + Name: "dst", + }, + &ast.Ident{ + NamePos: 123, + NameEnd: 125, + Name: "id", + }, + }, + }, + As: &ast.AsAlias{ + As: 126, + Alias: &ast.Ident{ + NamePos: 129, + NameEnd: 151, + Name: "destination_account_id", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (src:Account {id: 7})-[e:Transfers]->{1,3}(dst:Account) RETURN DISTINCT ARRAY_LENGTH(e) AS hops, dst.id AS destination_account_id diff --git a/testdata/result/gql/return_exists_graph_pattern.sql.txt b/testdata/result/gql/return_exists_graph_pattern.sql.txt new file mode 100644 index 00000000..46f78a18 --- /dev/null +++ b/testdata/result/gql/return_exists_graph_pattern.sql.txt @@ -0,0 +1,153 @@ +--- return_exists_graph_pattern.sql +GRAPH FinGraph +RETURN EXISTS { + (p:Person)-[o:Owns]->(a:Account) WHERE p.Name LIKE 'D%' +} AS results +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLReturnStatement{ + Return: 15, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.ExistsGQLSubQuery{ + Exists: 22, + Rbrace: 89, + Query: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 33, + Rparen: 42, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 34, + NameEnd: 35, + Name: "p", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 35, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 36, + NameEnd: 42, + Name: "Person", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 43, + Lbrack: 44, + Rbrack: 51, + Arrow: 52, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 45, + NameEnd: 46, + Name: "o", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 46, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 47, + NameEnd: 51, + Name: "Owns", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 54, + Rparen: 64, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 55, + NameEnd: 56, + Name: "a", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 56, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 57, + NameEnd: 64, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + WhereClause: &ast.Where{ + Where: 66, + Expr: &ast.BinaryExpr{ + Op: "LIKE", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 72, + NameEnd: 73, + Name: "p", + }, + &ast.Ident{ + NamePos: 74, + NameEnd: 78, + Name: "Name", + }, + }, + }, + Right: &ast.StringLiteral{ + ValuePos: 84, + ValueEnd: 88, + Value: "D%", + }, + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 91, + Alias: &ast.Ident{ + NamePos: 94, + NameEnd: 101, + Name: "results", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph RETURN EXISTS {(p:Person)-[o:Owns]->(a:Account) WHERE p.Name LIKE "D%"} AS results diff --git a/testdata/result/gql/return_exists_match.sql.txt b/testdata/result/gql/return_exists_match.sql.txt new file mode 100644 index 00000000..a0356c3c --- /dev/null +++ b/testdata/result/gql/return_exists_match.sql.txt @@ -0,0 +1,158 @@ +--- return_exists_match.sql +GRAPH FinGraph +RETURN EXISTS { + MATCH (p:Person)-[o:Owns]->(a:Account) + WHERE p.Name LIKE 'D%' +} AS results +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLReturnStatement{ + Return: 15, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.ExistsGQLSubQuery{ + Exists: 22, + Rbrace: 97, + Query: &ast.GQLMatchStatement{ + Optional: -1, + Match: 33, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 39, + Rparen: 48, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 40, + NameEnd: 41, + Name: "p", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 41, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 42, + NameEnd: 48, + Name: "Person", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 49, + Lbrack: 50, + Rbrack: 57, + Arrow: 58, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 51, + NameEnd: 52, + Name: "o", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 52, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 53, + NameEnd: 57, + Name: "Owns", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 60, + Rparen: 70, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 61, + NameEnd: 62, + Name: "a", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 62, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 63, + NameEnd: 70, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + WhereClause: &ast.Where{ + Where: 74, + Expr: &ast.BinaryExpr{ + Op: "LIKE", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 80, + NameEnd: 81, + Name: "p", + }, + &ast.Ident{ + NamePos: 82, + NameEnd: 86, + Name: "Name", + }, + }, + }, + Right: &ast.StringLiteral{ + ValuePos: 92, + ValueEnd: 96, + Value: "D%", + }, + }, + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 99, + Alias: &ast.Ident{ + NamePos: 102, + NameEnd: 109, + Name: "results", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph RETURN EXISTS {MATCH (p:Person)-[o:Owns]->(a:Account) WHERE p.Name LIKE "D%"} AS results diff --git a/testdata/result/gql/return_exists_query_expr.sql.txt b/testdata/result/gql/return_exists_query_expr.sql.txt new file mode 100644 index 00000000..3d0b4254 --- /dev/null +++ b/testdata/result/gql/return_exists_query_expr.sql.txt @@ -0,0 +1,204 @@ +--- return_exists_query_expr.sql +GRAPH FinGraph +RETURN EXISTS { + MATCH (p:Person)-[o:Owns]->(a:Account) + WHERE p.Name LIKE 'D%' + RETURN p.Name + LIMIT 1 +} AS results +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLReturnStatement{ + Return: 15, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.ExistsGQLSubQuery{ + Exists: 22, + Rbrace: 123, + Query: &ast.GQLQueryExpr{ + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 33, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 39, + Rparen: 48, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 40, + NameEnd: 41, + Name: "p", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 41, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 42, + NameEnd: 48, + Name: "Person", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 49, + Lbrack: 50, + Rbrack: 57, + Arrow: 58, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 51, + NameEnd: 52, + Name: "o", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 52, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 53, + NameEnd: 57, + Name: "Owns", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 60, + Rparen: 70, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 61, + NameEnd: 62, + Name: "a", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 62, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 63, + NameEnd: 70, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + WhereClause: &ast.Where{ + Where: 74, + Expr: &ast.BinaryExpr{ + Op: "LIKE", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 80, + NameEnd: 81, + Name: "p", + }, + &ast.Ident{ + NamePos: 82, + NameEnd: 86, + Name: "Name", + }, + }, + }, + Right: &ast.StringLiteral{ + ValuePos: 92, + ValueEnd: 96, + Value: "D%", + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 99, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 106, + NameEnd: 107, + Name: "p", + }, + &ast.Ident{ + NamePos: 108, + NameEnd: 112, + Name: "Name", + }, + }, + }, + }, + }, + }, + LimitAndOffsetClause: &ast.GQLLimitClause{ + Limit: &ast.Limit{ + Limit: 115, + Count: &ast.IntLiteral{ + ValuePos: 121, + ValueEnd: 122, + Base: 10, + Value: "1", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 125, + Alias: &ast.Ident{ + NamePos: 128, + NameEnd: 135, + Name: "results", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph RETURN EXISTS {MATCH (p:Person)-[o:Owns]->(a:Account) WHERE p.Name LIKE "D%" RETURN p.Name LIMIT 1} AS results diff --git a/testdata/result/gql/return_in_gql_subquery.sql.txt b/testdata/result/gql/return_in_gql_subquery.sql.txt new file mode 100644 index 00000000..2e9463c5 --- /dev/null +++ b/testdata/result/gql/return_in_gql_subquery.sql.txt @@ -0,0 +1,173 @@ +--- return_in_gql_subquery.sql +GRAPH FinGraph +RETURN 'Dana' IN { + MATCH (p:Person)-[o:Owns]->(a:Account) + RETURN p.name +} AS results +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLReturnStatement{ + Return: 15, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.InExpr{ + Left: &ast.StringLiteral{ + ValuePos: 22, + ValueEnd: 28, + Value: "Dana", + }, + Right: &ast.GQLSubQueryInCondition{ + Lbrace: 32, + Rbrace: 91, + Query: &ast.GQLQueryExpr{ + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 36, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 42, + Rparen: 51, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 43, + NameEnd: 44, + Name: "p", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 44, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 45, + NameEnd: 51, + Name: "Person", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 52, + Lbrack: 53, + Rbrack: 60, + Arrow: 61, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 54, + NameEnd: 55, + Name: "o", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 55, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 56, + NameEnd: 60, + Name: "Owns", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 63, + Rparen: 73, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 64, + NameEnd: 65, + Name: "a", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 65, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 66, + NameEnd: 73, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 77, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 84, + NameEnd: 85, + Name: "p", + }, + &ast.Ident{ + NamePos: 86, + NameEnd: 90, + Name: "name", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 93, + Alias: &ast.Ident{ + NamePos: 96, + NameEnd: 103, + Name: "results", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph RETURN "Dana" IN {MATCH (p:Person)-[o:Owns]->(a:Account) RETURN p.name} AS results diff --git a/testdata/result/gql/return_offset_limit.sql.txt b/testdata/result/gql/return_offset_limit.sql.txt new file mode 100644 index 00000000..ad1e4ee7 --- /dev/null +++ b/testdata/result/gql/return_offset_limit.sql.txt @@ -0,0 +1,125 @@ +--- return_offset_limit.sql +GRAPH FinGraph +MATCH (p:Person) +RETURN p.name, p.id +OFFSET 1 +LIMIT 1 +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "p", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 23, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 24, + NameEnd: 30, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 32, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 39, + NameEnd: 40, + Name: "p", + }, + &ast.Ident{ + NamePos: 41, + NameEnd: 45, + Name: "name", + }, + }, + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 47, + NameEnd: 48, + Name: "p", + }, + &ast.Ident{ + NamePos: 49, + NameEnd: 51, + Name: "id", + }, + }, + }, + }, + }, + }, + LimitAndOffsetClause: &ast.GQLLimitWithOffsetClause{ + Offset: &ast.Offset{ + Offset: 52, + Value: &ast.IntLiteral{ + ValuePos: 59, + ValueEnd: 60, + Base: 10, + Value: "1", + }, + }, + Limit: &ast.Limit{ + Limit: 61, + Count: &ast.IntLiteral{ + ValuePos: 67, + ValueEnd: 68, + Base: 10, + Value: "1", + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (p:Person) RETURN p.name, p.id OFFSET 1 LIMIT 1 diff --git a/testdata/result/gql/return_value_subquery.sql.txt b/testdata/result/gql/return_value_subquery.sql.txt new file mode 100644 index 00000000..dae5de82 --- /dev/null +++ b/testdata/result/gql/return_value_subquery.sql.txt @@ -0,0 +1,156 @@ +--- return_value_subquery.sql +GRAPH FinGraph +RETURN VALUE { + MATCH (p:Person) + WHERE p.name LIKE '%e%' + RETURN p.name + LIMIT 1 +} AS results +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLReturnStatement{ + Return: 15, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.ValueGQLSubQuery{ + Array: 22, + Rbrace: 101, + Query: &ast.GQLQueryExpr{ + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 32, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 38, + Rparen: 47, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 39, + NameEnd: 40, + Name: "p", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 40, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 41, + NameEnd: 47, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + WhereClause: &ast.Where{ + Where: 51, + Expr: &ast.BinaryExpr{ + Op: "LIKE", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 57, + NameEnd: 58, + Name: "p", + }, + &ast.Ident{ + NamePos: 59, + NameEnd: 63, + Name: "name", + }, + }, + }, + Right: &ast.StringLiteral{ + ValuePos: 69, + ValueEnd: 74, + Value: "%e%", + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 77, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 84, + NameEnd: 85, + Name: "p", + }, + &ast.Ident{ + NamePos: 86, + NameEnd: 90, + Name: "name", + }, + }, + }, + }, + }, + }, + LimitAndOffsetClause: &ast.GQLLimitClause{ + Limit: &ast.Limit{ + Limit: 93, + Count: &ast.IntLiteral{ + ValuePos: 99, + ValueEnd: 100, + Base: 10, + Value: "1", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 103, + Alias: &ast.Ident{ + NamePos: 106, + NameEnd: 113, + Name: "results", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph RETURN VALUE {MATCH (p:Person) WHERE p.name LIKE "%e%" RETURN p.name LIMIT 1} AS results diff --git a/testdata/result/gql/skip_1.sql.txt b/testdata/result/gql/skip_1.sql.txt new file mode 100644 index 00000000..da65cb57 --- /dev/null +++ b/testdata/result/gql/skip_1.sql.txt @@ -0,0 +1,114 @@ +--- skip_1.sql +GRAPH FinGraph +MATCH (p:Person) +SKIP 2 +RETURN p.name, p.id +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "p", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 23, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 24, + NameEnd: 30, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLOffsetStatement{ + Offset: 32, + IsSkip: true, + Count: &ast.IntLiteral{ + ValuePos: 37, + ValueEnd: 38, + Base: 10, + Value: "2", + }, + }, + &ast.GQLReturnStatement{ + Return: 39, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 46, + NameEnd: 47, + Name: "p", + }, + &ast.Ident{ + NamePos: 48, + NameEnd: 52, + Name: "name", + }, + }, + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 54, + NameEnd: 55, + Name: "p", + }, + &ast.Ident{ + NamePos: 56, + NameEnd: 58, + Name: "id", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (p:Person) SKIP 2 RETURN p.name, p.id diff --git a/testdata/result/gql/with_1.sql.txt b/testdata/result/gql/with_1.sql.txt new file mode 100644 index 00000000..0b7c91a2 --- /dev/null +++ b/testdata/result/gql/with_1.sql.txt @@ -0,0 +1,181 @@ +--- with_1.sql +GRAPH FinGraph +MATCH (:Account)-[:Transfers]->(account:Account) +WITH account, COUNT(*) AS num_incoming_transfers GROUP BY account +RETURN account.id AS account_id, num_incoming_transfers +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 15, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + PatternFiller: &ast.GQLPatternFiller{ + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 22, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 23, + NameEnd: 30, + Name: "Account", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 31, + Lbrack: 32, + Rbrack: 43, + Arrow: 44, + PatternFiller: &ast.GQLPatternFiller{ + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 33, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 34, + NameEnd: 43, + Name: "Transfers", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 46, + Rparen: 62, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 47, + NameEnd: 54, + Name: "account", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 54, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 55, + NameEnd: 62, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLWithStatement{ + With: 64, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Ident{ + NamePos: 69, + NameEnd: 76, + Name: "account", + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.CountStarExpr{ + Count: 78, + Rparen: 85, + }, + As: &ast.AsAlias{ + As: 87, + Alias: &ast.Ident{ + NamePos: 90, + NameEnd: 112, + Name: "num_incoming_transfers", + }, + }, + }, + }, + }, + GroupByClause: &ast.GroupBy{ + Group: 113, + Exprs: []ast.Expr{ + &ast.Ident{ + NamePos: 122, + NameEnd: 129, + Name: "account", + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 130, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 137, + NameEnd: 144, + Name: "account", + }, + &ast.Ident{ + NamePos: 145, + NameEnd: 147, + Name: "id", + }, + }, + }, + As: &ast.AsAlias{ + As: 148, + Alias: &ast.Ident{ + NamePos: 151, + NameEnd: 161, + Name: "account_id", + }, + }, + }, + }, + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Ident{ + NamePos: 163, + NameEnd: 185, + Name: "num_incoming_transfers", + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (:Account)-[:Transfers]->(account:Account) WITH account, COUNT(*) AS num_incoming_transfers GROUP BY account RETURN account.id AS account_id, num_incoming_transfers diff --git a/testdata/result/gql_path_pattern/abbr_edge_any.sql.txt b/testdata/result/gql_path_pattern/abbr_edge_any.sql.txt new file mode 100644 index 00000000..dcf96d59 --- /dev/null +++ b/testdata/result/gql_path_pattern/abbr_edge_any.sql.txt @@ -0,0 +1,15 @@ +--- abbr_edge_any.sql + - +--- AST +&ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLAbbreviatedEdgeAny{ + Hyphen: 1, + }, + }, + }, +} + +--- SQL +- diff --git a/testdata/result/gql_path_pattern/abbreviated_edge_left.sql.txt b/testdata/result/gql_path_pattern/abbreviated_edge_left.sql.txt new file mode 100644 index 00000000..4dd61d6c --- /dev/null +++ b/testdata/result/gql_path_pattern/abbreviated_edge_left.sql.txt @@ -0,0 +1,16 @@ +--- abbreviated_edge_left.sql + <- +--- AST +&ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLAbbreviatedEdgeLeft{ + Lt: 1, + Hyphen: 2, + }, + }, + }, +} + +--- SQL +<- diff --git a/testdata/result/gql_path_pattern/abbreviated_edge_right.sql.txt b/testdata/result/gql_path_pattern/abbreviated_edge_right.sql.txt new file mode 100644 index 00000000..a01ffbf1 --- /dev/null +++ b/testdata/result/gql_path_pattern/abbreviated_edge_right.sql.txt @@ -0,0 +1,16 @@ +--- abbreviated_edge_right.sql + -> +--- AST +&ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLAbbreviatedEdgeRight{ + Hyphen: 1, + Arrow: 1, + }, + }, + }, +} + +--- SQL +-> diff --git a/testdata/result/gql_path_pattern/full_edge_any.sql.txt b/testdata/result/gql_path_pattern/full_edge_any.sql.txt new file mode 100644 index 00000000..5b2107a9 --- /dev/null +++ b/testdata/result/gql_path_pattern/full_edge_any.sql.txt @@ -0,0 +1,25 @@ +--- full_edge_any.sql + -[a]- +--- AST +&ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeAny{ + FirstHyphen: 1, + LastHyphen: 5, + Lbrack: 2, + Rbrack: 4, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 3, + NameEnd: 4, + Name: "a", + }, + }, + }, + }, + }, +} + +--- SQL +-[a]- diff --git a/testdata/result/gql_path_pattern/full_edge_left.sql.txt b/testdata/result/gql_path_pattern/full_edge_left.sql.txt new file mode 100644 index 00000000..8c15dfba --- /dev/null +++ b/testdata/result/gql_path_pattern/full_edge_left.sql.txt @@ -0,0 +1,25 @@ +--- full_edge_left.sql + <-[a]- +--- AST +&ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeLeft{ + Lt: 2, + Lbrack: 4, + Rbrack: 6, + Hyphen: 7, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 5, + NameEnd: 6, + Name: "a", + }, + }, + }, + }, + }, +} + +--- SQL +<-[a]- diff --git a/testdata/result/gql_path_pattern/full_edge_right.sql.txt b/testdata/result/gql_path_pattern/full_edge_right.sql.txt new file mode 100644 index 00000000..83f6da0b --- /dev/null +++ b/testdata/result/gql_path_pattern/full_edge_right.sql.txt @@ -0,0 +1,18 @@ +--- full_edge_right.sql + -[]-> +--- AST +&ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 1, + Lbrack: 2, + Rbrack: 3, + Arrow: 4, + }, + }, + }, +} + +--- SQL +-[]-> diff --git a/testdata/result/gql_path_pattern/node_pattern_unnamed.sql.txt b/testdata/result/gql_path_pattern/node_pattern_unnamed.sql.txt new file mode 100644 index 00000000..f53470e3 --- /dev/null +++ b/testdata/result/gql_path_pattern/node_pattern_unnamed.sql.txt @@ -0,0 +1,17 @@ +--- node_pattern_unnamed.sql + () +--- AST +&ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 1, + Rparen: 2, + PatternFiller: &ast.GQLPatternFiller{}, + }, + }, + }, +} + +--- SQL +() diff --git a/testdata/result/gql_path_pattern/pattern_1.sql.txt b/testdata/result/gql_path_pattern/pattern_1.sql.txt new file mode 100644 index 00000000..68d04a01 --- /dev/null +++ b/testdata/result/gql_path_pattern/pattern_1.sql.txt @@ -0,0 +1,28 @@ +--- pattern_1.sql +(p:%) +--- AST +&ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Rparen: 4, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 1, + NameEnd: 2, + Name: "p", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 2, + LabelExpression: &ast.GQLWildcardLabel{ + Percent: 3, + }, + }, + }, + }, + }, + }, +} + +--- SQL +(p:%) diff --git a/testdata/result/gql_path_pattern/pattern_2.sql.txt b/testdata/result/gql_path_pattern/pattern_2.sql.txt new file mode 100644 index 00000000..5699d63d --- /dev/null +++ b/testdata/result/gql_path_pattern/pattern_2.sql.txt @@ -0,0 +1,64 @@ +--- pattern_2.sql +(p:(Singer|(!Writer&!Producer))) +--- AST +&ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Rparen: 31, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 1, + NameEnd: 2, + Name: "p", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 2, + LabelExpression: &ast.GQLLabelParenExpression{ + Lparen: 3, + Rparen: 30, + LabelExpr: &ast.GQLLabelOrExpression{ + Left: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 4, + NameEnd: 10, + Name: "Singer", + }, + }, + Right: &ast.GQLLabelParenExpression{ + Lparen: 11, + Rparen: 29, + LabelExpr: &ast.GQLLabelNotExpression{ + Not: 12, + LabelExpression: &ast.GQLLabelAndExpression{ + Left: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 13, + NameEnd: 19, + Name: "Writer", + }, + }, + Right: &ast.GQLLabelNotExpression{ + Not: 20, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 21, + NameEnd: 29, + Name: "Producer", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +(p:(Singer|(!Writer&!Producer))) diff --git a/testdata/result/gql_path_pattern/quantified_subpath_lower_default.sql.txt b/testdata/result/gql_path_pattern/quantified_subpath_lower_default.sql.txt new file mode 100644 index 00000000..43acf65c --- /dev/null +++ b/testdata/result/gql_path_pattern/quantified_subpath_lower_default.sql.txt @@ -0,0 +1,146 @@ +--- quantified_subpath_lower_default.sql +(source_person:Person)((p:Person)-[k:Knows]->(f:Person)){, 4}(dest_person:Person) +--- AST +&ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Rparen: 21, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 1, + NameEnd: 14, + Name: "source_person", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 14, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 15, + NameEnd: 21, + Name: "Person", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLSubpathPattern{ + Lparen: 22, + Rparen: 55, + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 23, + Rparen: 32, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 24, + NameEnd: 25, + Name: "p", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 25, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 26, + NameEnd: 32, + Name: "Person", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 33, + Lbrack: 34, + Rbrack: 42, + Arrow: 43, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 35, + NameEnd: 36, + Name: "k", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 36, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 37, + NameEnd: 42, + Name: "Knows", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 45, + Rparen: 54, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 46, + NameEnd: 47, + Name: "f", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 47, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 48, + NameEnd: 54, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, + }, + }, + Quantifier: &ast.GQLBoundedQuantifier{ + Lbrace: 56, + Rbrace: 60, + UpperBound: &ast.IntLiteral{ + ValuePos: 59, + ValueEnd: 60, + Base: 10, + Value: "4", + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 61, + Rparen: 80, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 62, + NameEnd: 73, + Name: "dest_person", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 73, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 74, + NameEnd: 80, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +(source_person:Person)((p:Person)-[k:Knows]->(f:Person)){,4}(dest_person:Person) diff --git a/testdata/result/query/select_from_graph_table.sql.txt b/testdata/result/query/select_from_graph_table.sql.txt new file mode 100644 index 00000000..8864740f --- /dev/null +++ b/testdata/result/query/select_from_graph_table.sql.txt @@ -0,0 +1,59 @@ +--- select_from_graph_table.sql +SELECT * FROM GRAPH_TABLE(FinGraph RETURN 1 AS a) +--- AST +&ast.QueryStatement{ + Query: &ast.Select{ + Results: []ast.SelectItem{ + &ast.Star{ + Star: 7, + }, + }, + From: &ast.From{ + From: 9, + Source: &ast.GraphTableExpr{ + GraphTable: 14, + PropertyGraphName: &ast.Ident{ + NamePos: 26, + NameEnd: 34, + Name: "FinGraph", + }, + Lparen: 25, + Rparen: 48, + Query: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLReturnStatement{ + Return: 35, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.IntLiteral{ + ValuePos: 42, + ValueEnd: 43, + Base: 10, + Value: "1", + }, + As: &ast.AsAlias{ + As: 44, + Alias: &ast.Ident{ + NamePos: 47, + NameEnd: 48, + Name: "a", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +SELECT * FROM GRAPH_TABLE(FinGraph RETURN 1 AS a) diff --git a/testdata/result/query/select_in_gql_subquery.sql.txt b/testdata/result/query/select_in_gql_subquery.sql.txt new file mode 100644 index 00000000..e446eb6e --- /dev/null +++ b/testdata/result/query/select_in_gql_subquery.sql.txt @@ -0,0 +1,163 @@ +--- select_in_gql_subquery.sql +SELECT 'Dana' IN { + GRAPH FinGraph + MATCH (p:Person)-[o:Owns]->(a:Account) + RETURN p.name +} AS results +--- AST +&ast.QueryStatement{ + Query: &ast.Select{ + Results: []ast.SelectItem{ + &ast.Alias{ + Expr: &ast.InExpr{ + Left: &ast.StringLiteral{ + ValuePos: 7, + ValueEnd: 13, + Value: "Dana", + }, + Right: &ast.GQLSubQueryInCondition{ + Lbrace: 17, + Rbrace: 93, + Query: &ast.GQLQueryExpr{ + GraphClause: &ast.GQLGraphClause{ + Graph: 21, + PropertyGraphName: &ast.Ident{ + NamePos: 27, + NameEnd: 35, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 38, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 44, + Rparen: 53, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 45, + NameEnd: 46, + Name: "p", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 46, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 47, + NameEnd: 53, + Name: "Person", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 54, + Lbrack: 55, + Rbrack: 62, + Arrow: 63, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 56, + NameEnd: 57, + Name: "o", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 57, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 58, + NameEnd: 62, + Name: "Owns", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 65, + Rparen: 75, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 66, + NameEnd: 67, + Name: "a", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 67, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 68, + NameEnd: 75, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 79, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 86, + NameEnd: 87, + Name: "p", + }, + &ast.Ident{ + NamePos: 88, + NameEnd: 92, + Name: "name", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 95, + Alias: &ast.Ident{ + NamePos: 98, + NameEnd: 105, + Name: "results", + }, + }, + }, + }, + }, +} + +--- SQL +SELECT "Dana" IN {GRAPH FinGraph MATCH (p:Person)-[o:Owns]->(a:Account) RETURN p.name} AS results diff --git a/testdata/result/statement/select_from_graph_table.sql.txt b/testdata/result/statement/select_from_graph_table.sql.txt new file mode 100644 index 00000000..8864740f --- /dev/null +++ b/testdata/result/statement/select_from_graph_table.sql.txt @@ -0,0 +1,59 @@ +--- select_from_graph_table.sql +SELECT * FROM GRAPH_TABLE(FinGraph RETURN 1 AS a) +--- AST +&ast.QueryStatement{ + Query: &ast.Select{ + Results: []ast.SelectItem{ + &ast.Star{ + Star: 7, + }, + }, + From: &ast.From{ + From: 9, + Source: &ast.GraphTableExpr{ + GraphTable: 14, + PropertyGraphName: &ast.Ident{ + NamePos: 26, + NameEnd: 34, + Name: "FinGraph", + }, + Lparen: 25, + Rparen: 48, + Query: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLReturnStatement{ + Return: 35, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.Alias{ + Expr: &ast.IntLiteral{ + ValuePos: 42, + ValueEnd: 43, + Base: 10, + Value: "1", + }, + As: &ast.AsAlias{ + As: 44, + Alias: &ast.Ident{ + NamePos: 47, + NameEnd: 48, + Name: "a", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +SELECT * FROM GRAPH_TABLE(FinGraph RETURN 1 AS a) diff --git a/testdata/result/statement/select_in_gql_subquery.sql.txt b/testdata/result/statement/select_in_gql_subquery.sql.txt new file mode 100644 index 00000000..e446eb6e --- /dev/null +++ b/testdata/result/statement/select_in_gql_subquery.sql.txt @@ -0,0 +1,163 @@ +--- select_in_gql_subquery.sql +SELECT 'Dana' IN { + GRAPH FinGraph + MATCH (p:Person)-[o:Owns]->(a:Account) + RETURN p.name +} AS results +--- AST +&ast.QueryStatement{ + Query: &ast.Select{ + Results: []ast.SelectItem{ + &ast.Alias{ + Expr: &ast.InExpr{ + Left: &ast.StringLiteral{ + ValuePos: 7, + ValueEnd: 13, + Value: "Dana", + }, + Right: &ast.GQLSubQueryInCondition{ + Lbrace: 17, + Rbrace: 93, + Query: &ast.GQLQueryExpr{ + GraphClause: &ast.GQLGraphClause{ + Graph: 21, + PropertyGraphName: &ast.Ident{ + NamePos: 27, + NameEnd: 35, + Name: "FinGraph", + }, + }, + MultiLinearQueryStatement: &ast.GQLMultiLinearQueryStatement{ + LinearQueryStatementList: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + PrimitiveQueryStatementList: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatchStatement{ + Optional: -1, + Match: 38, + GraphPattern: &ast.GQLGraphPattern{ + PathPatternList: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + PathPattern: &ast.GQLPathPattern{ + PathTermList: []*ast.GQLQuantifiablePathTerm{ + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 44, + Rparen: 53, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 45, + NameEnd: 46, + Name: "p", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 46, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 47, + NameEnd: 53, + Name: "Person", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLFullEdgeRight{ + Hyphen: 54, + Lbrack: 55, + Rbrack: 62, + Arrow: 63, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 56, + NameEnd: 57, + Name: "o", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 57, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 58, + NameEnd: 62, + Name: "Owns", + }, + }, + }, + }, + }, + }, + &ast.GQLQuantifiablePathTerm{ + PathTerm: &ast.GQLNodePattern{ + Lparen: 65, + Rparen: 75, + PatternFiller: &ast.GQLPatternFiller{ + GraphPatternVariable: &ast.Ident{ + NamePos: 66, + NameEnd: 67, + Name: "a", + }, + IsLabelCondition: &ast.GQLIsLabelCondition{ + IsOrColon: 67, + LabelExpression: &ast.GQLElementLabel{ + LabelName: &ast.Ident{ + NamePos: 68, + NameEnd: 75, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturnStatement{ + Return: 79, + ReturnItemList: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Item: &ast.ExprSelectItem{ + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 86, + NameEnd: 87, + Name: "p", + }, + &ast.Ident{ + NamePos: 88, + NameEnd: 92, + Name: "name", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 95, + Alias: &ast.Ident{ + NamePos: 98, + NameEnd: 105, + Name: "results", + }, + }, + }, + }, + }, +} + +--- SQL +SELECT "Dana" IN {GRAPH FinGraph MATCH (p:Person)-[o:Owns]->(a:Account) RETURN p.name} AS results diff --git a/tools/parse/main.go b/tools/parse/main.go index 0667894f..a647c20e 100644 --- a/tools/parse/main.go +++ b/tools/parse/main.go @@ -89,6 +89,10 @@ func main() { node, err = p.ParseDDL() case "dml": node, err = p.ParseDML() + case "gql": + node, err = p.ParseGQLStatement() + case "gql_path_pattern": + node, err = p.ParseGQLPathPattern() default: log.Fatalf("unknown mode: %s", *mode) }