diff --git a/ast/ast.go b/ast/ast.go index 51e4b471..2f61d92f 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -230,6 +230,9 @@ func (ParenExpr) isExpr() {} func (ScalarSubQuery) isExpr() {} func (ArraySubQuery) isExpr() {} func (ExistsSubQuery) isExpr() {} +func (ExistsGQLSubQuery) isExpr() {} +func (ArrayGQLSubQuery) isExpr() {} +func (ValueGQLSubQuery) isExpr() {} func (Param) isExpr() {} func (Ident) isExpr() {} func (Path) isExpr() {} @@ -305,9 +308,10 @@ type InCondition interface { isInCondition() } -func (UnnestInCondition) isInCondition() {} -func (SubQueryInCondition) isInCondition() {} -func (ValuesInCondition) isInCondition() {} +func (UnnestInCondition) isInCondition() {} +func (SubQueryInCondition) isInCondition() {} +func (GQLSubQueryInCondition) isInCondition() {} +func (ValuesInCondition) isInCondition() {} // TypelessStructLiteralArg represents an argument of typeless STRUCT literals. type TypelessStructLiteralArg interface { @@ -1902,6 +1906,74 @@ type ExistsSubQuery struct { Query QueryExpr } +// GQLExistsContent is the body of EXISTS { ... }. +type GQLExistsContent interface { + Node + isGQLExistsContent() +} + +func (GQLMultiLinearQueryStatement) isGQLExistsContent() {} +func (GQLMatch) isGQLExistsContent() {} +func (GQLGraphPattern) isGQLExistsContent() {} + +// ExistsGQLSubQuery is EXISTS { ... } GQL subquery expression. +// +// EXISTS {{.Hint | sqlOpt}} { {{.GraphClause | sqlOpt}} {{.Query | sql}} } +type ExistsGQLSubQuery struct { + // pos = Exists + // end = Rbrace + 1 + + Exists token.Pos // position of "EXISTS" + Rbrace token.Pos // position of "}" + + Hint *Hint // optional + GraphClause *GQLGraphClause // optional + Query GQLExistsContent +} + +// ArrayGQLSubQuery is ARRAY { gql_query_expr } expression. +// +// ARRAY { {{.GraphClause | sqlOpt}} {{.Query | sql}} } +type ArrayGQLSubQuery struct { + // pos = Array + // end = Rbrace + 1 + + Array token.Pos // position of "ARRAY" + Rbrace token.Pos // position of "}" + + GraphClause *GQLGraphClause // optional + Query *GQLMultiLinearQueryStatement +} + +// ValueGQLSubQuery is VALUE { gql_query_expr } expression. +// +// VALUE {{.Hint | sqlOpt}} { {{.GraphClause | sqlOpt}} {{.Query | sql}} } +type ValueGQLSubQuery struct { + // pos = Value + // end = Rbrace + 1 + + Value token.Pos // position of "VALUE" + Rbrace token.Pos // position of "}" + + Hint *Hint // optional + GraphClause *GQLGraphClause // optional + Query *GQLMultiLinearQueryStatement +} + +// GQLSubQueryInCondition is IN { gql_query_expr } condition. +// +// { {{.GraphClause | sqlOpt}} {{.Query | sql}} } +type GQLSubQueryInCondition struct { + // pos = Lbrace + // end = Rbrace + 1 + + Lbrace token.Pos // position of "{" + Rbrace token.Pos // position of "}" + + GraphClause *GQLGraphClause // optional + Query *GQLMultiLinearQueryStatement +} + // ================================================================================ // // Literal diff --git a/ast/expr_impls_test.go b/ast/expr_impls_test.go index 70606da0..ecfa99e7 100644 --- a/ast/expr_impls_test.go +++ b/ast/expr_impls_test.go @@ -32,6 +32,9 @@ var exprPrecTestCases = []exprPrecTestCase{ {name: "ScalarSubQuery", expr: &ScalarSubQuery{}}, {name: "ArraySubQuery", expr: &ArraySubQuery{}}, {name: "ExistsSubQuery", expr: &ExistsSubQuery{}}, + {name: "ExistsGQLSubQuery", expr: &ExistsGQLSubQuery{}}, + {name: "ArrayGQLSubQuery", expr: &ArrayGQLSubQuery{}}, + {name: "ValueGQLSubQuery", expr: &ValueGQLSubQuery{}}, {name: "Param", expr: &Param{}}, {name: "Ident", expr: &Ident{}}, {name: "Path", expr: &Path{}}, diff --git a/ast/pos.go b/ast/pos.go index 31ea4460..34803109 100644 --- a/ast/pos.go +++ b/ast/pos.go @@ -750,6 +750,38 @@ func (e *ExistsSubQuery) End() token.Pos { return posAdd(e.Rparen, 1) } +func (e *ExistsGQLSubQuery) Pos() token.Pos { + return e.Exists +} + +func (e *ExistsGQLSubQuery) End() token.Pos { + return posAdd(e.Rbrace, 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.Value +} + +func (v *ValueGQLSubQuery) End() token.Pos { + return posAdd(v.Rbrace, 1) +} + +func (g *GQLSubQueryInCondition) Pos() token.Pos { + return g.Lbrace +} + +func (g *GQLSubQueryInCondition) End() token.Pos { + return posAdd(g.Rbrace, 1) +} + func (p *Param) Pos() token.Pos { return p.Atmark } diff --git a/ast/sql.go b/ast/sql.go index 8a4f7ed9..e025763d 100644 --- a/ast/sql.go +++ b/ast/sql.go @@ -106,7 +106,7 @@ const ( func exprPrec(e Expr) prec { switch e := e.(type) { case *BadExpr, *CallExpr, *CountStarExpr, *CastExpr, *ExtractExpr, *ReplaceFieldsExpr, *CaseExpr, *IfExpr, *ParenExpr, *ScalarSubQuery, - *ArraySubQuery, *ExistsSubQuery, *Param, *Ident, *Path, *ArrayLiteral, *TupleStructLiteral, *TypedStructLiteral, + *ArraySubQuery, *ExistsSubQuery, *ExistsGQLSubQuery, *ArrayGQLSubQuery, *ValueGQLSubQuery, *Param, *Ident, *Path, *ArrayLiteral, *TupleStructLiteral, *TypedStructLiteral, *TypelessStructLiteral, *NullLiteral, *BoolLiteral, *IntLiteral, *FloatLiteral, *StringLiteral, *BytesLiteral, *DateLiteral, *TimestampLiteral, *NumericLiteral, *JSONLiteral, *IntervalLiteralSingle, *IntervalLiteralRange, *NewConstructor, *BracedNewConstructor, *BracedConstructor, *WithExpr: @@ -608,6 +608,22 @@ func (e *ExistsSubQuery) SQL() string { "(" + e.Query.SQL() + ")" } +func (e *ExistsGQLSubQuery) SQL() string { + return "EXISTS" + sqlOpt(" ", e.Hint, "") + " { " + sqlOpt("", e.GraphClause, " ") + e.Query.SQL() + " }" +} + +func (a *ArrayGQLSubQuery) SQL() string { + return "ARRAY { " + sqlOpt("", a.GraphClause, " ") + a.Query.SQL() + " }" +} + +func (v *ValueGQLSubQuery) SQL() string { + return "VALUE" + sqlOpt(" ", v.Hint, "") + " { " + sqlOpt("", v.GraphClause, " ") + v.Query.SQL() + " }" +} + +func (g *GQLSubQueryInCondition) SQL() string { + return "{ " + sqlOpt("", g.GraphClause, " ") + g.Query.SQL() + " }" +} + func (p *Param) SQL() string { return "@" + p.Name } diff --git a/ast/walk_internal.go b/ast/walk_internal.go index 8a1daf6e..b3697031 100644 --- a/ast/walk_internal.go +++ b/ast/walk_internal.go @@ -359,6 +359,24 @@ func walkInternal(node Node, v Visitor, stack []*stackItem) []*stackItem { stack = append(stack, &stackItem{node: wrapNode(n.Query), visitor: v.Field("Query")}) stack = append(stack, &stackItem{node: wrapNode(n.Hint), visitor: v.Field("Hint")}) + case *ExistsGQLSubQuery: + stack = append(stack, &stackItem{node: wrapNode(n.Query), visitor: v.Field("Query")}) + 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 *ArrayGQLSubQuery: + stack = append(stack, &stackItem{node: wrapNode(n.Query), visitor: v.Field("Query")}) + stack = append(stack, &stackItem{node: wrapNode(n.GraphClause), visitor: v.Field("GraphClause")}) + + case *ValueGQLSubQuery: + stack = append(stack, &stackItem{node: wrapNode(n.Query), visitor: v.Field("Query")}) + 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 *GQLSubQueryInCondition: + stack = append(stack, &stackItem{node: wrapNode(n.Query), visitor: v.Field("Query")}) + stack = append(stack, &stackItem{node: wrapNode(n.GraphClause), visitor: v.Field("GraphClause")}) + case *Param: // nothing to do diff --git a/parser.go b/parser.go index 59284a71..8df443c8 100644 --- a/parser.go +++ b/parser.go @@ -1735,6 +1735,19 @@ func (p *Parser) parseInCondition() ast.InCondition { } } + if p.Token.Kind == "{" { + lbrace := p.expect("{").Pos + graphClause := p.tryParseGQLGraphClause() + query := p.parseGQLMultiLinearQueryStatement() + rbrace := p.expect("}").Pos + return &ast.GQLSubQueryInCondition{ + Lbrace: lbrace, + Rbrace: rbrace, + GraphClause: graphClause, + Query: query, + } + } + if p.Token.Kind == "(" { lparen := p.Token.Pos p.nextToken() @@ -2039,6 +2052,11 @@ func (p *Parser) parseLit() ast.Expr { return p.parseCastExpr() case id.IsKeywordLike("REPLACE_FIELDS"): return p.parseReplaceFieldsExpr() + case id.IsKeywordLike("VALUE"): + // VALUE is non-reserved, so look ahead for the `VALUE hint? {` form before choosing the GQL subquery parser. + if p.lookaheadValueGQLSubQuery() { + return p.parseValueGQLSubQuery() + } } if p.lookaheadCallExpr() { @@ -2456,18 +2474,113 @@ func (p *Parser) parseCastExpr() *ast.CastExpr { } } -func (p *Parser) parseExistsSubQuery() *ast.ExistsSubQuery { +func (p *Parser) parseExistsSubQuery() ast.Expr { exists := p.expect("EXISTS").Pos + hint := p.tryParseHint() + if p.Token.Kind == "{" { + p.nextToken() + graphClause := p.tryParseGQLGraphClause() + query := p.parseGQLExistsContent() + rbrace := p.expect("}").Pos + return &ast.ExistsGQLSubQuery{ + Exists: exists, + Rbrace: rbrace, + Hint: hint, + GraphClause: graphClause, + Query: query, + } + } p.expect("(") query := p.parseQueryExpr() rparen := p.expect(")").Pos return &ast.ExistsSubQuery{ Exists: exists, Rparen: rparen, + Hint: hint, Query: query, } } +func (p *Parser) parseGQLExistsContent() ast.GQLExistsContent { + switch { + case p.lookaheadGQLPathVariable(): + return p.parseGQLGraphPattern() + case p.Token.IsKeywordLike("MATCH") || p.Token.IsKeywordLike("OPTIONAL"): + // OPTIONAL MATCH or MATCH — if followed by more primitive statements / NEXT / RETURN chain, + // treat as multi-linear; otherwise single match statement form. + return p.parseGQLExistsMatchOrQuery() + case p.Token.Kind == "@": + // A leading traversal hint is invalid, but routing it through the graph-pattern + // parser produces the specific leading-hint diagnostic instead of a generic + // missing-query-statement error. + return p.parseGQLGraphPattern() + case p.Token.Kind == "(" || p.Token.Kind == "-" || p.Token.Kind == "<" || p.Token.Kind == "->", + p.Token.Kind == "ALL" || p.Token.Kind == "ANY", + p.Token.IsKeywordLike("SHORTEST") || p.Token.IsKeywordLike("CHEAPEST"), + p.Token.IsKeywordLike("WALK") || p.Token.IsKeywordLike("TRAIL") || + p.Token.IsKeywordLike("SIMPLE") || p.Token.IsKeywordLike("ACYCLIC"): + return p.parseGQLGraphPattern() + default: + return p.parseGQLMultiLinearQueryStatement() + } +} + +func (p *Parser) parseGQLExistsMatchOrQuery() ast.GQLExistsContent { + // A MATCH can be either the standalone EXISTS form or the first statement of + // a query. Reparse the latter through the normal query path to keep this + // ambiguity handling separate from query parsing and recovery. + lexer := p.cloneLexer() + errorCount := len(p.errors) + match := p.parseGQLMatch() + if p.Token.Kind == "}" { + return match + } + p.Lexer = lexer + p.errors = p.errors[:errorCount] + return p.parseGQLMultiLinearQueryStatement() +} + +func (p *Parser) lookaheadGQLPathVariable() bool { + if p.Token.Kind != token.TokenIdent { + return false + } + + lexer := p.cloneLexer() + lexer.nextToken(false) + return lexer.Token.Kind == "=" +} + +func (p *Parser) lookaheadValueGQLSubQuery() bool { + lexer := p.cloneLexer() + errorCount := len(p.errors) + defer func() { + p.Lexer = lexer + p.errors = p.errors[:errorCount] + }() + if !p.Token.IsKeywordLike("VALUE") { + return false + } + p.nextToken() + p.tryParseHint() + return p.Token.Kind == "{" +} + +func (p *Parser) parseValueGQLSubQuery() *ast.ValueGQLSubQuery { + value := p.expectKeywordLike("VALUE").Pos + hint := p.tryParseHint() + p.expect("{") + graphClause := p.tryParseGQLGraphClause() + query := p.parseGQLMultiLinearQueryStatement() + rbrace := p.expect("}").Pos + return &ast.ValueGQLSubQuery{ + Value: value, + Rbrace: rbrace, + Hint: hint, + GraphClause: graphClause, + Query: query, + } +} + func (p *Parser) parseExtractExpr() *ast.ExtractExpr { extract := p.expect("EXTRACT").Pos p.expect("(") @@ -2591,6 +2704,19 @@ func (p *Parser) parseParenExpr() ast.Expr { func (p *Parser) parseArrayLiteralOrSubQuery() ast.Expr { pos := p.expect("ARRAY").Pos + if p.Token.Kind == "{" { + p.nextToken() + graphClause := p.tryParseGQLGraphClause() + query := p.parseGQLMultiLinearQueryStatement() + rbrace := p.expect("}").Pos + return &ast.ArrayGQLSubQuery{ + Array: pos, + Rbrace: rbrace, + GraphClause: graphClause, + Query: query, + } + } + if p.Token.Kind == "(" { p.nextToken() query := p.parseQueryExpr() @@ -6942,6 +7068,13 @@ func (p *Parser) parseGQLGraphClause() *ast.GQLGraphClause { } } +func (p *Parser) tryParseGQLGraphClause() *ast.GQLGraphClause { + if !p.Token.IsKeywordLike("GRAPH") { + return nil + } + return p.parseGQLGraphClause() +} + func (p *Parser) parseGQLMultiLinearQueryStatement() *ast.GQLMultiLinearQueryStatement { var stmts []ast.GQLLinearQueryStatement stmts = append(stmts, p.parseGQLLinearQueryStatement()) diff --git a/testdata/input/dml/update_in_braced_gql_graph_clause.sql b/testdata/input/dml/update_in_braced_gql_graph_clause.sql new file mode 100644 index 00000000..6c66c456 --- /dev/null +++ b/testdata/input/dml/update_in_braced_gql_graph_clause.sql @@ -0,0 +1,7 @@ +UPDATE Account +SET is_active = TRUE +WHERE id IN { + GRAPH FinGraph + MATCH (a) + RETURN a.id +} diff --git a/testdata/input/expr/gql_braced_ambiguity_array_paren.sql b/testdata/input/expr/gql_braced_ambiguity_array_paren.sql new file mode 100644 index 00000000..31fd2c59 --- /dev/null +++ b/testdata/input/expr/gql_braced_ambiguity_array_paren.sql @@ -0,0 +1,2 @@ +-- Regression: ARRAY(...) SQL subquery must still parse after ARRAY { gql } support. +ARRAY(SELECT 1) \ No newline at end of file diff --git a/testdata/input/expr/gql_braced_ambiguity_exists_paren.sql b/testdata/input/expr/gql_braced_ambiguity_exists_paren.sql new file mode 100644 index 00000000..2d0088b3 --- /dev/null +++ b/testdata/input/expr/gql_braced_ambiguity_exists_paren.sql @@ -0,0 +1,2 @@ +-- Regression: EXISTS(...) SQL subquery must still parse after EXISTS { gql } support. +EXISTS @{JOIN_METHOD=HASH_JOIN} (SELECT 1) diff --git a/testdata/input/expr/gql_braced_ambiguity_in_paren.sql b/testdata/input/expr/gql_braced_ambiguity_in_paren.sql new file mode 100644 index 00000000..4a942586 --- /dev/null +++ b/testdata/input/expr/gql_braced_ambiguity_in_paren.sql @@ -0,0 +1,2 @@ +-- Regression: IN (list) must still parse after IN { gql } support. +1 IN (1, 2, 3) \ No newline at end of file diff --git a/testdata/input/expr/gql_braced_ambiguity_in_unnest.sql b/testdata/input/expr/gql_braced_ambiguity_in_unnest.sql new file mode 100644 index 00000000..abd9454e --- /dev/null +++ b/testdata/input/expr/gql_braced_ambiguity_in_unnest.sql @@ -0,0 +1,2 @@ +-- Regression: IN UNNEST(...) must still parse after IN { gql } support. +1 IN UNNEST([1, 2, 3]) \ No newline at end of file diff --git a/testdata/input/expr/gql_braced_ambiguity_new_braced.sql b/testdata/input/expr/gql_braced_ambiguity_new_braced.sql new file mode 100644 index 00000000..8a303a87 --- /dev/null +++ b/testdata/input/expr/gql_braced_ambiguity_new_braced.sql @@ -0,0 +1,2 @@ +-- Regression: NEW Type { ... } braced constructor must not be confused with braced gql subqueries. +NEW pkg.Foo {a: 1} \ No newline at end of file diff --git a/testdata/input/expr/gql_braced_ambiguity_not_in_paren.sql b/testdata/input/expr/gql_braced_ambiguity_not_in_paren.sql new file mode 100644 index 00000000..e3e28436 --- /dev/null +++ b/testdata/input/expr/gql_braced_ambiguity_not_in_paren.sql @@ -0,0 +1,2 @@ +-- Regression: NOT IN (list) must still parse after IN { gql } support. +1 NOT IN (1, 2) \ No newline at end of file diff --git a/testdata/input/expr/gql_braced_ambiguity_value_ident.sql b/testdata/input/expr/gql_braced_ambiguity_value_ident.sql new file mode 100644 index 00000000..92c278b6 --- /dev/null +++ b/testdata/input/expr/gql_braced_ambiguity_value_ident.sql @@ -0,0 +1,2 @@ +-- Regression: VALUE used as an identifier must not be hijacked by VALUE { gql } lookahead. +value IN (1, 2) \ No newline at end of file diff --git a/testdata/input/gql/!bad_exists_braced_match_continuation.sql b/testdata/input/gql/!bad_exists_braced_match_continuation.sql new file mode 100644 index 00000000..4c55dd14 --- /dev/null +++ b/testdata/input/gql/!bad_exists_braced_match_continuation.sql @@ -0,0 +1,5 @@ +GRAPH FinGraph +RETURN EXISTS { + MATCH (a) + FILTER +} diff --git a/testdata/input/gql/!bad_exists_braced_match_prefix_error.sql b/testdata/input/gql/!bad_exists_braced_match_prefix_error.sql new file mode 100644 index 00000000..a5c2e8a1 --- /dev/null +++ b/testdata/input/gql/!bad_exists_braced_match_prefix_error.sql @@ -0,0 +1,5 @@ +GRAPH FinGraph +RETURN EXISTS { + MATCH (a WHERE EXISTS { MATCH (b) RETURN }) + RETURN 1 +} diff --git a/testdata/input/gql/!bad_value_braced_hint.sql b/testdata/input/gql/!bad_value_braced_hint.sql new file mode 100644 index 00000000..a807b354 --- /dev/null +++ b/testdata/input/gql/!bad_value_braced_hint.sql @@ -0,0 +1,2 @@ +GRAPH FinGraph +RETURN VALUE @{x=} { RETURN 1 } diff --git a/testdata/input/gql/array_braced.sql b/testdata/input/gql/array_braced.sql new file mode 100644 index 00000000..13083da0 --- /dev/null +++ b/testdata/input/gql/array_braced.sql @@ -0,0 +1,7 @@ +GRAPH FinGraph +MATCH (p:Person)-[:Owns]->(account:Account) +RETURN p.name, ARRAY { + MATCH (a:Account)-[transfer:Transfers]->(:Account) + WHERE a = account + RETURN transfer.amount AS transfers +} AS transfers diff --git a/testdata/input/gql/braced_subqueries_graph_clause.sql b/testdata/input/gql/braced_subqueries_graph_clause.sql new file mode 100644 index 00000000..c5ea896b --- /dev/null +++ b/testdata/input/gql/braced_subqueries_graph_clause.sql @@ -0,0 +1,9 @@ +GRAPH FinGraph +MATCH (n) +RETURN + EXISTS { GRAPH FinGraph MATCH (m) RETURN m } AS has_match, + EXISTS { MATCH (m) RETURN m UNION ALL RETURN m } AS compound_match, + EXISTS { MATCH (m) RETURN m NEXT RETURN m } AS chained_match, + ARRAY { GRAPH FinGraph MATCH (m) RETURN m } AS matches, + VALUE { GRAPH FinGraph MATCH (m) RETURN m } AS value, + n IN { GRAPH FinGraph MATCH (m) RETURN m } AS contained diff --git a/testdata/input/gql/braced_subqueries_hints_and_path_modes.sql b/testdata/input/gql/braced_subqueries_hints_and_path_modes.sql new file mode 100644 index 00000000..a07446b0 --- /dev/null +++ b/testdata/input/gql/braced_subqueries_hints_and_path_modes.sql @@ -0,0 +1,5 @@ +GRAPH FinGraph +RETURN + EXISTS @{JOIN_METHOD=HASH_JOIN} { ANY SHORTEST (a)-[]->(b) } AS any_shortest, + EXISTS { TRAIL PATH (a)-[]->(b) } AS trail_path, + VALUE @{a=1} { RETURN 1 AS value } AS hinted_value diff --git a/testdata/input/gql/exists_braced_match.sql b/testdata/input/gql/exists_braced_match.sql new file mode 100644 index 00000000..52b32591 --- /dev/null +++ b/testdata/input/gql/exists_braced_match.sql @@ -0,0 +1,5 @@ +GRAPH FinGraph +MATCH (p:Person) +RETURN EXISTS { + MATCH (p)-[:Owns]->(a:Account) +} AS has_account diff --git a/testdata/input/gql/exists_braced_path_variable.sql b/testdata/input/gql/exists_braced_path_variable.sql new file mode 100644 index 00000000..5c462d55 --- /dev/null +++ b/testdata/input/gql/exists_braced_path_variable.sql @@ -0,0 +1,4 @@ +GRAPH FinGraph +RETURN + EXISTS { p = (a)-[e]->(b) } AS local_graph, + EXISTS { GRAPH FinGraph p = (a)-[e]->(b) } AS explicit_graph diff --git a/testdata/input/gql/exists_braced_pattern.sql b/testdata/input/gql/exists_braced_pattern.sql new file mode 100644 index 00000000..2bc1e341 --- /dev/null +++ b/testdata/input/gql/exists_braced_pattern.sql @@ -0,0 +1,3 @@ +GRAPH FinGraph +MATCH (p:Person) +RETURN EXISTS { (p)-[:Owns]->(:Account) } AS has_account diff --git a/testdata/input/gql/exists_braced_query.sql b/testdata/input/gql/exists_braced_query.sql new file mode 100644 index 00000000..02f53baa --- /dev/null +++ b/testdata/input/gql/exists_braced_query.sql @@ -0,0 +1,6 @@ +GRAPH FinGraph +MATCH (p:Person) +RETURN EXISTS { + MATCH (p)-[:Owns]->(a:Account) + RETURN a.id +} AS has_account diff --git a/testdata/input/gql/in_braced.sql b/testdata/input/gql/in_braced.sql new file mode 100644 index 00000000..f4a0ef75 --- /dev/null +++ b/testdata/input/gql/in_braced.sql @@ -0,0 +1,7 @@ +GRAPH FinGraph +MATCH (p:Person) +RETURN p.name IN { + MATCH (x:Person) + WHERE x.id < 10 + RETURN x.name +} AS flag diff --git a/testdata/input/gql/not_in_braced.sql b/testdata/input/gql/not_in_braced.sql new file mode 100644 index 00000000..6e4ae1bd --- /dev/null +++ b/testdata/input/gql/not_in_braced.sql @@ -0,0 +1,6 @@ +GRAPH FinGraph +MATCH (p:Person) +RETURN p.name NOT IN { + MATCH (x:Person) + RETURN x.name +} AS flag diff --git a/testdata/input/gql/value_braced.sql b/testdata/input/gql/value_braced.sql new file mode 100644 index 00000000..3f6a6526 --- /dev/null +++ b/testdata/input/gql/value_braced.sql @@ -0,0 +1,7 @@ +GRAPH FinGraph +MATCH (p:Person) +RETURN VALUE { + MATCH (p)-[:Owns]->(a:Account) + RETURN a.id AS id + LIMIT 1 +} AS account_id diff --git a/testdata/result/dml/update_in_braced_gql_graph_clause.sql.txt b/testdata/result/dml/update_in_braced_gql_graph_clause.sql.txt new file mode 100644 index 00000000..aec77de1 --- /dev/null +++ b/testdata/result/dml/update_in_braced_gql_graph_clause.sql.txt @@ -0,0 +1,126 @@ +--- update_in_braced_gql_graph_clause.sql +UPDATE Account +SET is_active = TRUE +WHERE id IN { + GRAPH FinGraph + MATCH (a) + RETURN a.id +} + +--- AST +&ast.Update{ + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 7, + NameEnd: 14, + Name: "Account", + }, + }, + }, + Updates: []ast.UpdateItem{ + &ast.UpdateItemSetValue{ + Path: []*ast.Ident{ + &ast.Ident{ + NamePos: 19, + NameEnd: 28, + Name: "is_active", + }, + }, + DefaultExpr: &ast.DefaultExpr{ + DefaultPos: -1, + Expr: &ast.BoolLiteral{ + ValuePos: 31, + Value: true, + }, + }, + }, + }, + Where: &ast.Where{ + Where: 36, + Expr: &ast.InExpr{ + Left: &ast.Ident{ + NamePos: 42, + NameEnd: 44, + Name: "id", + }, + Right: &ast.GQLSubQueryInCondition{ + Lbrace: 48, + Rbrace: 93, + GraphClause: &ast.GQLGraphClause{ + Graph: 52, + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 58, + NameEnd: 66, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 69, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 75, + Rparen: 77, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 76, + NameEnd: 77, + Name: "a", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 81, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 88, + NameEnd: 89, + Name: "a", + }, + &ast.Ident{ + NamePos: 90, + NameEnd: 92, + Name: "id", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +UPDATE Account SET is_active = TRUE WHERE id IN { GRAPH FinGraph MATCH (a) RETURN a.id } diff --git a/testdata/result/expr/gql_braced_ambiguity_array_paren.sql.txt b/testdata/result/expr/gql_braced_ambiguity_array_paren.sql.txt new file mode 100644 index 00000000..a561272c --- /dev/null +++ b/testdata/result/expr/gql_braced_ambiguity_array_paren.sql.txt @@ -0,0 +1,24 @@ +--- gql_braced_ambiguity_array_paren.sql +-- Regression: ARRAY(...) SQL subquery must still parse after ARRAY { gql } support. +ARRAY(SELECT 1) +--- AST +&ast.ArraySubQuery{ + Array: 85, + Rparen: 99, + Query: &ast.Select{ + Select: 91, + Results: []ast.SelectItem{ + &ast.ExprSelectItem{ + Expr: &ast.IntLiteral{ + ValuePos: 98, + ValueEnd: 99, + Base: 10, + Value: "1", + }, + }, + }, + }, +} + +--- SQL +ARRAY(SELECT 1) diff --git a/testdata/result/expr/gql_braced_ambiguity_exists_paren.sql.txt b/testdata/result/expr/gql_braced_ambiguity_exists_paren.sql.txt new file mode 100644 index 00000000..ef0a2fde --- /dev/null +++ b/testdata/result/expr/gql_braced_ambiguity_exists_paren.sql.txt @@ -0,0 +1,47 @@ +--- gql_braced_ambiguity_exists_paren.sql +-- Regression: EXISTS(...) SQL subquery must still parse after EXISTS { gql } support. +EXISTS @{JOIN_METHOD=HASH_JOIN} (SELECT 1) + +--- AST +&ast.ExistsSubQuery{ + Exists: 87, + Rparen: 128, + Hint: &ast.Hint{ + Atmark: 94, + Rbrace: 117, + Records: []*ast.HintRecord{ + &ast.HintRecord{ + Key: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 96, + NameEnd: 107, + Name: "JOIN_METHOD", + }, + }, + }, + Value: &ast.Ident{ + NamePos: 108, + NameEnd: 117, + Name: "HASH_JOIN", + }, + }, + }, + }, + Query: &ast.Select{ + Select: 120, + Results: []ast.SelectItem{ + &ast.ExprSelectItem{ + Expr: &ast.IntLiteral{ + ValuePos: 127, + ValueEnd: 128, + Base: 10, + Value: "1", + }, + }, + }, + }, +} + +--- SQL +EXISTS @{JOIN_METHOD=HASH_JOIN} (SELECT 1) diff --git a/testdata/result/expr/gql_braced_ambiguity_in_paren.sql.txt b/testdata/result/expr/gql_braced_ambiguity_in_paren.sql.txt new file mode 100644 index 00000000..0dbf6962 --- /dev/null +++ b/testdata/result/expr/gql_braced_ambiguity_in_paren.sql.txt @@ -0,0 +1,39 @@ +--- gql_braced_ambiguity_in_paren.sql +-- Regression: IN (list) must still parse after IN { gql } support. +1 IN (1, 2, 3) +--- AST +&ast.InExpr{ + Left: &ast.IntLiteral{ + ValuePos: 68, + ValueEnd: 69, + Base: 10, + Value: "1", + }, + Right: &ast.ValuesInCondition{ + Lparen: 73, + Rparen: 81, + Exprs: []ast.Expr{ + &ast.IntLiteral{ + ValuePos: 74, + ValueEnd: 75, + Base: 10, + Value: "1", + }, + &ast.IntLiteral{ + ValuePos: 77, + ValueEnd: 78, + Base: 10, + Value: "2", + }, + &ast.IntLiteral{ + ValuePos: 80, + ValueEnd: 81, + Base: 10, + Value: "3", + }, + }, + }, +} + +--- SQL +1 IN (1, 2, 3) diff --git a/testdata/result/expr/gql_braced_ambiguity_in_unnest.sql.txt b/testdata/result/expr/gql_braced_ambiguity_in_unnest.sql.txt new file mode 100644 index 00000000..4ae08762 --- /dev/null +++ b/testdata/result/expr/gql_braced_ambiguity_in_unnest.sql.txt @@ -0,0 +1,44 @@ +--- gql_braced_ambiguity_in_unnest.sql +-- Regression: IN UNNEST(...) must still parse after IN { gql } support. +1 IN UNNEST([1, 2, 3]) +--- AST +&ast.InExpr{ + Left: &ast.IntLiteral{ + ValuePos: 73, + ValueEnd: 74, + Base: 10, + Value: "1", + }, + Right: &ast.UnnestInCondition{ + Unnest: 78, + Rparen: 94, + Expr: &ast.ArrayLiteral{ + Array: -1, + Lbrack: 85, + Rbrack: 93, + Values: []ast.Expr{ + &ast.IntLiteral{ + ValuePos: 86, + ValueEnd: 87, + Base: 10, + Value: "1", + }, + &ast.IntLiteral{ + ValuePos: 89, + ValueEnd: 90, + Base: 10, + Value: "2", + }, + &ast.IntLiteral{ + ValuePos: 92, + ValueEnd: 93, + Base: 10, + Value: "3", + }, + }, + }, + }, +} + +--- SQL +1 IN UNNEST([1, 2, 3]) diff --git a/testdata/result/expr/gql_braced_ambiguity_new_braced.sql.txt b/testdata/result/expr/gql_braced_ambiguity_new_braced.sql.txt new file mode 100644 index 00000000..199f532d --- /dev/null +++ b/testdata/result/expr/gql_braced_ambiguity_new_braced.sql.txt @@ -0,0 +1,46 @@ +--- gql_braced_ambiguity_new_braced.sql +-- Regression: NEW Type { ... } braced constructor must not be confused with braced gql subqueries. +NEW pkg.Foo {a: 1} +--- AST +&ast.BracedNewConstructor{ + New: 100, + Type: &ast.NamedType{ + Path: []*ast.Ident{ + &ast.Ident{ + NamePos: 104, + NameEnd: 107, + Name: "pkg", + }, + &ast.Ident{ + NamePos: 108, + NameEnd: 111, + Name: "Foo", + }, + }, + }, + Body: &ast.BracedConstructor{ + Lbrace: 112, + Rbrace: 117, + Fields: []*ast.BracedConstructorField{ + &ast.BracedConstructorField{ + Name: &ast.Ident{ + NamePos: 113, + NameEnd: 114, + Name: "a", + }, + Value: &ast.BracedConstructorFieldValueExpr{ + Colon: 114, + Expr: &ast.IntLiteral{ + ValuePos: 116, + ValueEnd: 117, + Base: 10, + Value: "1", + }, + }, + }, + }, + }, +} + +--- SQL +NEW pkg.Foo {a: 1} diff --git a/testdata/result/expr/gql_braced_ambiguity_not_in_paren.sql.txt b/testdata/result/expr/gql_braced_ambiguity_not_in_paren.sql.txt new file mode 100644 index 00000000..4256c880 --- /dev/null +++ b/testdata/result/expr/gql_braced_ambiguity_not_in_paren.sql.txt @@ -0,0 +1,34 @@ +--- gql_braced_ambiguity_not_in_paren.sql +-- Regression: NOT IN (list) must still parse after IN { gql } support. +1 NOT IN (1, 2) +--- AST +&ast.InExpr{ + Not: true, + Left: &ast.IntLiteral{ + ValuePos: 72, + ValueEnd: 73, + Base: 10, + Value: "1", + }, + Right: &ast.ValuesInCondition{ + Lparen: 81, + Rparen: 86, + Exprs: []ast.Expr{ + &ast.IntLiteral{ + ValuePos: 82, + ValueEnd: 83, + Base: 10, + Value: "1", + }, + &ast.IntLiteral{ + ValuePos: 85, + ValueEnd: 86, + Base: 10, + Value: "2", + }, + }, + }, +} + +--- SQL +1 NOT IN (1, 2) diff --git a/testdata/result/expr/gql_braced_ambiguity_value_ident.sql.txt b/testdata/result/expr/gql_braced_ambiguity_value_ident.sql.txt new file mode 100644 index 00000000..aa25342b --- /dev/null +++ b/testdata/result/expr/gql_braced_ambiguity_value_ident.sql.txt @@ -0,0 +1,32 @@ +--- gql_braced_ambiguity_value_ident.sql +-- Regression: VALUE used as an identifier must not be hijacked by VALUE { gql } lookahead. +value IN (1, 2) +--- AST +&ast.InExpr{ + Left: &ast.Ident{ + NamePos: 92, + NameEnd: 97, + Name: "value", + }, + Right: &ast.ValuesInCondition{ + Lparen: 101, + Rparen: 106, + Exprs: []ast.Expr{ + &ast.IntLiteral{ + ValuePos: 102, + ValueEnd: 103, + Base: 10, + Value: "1", + }, + &ast.IntLiteral{ + ValuePos: 105, + ValueEnd: 106, + Base: 10, + Value: "2", + }, + }, + }, +} + +--- SQL +value IN (1, 2) diff --git a/testdata/result/gql/!bad_exists_braced_match_continuation.sql.txt b/testdata/result/gql/!bad_exists_braced_match_continuation.sql.txt new file mode 100644 index 00000000..3e4e5e5a --- /dev/null +++ b/testdata/result/gql/!bad_exists_braced_match_continuation.sql.txt @@ -0,0 +1,113 @@ +--- !bad_exists_braced_match_continuation.sql +GRAPH FinGraph +RETURN EXISTS { + MATCH (a) + FILTER +} + +--- Error +syntax error: testdata/input/gql/!bad_exists_braced_match_continuation.sql:5:1: unexpected token: } + 5| } + | ^ +syntax error: testdata/input/gql/!bad_exists_braced_match_continuation.sql:5:1: linear query must end with RETURN + 5| } + | ^ +syntax error: testdata/input/gql/!bad_exists_braced_match_continuation.sql:6:1: expected token: }, but: + 6| + | ^ +syntax error: testdata/input/gql/!bad_exists_braced_match_continuation.sql:5:1: expected token: , but: } + 5| } + | ^ + + +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLReturn{ + Return: 15, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.BadExpr{ + BadNode: &ast.BadNode{ + NodePos: 22, + NodeEnd: 51, + Tokens: []*token.Token{ + &token.Token{ + Kind: "EXISTS", + Space: " ", + Raw: "EXISTS", + Pos: 22, + End: 28, + }, + &token.Token{ + Kind: "{", + Space: " ", + Raw: "{", + Pos: 29, + End: 30, + }, + &token.Token{ + Kind: "", + Space: "\n ", + Raw: "MATCH", + AsString: "MATCH", + Pos: 33, + End: 38, + }, + &token.Token{ + Kind: "(", + Space: " ", + Raw: "(", + Pos: 39, + End: 40, + }, + &token.Token{ + Kind: "", + Raw: "a", + AsString: "a", + Pos: 40, + End: 41, + }, + &token.Token{ + Kind: ")", + Raw: ")", + Pos: 41, + End: 42, + }, + &token.Token{ + Kind: "", + Space: "\n ", + Raw: "FILTER", + AsString: "FILTER", + Pos: 45, + End: 51, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph RETURN EXISTS { MATCH (a) FILTER diff --git a/testdata/result/gql/!bad_exists_braced_match_prefix_error.sql.txt b/testdata/result/gql/!bad_exists_braced_match_prefix_error.sql.txt new file mode 100644 index 00000000..d634fe3b --- /dev/null +++ b/testdata/result/gql/!bad_exists_braced_match_prefix_error.sql.txt @@ -0,0 +1,157 @@ +--- !bad_exists_braced_match_prefix_error.sql +GRAPH FinGraph +RETURN EXISTS { + MATCH (a WHERE EXISTS { MATCH (b) RETURN }) + RETURN 1 +} + +--- Error +syntax error: testdata/input/gql/!bad_exists_braced_match_prefix_error.sql:3:44: unexpected token: } + 3| MATCH (a WHERE EXISTS { MATCH (b) RETURN }) + | ^ + + +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLReturn{ + Return: 15, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ExistsGQLSubQuery{ + Exists: 22, + Rbrace: 88, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 33, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 39, + Rparen: 75, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 40, + NameEnd: 41, + Name: "a", + }, + Where: &ast.Where{ + Where: 42, + Expr: &ast.ExistsGQLSubQuery{ + Exists: 48, + Rbrace: 74, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 57, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 63, + Rparen: 65, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 64, + NameEnd: 65, + Name: "b", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 67, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.BadExpr{ + BadNode: &ast.BadNode{ + NodePos: 74, + NodeEnd: 74, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 79, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.IntLiteral{ + ValuePos: 86, + ValueEnd: 87, + Base: 10, + Value: "1", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph RETURN EXISTS { MATCH (a WHERE EXISTS { MATCH (b) RETURN }) RETURN 1 } diff --git a/testdata/result/gql/!bad_value_braced_hint.sql.txt b/testdata/result/gql/!bad_value_braced_hint.sql.txt new file mode 100644 index 00000000..7e8a7d1d --- /dev/null +++ b/testdata/result/gql/!bad_value_braced_hint.sql.txt @@ -0,0 +1,92 @@ +--- !bad_value_braced_hint.sql +GRAPH FinGraph +RETURN VALUE @{x=} { RETURN 1 } + +--- Error +syntax error: testdata/input/gql/!bad_value_braced_hint.sql:2:18: unexpected token: } + 2| RETURN VALUE @{x=} { RETURN 1 } + | ^ + + +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLReturn{ + Return: 15, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ValueGQLSubQuery{ + Value: 22, + Rbrace: 45, + Hint: &ast.Hint{ + Atmark: 28, + Rbrace: 32, + Records: []*ast.HintRecord{ + &ast.HintRecord{ + Key: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 30, + NameEnd: 31, + Name: "x", + }, + }, + }, + Value: &ast.BadExpr{ + BadNode: &ast.BadNode{ + NodePos: 32, + NodeEnd: 32, + }, + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLReturn{ + Return: 36, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.IntLiteral{ + ValuePos: 43, + ValueEnd: 44, + Base: 10, + Value: "1", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph RETURN VALUE @{x=} { RETURN 1 } diff --git a/testdata/result/gql/array_braced.sql.txt b/testdata/result/gql/array_braced.sql.txt new file mode 100644 index 00000000..aaa7f03c --- /dev/null +++ b/testdata/result/gql/array_braced.sql.txt @@ -0,0 +1,295 @@ +--- array_braced.sql +GRAPH FinGraph +MATCH (p:Person)-[:Owns]->(account:Account) +RETURN p.name, 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.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 15, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "p", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 23, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 24, + NameEnd: 30, + Name: "Person", + }, + }, + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLEdgePattern{ + StartPos: 31, + EndPos: 41, + Direction: "RIGHT", + Filler: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 33, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 34, + NameEnd: 38, + Name: "Owns", + }, + }, + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 41, + Rparen: 57, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 42, + NameEnd: 49, + Name: "account", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 49, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 50, + NameEnd: 57, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 59, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 66, + NameEnd: 67, + Name: "p", + }, + &ast.Ident{ + NamePos: 68, + NameEnd: 72, + Name: "name", + }, + }, + }, + }, + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ArrayGQLSubQuery{ + Array: 74, + Rbrace: 193, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 84, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 90, + Rparen: 100, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 91, + NameEnd: 92, + Name: "a", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 92, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 93, + NameEnd: 100, + Name: "Account", + }, + }, + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLEdgePattern{ + StartPos: 101, + EndPos: 124, + Direction: "RIGHT", + Filler: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 103, + NameEnd: 111, + Name: "transfer", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 111, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 112, + NameEnd: 121, + Name: "Transfers", + }, + }, + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 124, + Rparen: 133, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 125, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 126, + NameEnd: 133, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Where: &ast.Where{ + Where: 137, + Expr: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Ident{ + NamePos: 143, + NameEnd: 144, + Name: "a", + }, + Right: &ast.Ident{ + NamePos: 147, + NameEnd: 154, + Name: "account", + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 157, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 164, + NameEnd: 172, + Name: "transfer", + }, + &ast.Ident{ + NamePos: 173, + NameEnd: 179, + Name: "amount", + }, + }, + }, + Alias: &ast.AsAlias{ + As: 180, + Alias: &ast.Ident{ + NamePos: 183, + NameEnd: 192, + Name: "transfers", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 195, + Alias: &ast.Ident{ + NamePos: 198, + NameEnd: 207, + Name: "transfers", + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (p:Person)-[:Owns]->(account:Account) RETURN p.name, ARRAY { MATCH (a:Account)-[transfer:Transfers]->(:Account) WHERE a = account RETURN transfer.amount AS transfers } AS transfers diff --git a/testdata/result/gql/braced_subqueries_graph_clause.sql.txt b/testdata/result/gql/braced_subqueries_graph_clause.sql.txt new file mode 100644 index 00000000..6257820d --- /dev/null +++ b/testdata/result/gql/braced_subqueries_graph_clause.sql.txt @@ -0,0 +1,548 @@ +--- braced_subqueries_graph_clause.sql +GRAPH FinGraph +MATCH (n) +RETURN + EXISTS { GRAPH FinGraph MATCH (m) RETURN m } AS has_match, + EXISTS { MATCH (m) RETURN m UNION ALL RETURN m } AS compound_match, + EXISTS { MATCH (m) RETURN m NEXT RETURN m } AS chained_match, + ARRAY { GRAPH FinGraph MATCH (m) RETURN m } AS matches, + VALUE { GRAPH FinGraph MATCH (m) RETURN m } AS value, + n IN { GRAPH FinGraph MATCH (m) RETURN m } AS contained + +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 15, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 23, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "n", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 25, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ExistsGQLSubQuery{ + Exists: 34, + Rbrace: 77, + GraphClause: &ast.GQLGraphClause{ + Graph: 43, + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 49, + NameEnd: 57, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 58, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 64, + Rparen: 66, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 65, + NameEnd: 66, + Name: "m", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 68, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Ident{ + NamePos: 75, + NameEnd: 76, + Name: "m", + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 79, + Alias: &ast.Ident{ + NamePos: 82, + NameEnd: 91, + Name: "has_match", + }, + }, + }, + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ExistsGQLSubQuery{ + Exists: 95, + Rbrace: 142, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLCompoundLinearQueryStatement{ + Op: "UNION", + AllOrDistinct: "ALL", + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 104, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 110, + Rparen: 112, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 111, + NameEnd: 112, + Name: "m", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 114, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Ident{ + NamePos: 121, + NameEnd: 122, + Name: "m", + }, + }, + }, + }, + }, + }, + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLReturn{ + Return: 133, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Ident{ + NamePos: 140, + NameEnd: 141, + Name: "m", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 144, + Alias: &ast.Ident{ + NamePos: 147, + NameEnd: 161, + Name: "compound_match", + }, + }, + }, + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ExistsGQLSubQuery{ + Exists: 165, + Rbrace: 207, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 174, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 180, + Rparen: 182, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 181, + NameEnd: 182, + Name: "m", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 184, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Ident{ + NamePos: 191, + NameEnd: 192, + Name: "m", + }, + }, + }, + }, + }, + }, + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLReturn{ + Return: 198, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Ident{ + NamePos: 205, + NameEnd: 206, + Name: "m", + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 209, + Alias: &ast.Ident{ + NamePos: 212, + NameEnd: 225, + Name: "chained_match", + }, + }, + }, + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ArrayGQLSubQuery{ + Array: 229, + Rbrace: 271, + GraphClause: &ast.GQLGraphClause{ + Graph: 237, + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 243, + NameEnd: 251, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 252, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 258, + Rparen: 260, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 259, + NameEnd: 260, + Name: "m", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 262, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Ident{ + NamePos: 269, + NameEnd: 270, + Name: "m", + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 273, + Alias: &ast.Ident{ + NamePos: 276, + NameEnd: 283, + Name: "matches", + }, + }, + }, + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ValueGQLSubQuery{ + Value: 287, + Rbrace: 329, + GraphClause: &ast.GQLGraphClause{ + Graph: 295, + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 301, + NameEnd: 309, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 310, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 316, + Rparen: 318, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 317, + NameEnd: 318, + Name: "m", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 320, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Ident{ + NamePos: 327, + NameEnd: 328, + Name: "m", + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 331, + Alias: &ast.Ident{ + NamePos: 334, + NameEnd: 339, + Name: "value", + }, + }, + }, + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.InExpr{ + Left: &ast.Ident{ + NamePos: 343, + NameEnd: 344, + Name: "n", + }, + Right: &ast.GQLSubQueryInCondition{ + Lbrace: 348, + Rbrace: 384, + GraphClause: &ast.GQLGraphClause{ + Graph: 350, + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 356, + NameEnd: 364, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 365, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 371, + Rparen: 373, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 372, + NameEnd: 373, + Name: "m", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 375, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Ident{ + NamePos: 382, + NameEnd: 383, + Name: "m", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 386, + Alias: &ast.Ident{ + NamePos: 389, + NameEnd: 398, + Name: "contained", + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (n) RETURN EXISTS { GRAPH FinGraph MATCH (m) RETURN m } AS has_match, EXISTS { MATCH (m) RETURN m UNION ALL RETURN m } AS compound_match, EXISTS { MATCH (m) RETURN m NEXT RETURN m } AS chained_match, ARRAY { GRAPH FinGraph MATCH (m) RETURN m } AS matches, VALUE { GRAPH FinGraph MATCH (m) RETURN m } AS value, n IN { GRAPH FinGraph MATCH (m) RETURN m } AS contained diff --git a/testdata/result/gql/braced_subqueries_hints_and_path_modes.sql.txt b/testdata/result/gql/braced_subqueries_hints_and_path_modes.sql.txt new file mode 100644 index 00000000..6930c3e3 --- /dev/null +++ b/testdata/result/gql/braced_subqueries_hints_and_path_modes.sql.txt @@ -0,0 +1,264 @@ +--- braced_subqueries_hints_and_path_modes.sql +GRAPH FinGraph +RETURN + EXISTS @{JOIN_METHOD=HASH_JOIN} { ANY SHORTEST (a)-[]->(b) } AS any_shortest, + EXISTS { TRAIL PATH (a)-[]->(b) } AS trail_path, + VALUE @{a=1} { RETURN 1 AS value } AS hinted_value + +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLReturn{ + Return: 15, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ExistsGQLSubQuery{ + Exists: 24, + Rbrace: 83, + Hint: &ast.Hint{ + Atmark: 31, + Rbrace: 54, + Records: []*ast.HintRecord{ + &ast.HintRecord{ + Key: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 33, + NameEnd: 44, + Name: "JOIN_METHOD", + }, + }, + }, + Value: &ast.Ident{ + NamePos: 45, + NameEnd: 54, + Name: "HASH_JOIN", + }, + }, + }, + }, + Query: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + SearchPrefix: &ast.GQLPathSearchPrefix{ + StartPos: 58, + EndPos: 70, + Prefix: "ANY SHORTEST", + }, + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 71, + Rparen: 73, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 72, + NameEnd: 73, + Name: "a", + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLEdgePattern{ + StartPos: 74, + EndPos: 79, + Direction: "RIGHT", + Filler: &ast.GQLElementPatternFiller{ + EmptyPos: 76, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 79, + Rparen: 81, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 80, + NameEnd: 81, + Name: "b", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 85, + Alias: &ast.Ident{ + NamePos: 88, + NameEnd: 100, + Name: "any_shortest", + }, + }, + }, + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ExistsGQLSubQuery{ + Exists: 104, + Rbrace: 136, + Query: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Mode: &ast.GQLPathModeClause{ + StartPos: 113, + EndPos: 123, + Mode: "TRAIL", + Suffix: "PATH", + }, + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 124, + Rparen: 126, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 125, + NameEnd: 126, + Name: "a", + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLEdgePattern{ + StartPos: 127, + EndPos: 132, + Direction: "RIGHT", + Filler: &ast.GQLElementPatternFiller{ + EmptyPos: 129, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 132, + Rparen: 134, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 133, + NameEnd: 134, + Name: "b", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 138, + Alias: &ast.Ident{ + NamePos: 141, + NameEnd: 151, + Name: "trail_path", + }, + }, + }, + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ValueGQLSubQuery{ + Value: 155, + Rbrace: 188, + Hint: &ast.Hint{ + Atmark: 161, + Rbrace: 166, + Records: []*ast.HintRecord{ + &ast.HintRecord{ + Key: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 163, + NameEnd: 164, + Name: "a", + }, + }, + }, + Value: &ast.IntLiteral{ + ValuePos: 165, + ValueEnd: 166, + Base: 10, + Value: "1", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLReturn{ + Return: 170, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.IntLiteral{ + ValuePos: 177, + ValueEnd: 178, + Base: 10, + Value: "1", + }, + Alias: &ast.AsAlias{ + As: 179, + Alias: &ast.Ident{ + NamePos: 182, + NameEnd: 187, + Name: "value", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 190, + Alias: &ast.Ident{ + NamePos: 193, + NameEnd: 205, + Name: "hinted_value", + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph RETURN EXISTS @{JOIN_METHOD=HASH_JOIN} { ANY SHORTEST (a)-[]->(b) } AS any_shortest, EXISTS { TRAIL PATH (a)-[]->(b) } AS trail_path, VALUE @{a=1} { RETURN 1 AS value } AS hinted_value diff --git a/testdata/result/gql/exists_braced_match.sql.txt b/testdata/result/gql/exists_braced_match.sql.txt new file mode 100644 index 00000000..80ec729a --- /dev/null +++ b/testdata/result/gql/exists_braced_match.sql.txt @@ -0,0 +1,165 @@ +--- exists_braced_match.sql +GRAPH FinGraph +MATCH (p:Person) +RETURN EXISTS { + MATCH (p)-[:Owns]->(a:Account) +} AS has_account + +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 15, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "p", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 23, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 24, + NameEnd: 30, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 32, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ExistsGQLSubQuery{ + Exists: 39, + Rbrace: 81, + Query: &ast.GQLMatch{ + Optional: -1, + Match: 50, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 56, + Rparen: 58, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 57, + NameEnd: 58, + Name: "p", + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLEdgePattern{ + StartPos: 59, + EndPos: 69, + Direction: "RIGHT", + Filler: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 61, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 62, + NameEnd: 66, + Name: "Owns", + }, + }, + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 69, + Rparen: 79, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 70, + NameEnd: 71, + Name: "a", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 71, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 72, + NameEnd: 79, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 83, + Alias: &ast.Ident{ + NamePos: 86, + NameEnd: 97, + Name: "has_account", + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (p:Person) RETURN EXISTS { MATCH (p)-[:Owns]->(a:Account) } AS has_account diff --git a/testdata/result/gql/exists_braced_path_variable.sql.txt b/testdata/result/gql/exists_braced_path_variable.sql.txt new file mode 100644 index 00000000..ab8ffdd3 --- /dev/null +++ b/testdata/result/gql/exists_braced_path_variable.sql.txt @@ -0,0 +1,194 @@ +--- exists_braced_path_variable.sql +GRAPH FinGraph +RETURN + EXISTS { p = (a)-[e]->(b) } AS local_graph, + EXISTS { GRAPH FinGraph p = (a)-[e]->(b) } AS explicit_graph + +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLReturn{ + Return: 15, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ExistsGQLSubQuery{ + Exists: 24, + Rbrace: 50, + Query: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Variable: &ast.Ident{ + NamePos: 33, + NameEnd: 34, + Name: "p", + }, + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 37, + Rparen: 39, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 38, + NameEnd: 39, + Name: "a", + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLEdgePattern{ + StartPos: 40, + EndPos: 46, + Direction: "RIGHT", + Filler: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 42, + NameEnd: 43, + Name: "e", + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 46, + Rparen: 48, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 47, + NameEnd: 48, + Name: "b", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 52, + Alias: &ast.Ident{ + NamePos: 55, + NameEnd: 66, + Name: "local_graph", + }, + }, + }, + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ExistsGQLSubQuery{ + Exists: 70, + Rbrace: 111, + GraphClause: &ast.GQLGraphClause{ + Graph: 79, + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 85, + NameEnd: 93, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Variable: &ast.Ident{ + NamePos: 94, + NameEnd: 95, + Name: "p", + }, + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 98, + Rparen: 100, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 99, + NameEnd: 100, + Name: "a", + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLEdgePattern{ + StartPos: 101, + EndPos: 107, + Direction: "RIGHT", + Filler: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 103, + NameEnd: 104, + Name: "e", + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 107, + Rparen: 109, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 108, + NameEnd: 109, + Name: "b", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 113, + Alias: &ast.Ident{ + NamePos: 116, + NameEnd: 130, + Name: "explicit_graph", + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph RETURN EXISTS { p = (a)-[e]->(b) } AS local_graph, EXISTS { GRAPH FinGraph p = (a)-[e]->(b) } AS explicit_graph diff --git a/testdata/result/gql/exists_braced_pattern.sql.txt b/testdata/result/gql/exists_braced_pattern.sql.txt new file mode 100644 index 00000000..27c31036 --- /dev/null +++ b/testdata/result/gql/exists_braced_pattern.sql.txt @@ -0,0 +1,154 @@ +--- exists_braced_pattern.sql +GRAPH FinGraph +MATCH (p:Person) +RETURN EXISTS { (p)-[:Owns]->(:Account) } AS has_account + +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 15, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "p", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 23, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 24, + NameEnd: 30, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 32, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ExistsGQLSubQuery{ + Exists: 39, + Rbrace: 72, + Query: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 48, + Rparen: 50, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 49, + NameEnd: 50, + Name: "p", + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLEdgePattern{ + StartPos: 51, + EndPos: 61, + Direction: "RIGHT", + Filler: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 53, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 54, + NameEnd: 58, + Name: "Owns", + }, + }, + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 61, + Rparen: 70, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 62, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 63, + NameEnd: 70, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 74, + Alias: &ast.Ident{ + NamePos: 77, + NameEnd: 88, + Name: "has_account", + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (p:Person) RETURN EXISTS { (p)-[:Owns]->(:Account) } AS has_account diff --git a/testdata/result/gql/exists_braced_query.sql.txt b/testdata/result/gql/exists_braced_query.sql.txt new file mode 100644 index 00000000..36031721 --- /dev/null +++ b/testdata/result/gql/exists_braced_query.sql.txt @@ -0,0 +1,196 @@ +--- exists_braced_query.sql +GRAPH FinGraph +MATCH (p:Person) +RETURN EXISTS { + MATCH (p)-[:Owns]->(a:Account) + RETURN a.id +} AS has_account + +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 15, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "p", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 23, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 24, + NameEnd: 30, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 32, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ExistsGQLSubQuery{ + Exists: 39, + Rbrace: 95, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 50, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 56, + Rparen: 58, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 57, + NameEnd: 58, + Name: "p", + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLEdgePattern{ + StartPos: 59, + EndPos: 69, + Direction: "RIGHT", + Filler: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 61, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 62, + NameEnd: 66, + Name: "Owns", + }, + }, + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 69, + Rparen: 79, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 70, + NameEnd: 71, + Name: "a", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 71, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 72, + NameEnd: 79, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 83, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 90, + NameEnd: 91, + Name: "a", + }, + &ast.Ident{ + NamePos: 92, + NameEnd: 94, + Name: "id", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 97, + Alias: &ast.Ident{ + NamePos: 100, + NameEnd: 111, + Name: "has_account", + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (p:Person) RETURN EXISTS { MATCH (p)-[:Owns]->(a:Account) RETURN a.id } AS has_account diff --git a/testdata/result/gql/in_braced.sql.txt b/testdata/result/gql/in_braced.sql.txt new file mode 100644 index 00000000..d9b4dd61 --- /dev/null +++ b/testdata/result/gql/in_braced.sql.txt @@ -0,0 +1,204 @@ +--- in_braced.sql +GRAPH FinGraph +MATCH (p:Person) +RETURN p.name IN { + MATCH (x:Person) + WHERE x.id < 10 + RETURN x.name +} AS flag + +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 15, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "p", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 23, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 24, + NameEnd: 30, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 32, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.InExpr{ + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 39, + NameEnd: 40, + Name: "p", + }, + &ast.Ident{ + NamePos: 41, + NameEnd: 45, + Name: "name", + }, + }, + }, + Right: &ast.GQLSubQueryInCondition{ + Lbrace: 49, + Rbrace: 104, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 53, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 59, + Rparen: 68, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 60, + NameEnd: 61, + Name: "x", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 61, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 62, + NameEnd: 68, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Where: &ast.Where{ + Where: 72, + Expr: &ast.BinaryExpr{ + Op: "<", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 78, + NameEnd: 79, + Name: "x", + }, + &ast.Ident{ + NamePos: 80, + NameEnd: 82, + Name: "id", + }, + }, + }, + Right: &ast.IntLiteral{ + ValuePos: 85, + ValueEnd: 87, + Base: 10, + Value: "10", + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 90, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 97, + NameEnd: 98, + Name: "x", + }, + &ast.Ident{ + NamePos: 99, + NameEnd: 103, + Name: "name", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 106, + Alias: &ast.Ident{ + NamePos: 109, + NameEnd: 113, + Name: "flag", + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (p:Person) RETURN p.name IN { MATCH (x:Person) WHERE x.id < 10 RETURN x.name } AS flag diff --git a/testdata/result/gql/not_in_braced.sql.txt b/testdata/result/gql/not_in_braced.sql.txt new file mode 100644 index 00000000..fb9aadce --- /dev/null +++ b/testdata/result/gql/not_in_braced.sql.txt @@ -0,0 +1,178 @@ +--- not_in_braced.sql +GRAPH FinGraph +MATCH (p:Person) +RETURN p.name NOT IN { + MATCH (x:Person) + RETURN x.name +} AS flag + +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 15, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "p", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 23, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 24, + NameEnd: 30, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 32, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.InExpr{ + Not: true, + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 39, + NameEnd: 40, + Name: "p", + }, + &ast.Ident{ + NamePos: 41, + NameEnd: 45, + Name: "name", + }, + }, + }, + Right: &ast.GQLSubQueryInCondition{ + Lbrace: 53, + Rbrace: 90, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 57, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 63, + Rparen: 72, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 64, + NameEnd: 65, + Name: "x", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 65, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 66, + NameEnd: 72, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 76, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 83, + NameEnd: 84, + Name: "x", + }, + &ast.Ident{ + NamePos: 85, + NameEnd: 89, + Name: "name", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 92, + Alias: &ast.Ident{ + NamePos: 95, + NameEnd: 99, + Name: "flag", + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (p:Person) RETURN p.name NOT IN { MATCH (x:Person) RETURN x.name } AS flag diff --git a/testdata/result/gql/value_braced.sql.txt b/testdata/result/gql/value_braced.sql.txt new file mode 100644 index 00000000..274f1525 --- /dev/null +++ b/testdata/result/gql/value_braced.sql.txt @@ -0,0 +1,214 @@ +--- value_braced.sql +GRAPH FinGraph +MATCH (p:Person) +RETURN VALUE { + MATCH (p)-[:Owns]->(a:Account) + RETURN a.id AS id + LIMIT 1 +} AS account_id + +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 15, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "p", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 23, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 24, + NameEnd: 30, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 32, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ValueGQLSubQuery{ + Value: 39, + Rbrace: 110, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 49, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 55, + Rparen: 57, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 56, + NameEnd: 57, + Name: "p", + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLEdgePattern{ + StartPos: 58, + EndPos: 68, + Direction: "RIGHT", + Filler: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 60, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 61, + NameEnd: 65, + Name: "Owns", + }, + }, + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 68, + Rparen: 78, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 69, + NameEnd: 70, + Name: "a", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 70, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 71, + NameEnd: 78, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 82, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 89, + NameEnd: 90, + Name: "a", + }, + &ast.Ident{ + NamePos: 91, + NameEnd: 93, + Name: "id", + }, + }, + }, + Alias: &ast.AsAlias{ + As: 94, + Alias: &ast.Ident{ + NamePos: 97, + NameEnd: 99, + Name: "id", + }, + }, + }, + }, + Limit: &ast.GQLLimit{ + LimitPos: 102, + Limit: &ast.IntLiteral{ + ValuePos: 108, + ValueEnd: 109, + Base: 10, + Value: "1", + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 112, + Alias: &ast.Ident{ + NamePos: 115, + NameEnd: 125, + Name: "account_id", + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (p:Person) RETURN VALUE { MATCH (p)-[:Owns]->(a:Account) RETURN a.id AS id LIMIT 1 } AS account_id diff --git a/testdata/result/statement/!bad_exists_braced_match_continuation.sql.txt b/testdata/result/statement/!bad_exists_braced_match_continuation.sql.txt new file mode 100644 index 00000000..3e4e5e5a --- /dev/null +++ b/testdata/result/statement/!bad_exists_braced_match_continuation.sql.txt @@ -0,0 +1,113 @@ +--- !bad_exists_braced_match_continuation.sql +GRAPH FinGraph +RETURN EXISTS { + MATCH (a) + FILTER +} + +--- Error +syntax error: testdata/input/gql/!bad_exists_braced_match_continuation.sql:5:1: unexpected token: } + 5| } + | ^ +syntax error: testdata/input/gql/!bad_exists_braced_match_continuation.sql:5:1: linear query must end with RETURN + 5| } + | ^ +syntax error: testdata/input/gql/!bad_exists_braced_match_continuation.sql:6:1: expected token: }, but: + 6| + | ^ +syntax error: testdata/input/gql/!bad_exists_braced_match_continuation.sql:5:1: expected token: , but: } + 5| } + | ^ + + +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLReturn{ + Return: 15, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.BadExpr{ + BadNode: &ast.BadNode{ + NodePos: 22, + NodeEnd: 51, + Tokens: []*token.Token{ + &token.Token{ + Kind: "EXISTS", + Space: " ", + Raw: "EXISTS", + Pos: 22, + End: 28, + }, + &token.Token{ + Kind: "{", + Space: " ", + Raw: "{", + Pos: 29, + End: 30, + }, + &token.Token{ + Kind: "", + Space: "\n ", + Raw: "MATCH", + AsString: "MATCH", + Pos: 33, + End: 38, + }, + &token.Token{ + Kind: "(", + Space: " ", + Raw: "(", + Pos: 39, + End: 40, + }, + &token.Token{ + Kind: "", + Raw: "a", + AsString: "a", + Pos: 40, + End: 41, + }, + &token.Token{ + Kind: ")", + Raw: ")", + Pos: 41, + End: 42, + }, + &token.Token{ + Kind: "", + Space: "\n ", + Raw: "FILTER", + AsString: "FILTER", + Pos: 45, + End: 51, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph RETURN EXISTS { MATCH (a) FILTER diff --git a/testdata/result/statement/!bad_exists_braced_match_prefix_error.sql.txt b/testdata/result/statement/!bad_exists_braced_match_prefix_error.sql.txt new file mode 100644 index 00000000..d634fe3b --- /dev/null +++ b/testdata/result/statement/!bad_exists_braced_match_prefix_error.sql.txt @@ -0,0 +1,157 @@ +--- !bad_exists_braced_match_prefix_error.sql +GRAPH FinGraph +RETURN EXISTS { + MATCH (a WHERE EXISTS { MATCH (b) RETURN }) + RETURN 1 +} + +--- Error +syntax error: testdata/input/gql/!bad_exists_braced_match_prefix_error.sql:3:44: unexpected token: } + 3| MATCH (a WHERE EXISTS { MATCH (b) RETURN }) + | ^ + + +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLReturn{ + Return: 15, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ExistsGQLSubQuery{ + Exists: 22, + Rbrace: 88, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 33, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 39, + Rparen: 75, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 40, + NameEnd: 41, + Name: "a", + }, + Where: &ast.Where{ + Where: 42, + Expr: &ast.ExistsGQLSubQuery{ + Exists: 48, + Rbrace: 74, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 57, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 63, + Rparen: 65, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 64, + NameEnd: 65, + Name: "b", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 67, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.BadExpr{ + BadNode: &ast.BadNode{ + NodePos: 74, + NodeEnd: 74, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 79, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.IntLiteral{ + ValuePos: 86, + ValueEnd: 87, + Base: 10, + Value: "1", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph RETURN EXISTS { MATCH (a WHERE EXISTS { MATCH (b) RETURN }) RETURN 1 } diff --git a/testdata/result/statement/!bad_value_braced_hint.sql.txt b/testdata/result/statement/!bad_value_braced_hint.sql.txt new file mode 100644 index 00000000..7e8a7d1d --- /dev/null +++ b/testdata/result/statement/!bad_value_braced_hint.sql.txt @@ -0,0 +1,92 @@ +--- !bad_value_braced_hint.sql +GRAPH FinGraph +RETURN VALUE @{x=} { RETURN 1 } + +--- Error +syntax error: testdata/input/gql/!bad_value_braced_hint.sql:2:18: unexpected token: } + 2| RETURN VALUE @{x=} { RETURN 1 } + | ^ + + +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLReturn{ + Return: 15, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ValueGQLSubQuery{ + Value: 22, + Rbrace: 45, + Hint: &ast.Hint{ + Atmark: 28, + Rbrace: 32, + Records: []*ast.HintRecord{ + &ast.HintRecord{ + Key: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 30, + NameEnd: 31, + Name: "x", + }, + }, + }, + Value: &ast.BadExpr{ + BadNode: &ast.BadNode{ + NodePos: 32, + NodeEnd: 32, + }, + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLReturn{ + Return: 36, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.IntLiteral{ + ValuePos: 43, + ValueEnd: 44, + Base: 10, + Value: "1", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph RETURN VALUE @{x=} { RETURN 1 } diff --git a/testdata/result/statement/array_braced.sql.txt b/testdata/result/statement/array_braced.sql.txt new file mode 100644 index 00000000..aaa7f03c --- /dev/null +++ b/testdata/result/statement/array_braced.sql.txt @@ -0,0 +1,295 @@ +--- array_braced.sql +GRAPH FinGraph +MATCH (p:Person)-[:Owns]->(account:Account) +RETURN p.name, 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.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 15, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "p", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 23, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 24, + NameEnd: 30, + Name: "Person", + }, + }, + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLEdgePattern{ + StartPos: 31, + EndPos: 41, + Direction: "RIGHT", + Filler: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 33, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 34, + NameEnd: 38, + Name: "Owns", + }, + }, + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 41, + Rparen: 57, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 42, + NameEnd: 49, + Name: "account", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 49, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 50, + NameEnd: 57, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 59, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 66, + NameEnd: 67, + Name: "p", + }, + &ast.Ident{ + NamePos: 68, + NameEnd: 72, + Name: "name", + }, + }, + }, + }, + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ArrayGQLSubQuery{ + Array: 74, + Rbrace: 193, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 84, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 90, + Rparen: 100, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 91, + NameEnd: 92, + Name: "a", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 92, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 93, + NameEnd: 100, + Name: "Account", + }, + }, + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLEdgePattern{ + StartPos: 101, + EndPos: 124, + Direction: "RIGHT", + Filler: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 103, + NameEnd: 111, + Name: "transfer", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 111, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 112, + NameEnd: 121, + Name: "Transfers", + }, + }, + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 124, + Rparen: 133, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 125, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 126, + NameEnd: 133, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Where: &ast.Where{ + Where: 137, + Expr: &ast.BinaryExpr{ + Op: "=", + Left: &ast.Ident{ + NamePos: 143, + NameEnd: 144, + Name: "a", + }, + Right: &ast.Ident{ + NamePos: 147, + NameEnd: 154, + Name: "account", + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 157, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 164, + NameEnd: 172, + Name: "transfer", + }, + &ast.Ident{ + NamePos: 173, + NameEnd: 179, + Name: "amount", + }, + }, + }, + Alias: &ast.AsAlias{ + As: 180, + Alias: &ast.Ident{ + NamePos: 183, + NameEnd: 192, + Name: "transfers", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 195, + Alias: &ast.Ident{ + NamePos: 198, + NameEnd: 207, + Name: "transfers", + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (p:Person)-[:Owns]->(account:Account) RETURN p.name, ARRAY { MATCH (a:Account)-[transfer:Transfers]->(:Account) WHERE a = account RETURN transfer.amount AS transfers } AS transfers diff --git a/testdata/result/statement/braced_subqueries_graph_clause.sql.txt b/testdata/result/statement/braced_subqueries_graph_clause.sql.txt new file mode 100644 index 00000000..6257820d --- /dev/null +++ b/testdata/result/statement/braced_subqueries_graph_clause.sql.txt @@ -0,0 +1,548 @@ +--- braced_subqueries_graph_clause.sql +GRAPH FinGraph +MATCH (n) +RETURN + EXISTS { GRAPH FinGraph MATCH (m) RETURN m } AS has_match, + EXISTS { MATCH (m) RETURN m UNION ALL RETURN m } AS compound_match, + EXISTS { MATCH (m) RETURN m NEXT RETURN m } AS chained_match, + ARRAY { GRAPH FinGraph MATCH (m) RETURN m } AS matches, + VALUE { GRAPH FinGraph MATCH (m) RETURN m } AS value, + n IN { GRAPH FinGraph MATCH (m) RETURN m } AS contained + +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 15, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 23, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "n", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 25, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ExistsGQLSubQuery{ + Exists: 34, + Rbrace: 77, + GraphClause: &ast.GQLGraphClause{ + Graph: 43, + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 49, + NameEnd: 57, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 58, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 64, + Rparen: 66, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 65, + NameEnd: 66, + Name: "m", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 68, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Ident{ + NamePos: 75, + NameEnd: 76, + Name: "m", + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 79, + Alias: &ast.Ident{ + NamePos: 82, + NameEnd: 91, + Name: "has_match", + }, + }, + }, + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ExistsGQLSubQuery{ + Exists: 95, + Rbrace: 142, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLCompoundLinearQueryStatement{ + Op: "UNION", + AllOrDistinct: "ALL", + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 104, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 110, + Rparen: 112, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 111, + NameEnd: 112, + Name: "m", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 114, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Ident{ + NamePos: 121, + NameEnd: 122, + Name: "m", + }, + }, + }, + }, + }, + }, + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLReturn{ + Return: 133, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Ident{ + NamePos: 140, + NameEnd: 141, + Name: "m", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 144, + Alias: &ast.Ident{ + NamePos: 147, + NameEnd: 161, + Name: "compound_match", + }, + }, + }, + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ExistsGQLSubQuery{ + Exists: 165, + Rbrace: 207, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 174, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 180, + Rparen: 182, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 181, + NameEnd: 182, + Name: "m", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 184, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Ident{ + NamePos: 191, + NameEnd: 192, + Name: "m", + }, + }, + }, + }, + }, + }, + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLReturn{ + Return: 198, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Ident{ + NamePos: 205, + NameEnd: 206, + Name: "m", + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 209, + Alias: &ast.Ident{ + NamePos: 212, + NameEnd: 225, + Name: "chained_match", + }, + }, + }, + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ArrayGQLSubQuery{ + Array: 229, + Rbrace: 271, + GraphClause: &ast.GQLGraphClause{ + Graph: 237, + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 243, + NameEnd: 251, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 252, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 258, + Rparen: 260, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 259, + NameEnd: 260, + Name: "m", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 262, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Ident{ + NamePos: 269, + NameEnd: 270, + Name: "m", + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 273, + Alias: &ast.Ident{ + NamePos: 276, + NameEnd: 283, + Name: "matches", + }, + }, + }, + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ValueGQLSubQuery{ + Value: 287, + Rbrace: 329, + GraphClause: &ast.GQLGraphClause{ + Graph: 295, + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 301, + NameEnd: 309, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 310, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 316, + Rparen: 318, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 317, + NameEnd: 318, + Name: "m", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 320, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Ident{ + NamePos: 327, + NameEnd: 328, + Name: "m", + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 331, + Alias: &ast.Ident{ + NamePos: 334, + NameEnd: 339, + Name: "value", + }, + }, + }, + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.InExpr{ + Left: &ast.Ident{ + NamePos: 343, + NameEnd: 344, + Name: "n", + }, + Right: &ast.GQLSubQueryInCondition{ + Lbrace: 348, + Rbrace: 384, + GraphClause: &ast.GQLGraphClause{ + Graph: 350, + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 356, + NameEnd: 364, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 365, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 371, + Rparen: 373, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 372, + NameEnd: 373, + Name: "m", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 375, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Ident{ + NamePos: 382, + NameEnd: 383, + Name: "m", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 386, + Alias: &ast.Ident{ + NamePos: 389, + NameEnd: 398, + Name: "contained", + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (n) RETURN EXISTS { GRAPH FinGraph MATCH (m) RETURN m } AS has_match, EXISTS { MATCH (m) RETURN m UNION ALL RETURN m } AS compound_match, EXISTS { MATCH (m) RETURN m NEXT RETURN m } AS chained_match, ARRAY { GRAPH FinGraph MATCH (m) RETURN m } AS matches, VALUE { GRAPH FinGraph MATCH (m) RETURN m } AS value, n IN { GRAPH FinGraph MATCH (m) RETURN m } AS contained diff --git a/testdata/result/statement/braced_subqueries_hints_and_path_modes.sql.txt b/testdata/result/statement/braced_subqueries_hints_and_path_modes.sql.txt new file mode 100644 index 00000000..6930c3e3 --- /dev/null +++ b/testdata/result/statement/braced_subqueries_hints_and_path_modes.sql.txt @@ -0,0 +1,264 @@ +--- braced_subqueries_hints_and_path_modes.sql +GRAPH FinGraph +RETURN + EXISTS @{JOIN_METHOD=HASH_JOIN} { ANY SHORTEST (a)-[]->(b) } AS any_shortest, + EXISTS { TRAIL PATH (a)-[]->(b) } AS trail_path, + VALUE @{a=1} { RETURN 1 AS value } AS hinted_value + +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLReturn{ + Return: 15, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ExistsGQLSubQuery{ + Exists: 24, + Rbrace: 83, + Hint: &ast.Hint{ + Atmark: 31, + Rbrace: 54, + Records: []*ast.HintRecord{ + &ast.HintRecord{ + Key: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 33, + NameEnd: 44, + Name: "JOIN_METHOD", + }, + }, + }, + Value: &ast.Ident{ + NamePos: 45, + NameEnd: 54, + Name: "HASH_JOIN", + }, + }, + }, + }, + Query: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + SearchPrefix: &ast.GQLPathSearchPrefix{ + StartPos: 58, + EndPos: 70, + Prefix: "ANY SHORTEST", + }, + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 71, + Rparen: 73, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 72, + NameEnd: 73, + Name: "a", + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLEdgePattern{ + StartPos: 74, + EndPos: 79, + Direction: "RIGHT", + Filler: &ast.GQLElementPatternFiller{ + EmptyPos: 76, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 79, + Rparen: 81, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 80, + NameEnd: 81, + Name: "b", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 85, + Alias: &ast.Ident{ + NamePos: 88, + NameEnd: 100, + Name: "any_shortest", + }, + }, + }, + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ExistsGQLSubQuery{ + Exists: 104, + Rbrace: 136, + Query: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Mode: &ast.GQLPathModeClause{ + StartPos: 113, + EndPos: 123, + Mode: "TRAIL", + Suffix: "PATH", + }, + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 124, + Rparen: 126, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 125, + NameEnd: 126, + Name: "a", + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLEdgePattern{ + StartPos: 127, + EndPos: 132, + Direction: "RIGHT", + Filler: &ast.GQLElementPatternFiller{ + EmptyPos: 129, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 132, + Rparen: 134, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 133, + NameEnd: 134, + Name: "b", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 138, + Alias: &ast.Ident{ + NamePos: 141, + NameEnd: 151, + Name: "trail_path", + }, + }, + }, + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ValueGQLSubQuery{ + Value: 155, + Rbrace: 188, + Hint: &ast.Hint{ + Atmark: 161, + Rbrace: 166, + Records: []*ast.HintRecord{ + &ast.HintRecord{ + Key: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 163, + NameEnd: 164, + Name: "a", + }, + }, + }, + Value: &ast.IntLiteral{ + ValuePos: 165, + ValueEnd: 166, + Base: 10, + Value: "1", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLReturn{ + Return: 170, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.IntLiteral{ + ValuePos: 177, + ValueEnd: 178, + Base: 10, + Value: "1", + }, + Alias: &ast.AsAlias{ + As: 179, + Alias: &ast.Ident{ + NamePos: 182, + NameEnd: 187, + Name: "value", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 190, + Alias: &ast.Ident{ + NamePos: 193, + NameEnd: 205, + Name: "hinted_value", + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph RETURN EXISTS @{JOIN_METHOD=HASH_JOIN} { ANY SHORTEST (a)-[]->(b) } AS any_shortest, EXISTS { TRAIL PATH (a)-[]->(b) } AS trail_path, VALUE @{a=1} { RETURN 1 AS value } AS hinted_value diff --git a/testdata/result/statement/exists_braced_match.sql.txt b/testdata/result/statement/exists_braced_match.sql.txt new file mode 100644 index 00000000..80ec729a --- /dev/null +++ b/testdata/result/statement/exists_braced_match.sql.txt @@ -0,0 +1,165 @@ +--- exists_braced_match.sql +GRAPH FinGraph +MATCH (p:Person) +RETURN EXISTS { + MATCH (p)-[:Owns]->(a:Account) +} AS has_account + +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 15, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "p", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 23, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 24, + NameEnd: 30, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 32, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ExistsGQLSubQuery{ + Exists: 39, + Rbrace: 81, + Query: &ast.GQLMatch{ + Optional: -1, + Match: 50, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 56, + Rparen: 58, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 57, + NameEnd: 58, + Name: "p", + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLEdgePattern{ + StartPos: 59, + EndPos: 69, + Direction: "RIGHT", + Filler: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 61, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 62, + NameEnd: 66, + Name: "Owns", + }, + }, + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 69, + Rparen: 79, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 70, + NameEnd: 71, + Name: "a", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 71, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 72, + NameEnd: 79, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 83, + Alias: &ast.Ident{ + NamePos: 86, + NameEnd: 97, + Name: "has_account", + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (p:Person) RETURN EXISTS { MATCH (p)-[:Owns]->(a:Account) } AS has_account diff --git a/testdata/result/statement/exists_braced_path_variable.sql.txt b/testdata/result/statement/exists_braced_path_variable.sql.txt new file mode 100644 index 00000000..ab8ffdd3 --- /dev/null +++ b/testdata/result/statement/exists_braced_path_variable.sql.txt @@ -0,0 +1,194 @@ +--- exists_braced_path_variable.sql +GRAPH FinGraph +RETURN + EXISTS { p = (a)-[e]->(b) } AS local_graph, + EXISTS { GRAPH FinGraph p = (a)-[e]->(b) } AS explicit_graph + +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLReturn{ + Return: 15, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ExistsGQLSubQuery{ + Exists: 24, + Rbrace: 50, + Query: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Variable: &ast.Ident{ + NamePos: 33, + NameEnd: 34, + Name: "p", + }, + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 37, + Rparen: 39, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 38, + NameEnd: 39, + Name: "a", + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLEdgePattern{ + StartPos: 40, + EndPos: 46, + Direction: "RIGHT", + Filler: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 42, + NameEnd: 43, + Name: "e", + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 46, + Rparen: 48, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 47, + NameEnd: 48, + Name: "b", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 52, + Alias: &ast.Ident{ + NamePos: 55, + NameEnd: 66, + Name: "local_graph", + }, + }, + }, + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ExistsGQLSubQuery{ + Exists: 70, + Rbrace: 111, + GraphClause: &ast.GQLGraphClause{ + Graph: 79, + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 85, + NameEnd: 93, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Variable: &ast.Ident{ + NamePos: 94, + NameEnd: 95, + Name: "p", + }, + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 98, + Rparen: 100, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 99, + NameEnd: 100, + Name: "a", + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLEdgePattern{ + StartPos: 101, + EndPos: 107, + Direction: "RIGHT", + Filler: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 103, + NameEnd: 104, + Name: "e", + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 107, + Rparen: 109, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 108, + NameEnd: 109, + Name: "b", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 113, + Alias: &ast.Ident{ + NamePos: 116, + NameEnd: 130, + Name: "explicit_graph", + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph RETURN EXISTS { p = (a)-[e]->(b) } AS local_graph, EXISTS { GRAPH FinGraph p = (a)-[e]->(b) } AS explicit_graph diff --git a/testdata/result/statement/exists_braced_pattern.sql.txt b/testdata/result/statement/exists_braced_pattern.sql.txt new file mode 100644 index 00000000..27c31036 --- /dev/null +++ b/testdata/result/statement/exists_braced_pattern.sql.txt @@ -0,0 +1,154 @@ +--- exists_braced_pattern.sql +GRAPH FinGraph +MATCH (p:Person) +RETURN EXISTS { (p)-[:Owns]->(:Account) } AS has_account + +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 15, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "p", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 23, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 24, + NameEnd: 30, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 32, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ExistsGQLSubQuery{ + Exists: 39, + Rbrace: 72, + Query: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 48, + Rparen: 50, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 49, + NameEnd: 50, + Name: "p", + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLEdgePattern{ + StartPos: 51, + EndPos: 61, + Direction: "RIGHT", + Filler: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 53, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 54, + NameEnd: 58, + Name: "Owns", + }, + }, + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 61, + Rparen: 70, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 62, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 63, + NameEnd: 70, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 74, + Alias: &ast.Ident{ + NamePos: 77, + NameEnd: 88, + Name: "has_account", + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (p:Person) RETURN EXISTS { (p)-[:Owns]->(:Account) } AS has_account diff --git a/testdata/result/statement/exists_braced_query.sql.txt b/testdata/result/statement/exists_braced_query.sql.txt new file mode 100644 index 00000000..36031721 --- /dev/null +++ b/testdata/result/statement/exists_braced_query.sql.txt @@ -0,0 +1,196 @@ +--- exists_braced_query.sql +GRAPH FinGraph +MATCH (p:Person) +RETURN EXISTS { + MATCH (p)-[:Owns]->(a:Account) + RETURN a.id +} AS has_account + +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 15, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "p", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 23, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 24, + NameEnd: 30, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 32, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ExistsGQLSubQuery{ + Exists: 39, + Rbrace: 95, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 50, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 56, + Rparen: 58, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 57, + NameEnd: 58, + Name: "p", + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLEdgePattern{ + StartPos: 59, + EndPos: 69, + Direction: "RIGHT", + Filler: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 61, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 62, + NameEnd: 66, + Name: "Owns", + }, + }, + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 69, + Rparen: 79, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 70, + NameEnd: 71, + Name: "a", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 71, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 72, + NameEnd: 79, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 83, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 90, + NameEnd: 91, + Name: "a", + }, + &ast.Ident{ + NamePos: 92, + NameEnd: 94, + Name: "id", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 97, + Alias: &ast.Ident{ + NamePos: 100, + NameEnd: 111, + Name: "has_account", + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (p:Person) RETURN EXISTS { MATCH (p)-[:Owns]->(a:Account) RETURN a.id } AS has_account diff --git a/testdata/result/statement/in_braced.sql.txt b/testdata/result/statement/in_braced.sql.txt new file mode 100644 index 00000000..d9b4dd61 --- /dev/null +++ b/testdata/result/statement/in_braced.sql.txt @@ -0,0 +1,204 @@ +--- in_braced.sql +GRAPH FinGraph +MATCH (p:Person) +RETURN p.name IN { + MATCH (x:Person) + WHERE x.id < 10 + RETURN x.name +} AS flag + +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 15, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "p", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 23, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 24, + NameEnd: 30, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 32, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.InExpr{ + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 39, + NameEnd: 40, + Name: "p", + }, + &ast.Ident{ + NamePos: 41, + NameEnd: 45, + Name: "name", + }, + }, + }, + Right: &ast.GQLSubQueryInCondition{ + Lbrace: 49, + Rbrace: 104, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 53, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 59, + Rparen: 68, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 60, + NameEnd: 61, + Name: "x", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 61, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 62, + NameEnd: 68, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Where: &ast.Where{ + Where: 72, + Expr: &ast.BinaryExpr{ + Op: "<", + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 78, + NameEnd: 79, + Name: "x", + }, + &ast.Ident{ + NamePos: 80, + NameEnd: 82, + Name: "id", + }, + }, + }, + Right: &ast.IntLiteral{ + ValuePos: 85, + ValueEnd: 87, + Base: 10, + Value: "10", + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 90, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 97, + NameEnd: 98, + Name: "x", + }, + &ast.Ident{ + NamePos: 99, + NameEnd: 103, + Name: "name", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 106, + Alias: &ast.Ident{ + NamePos: 109, + NameEnd: 113, + Name: "flag", + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (p:Person) RETURN p.name IN { MATCH (x:Person) WHERE x.id < 10 RETURN x.name } AS flag diff --git a/testdata/result/statement/not_in_braced.sql.txt b/testdata/result/statement/not_in_braced.sql.txt new file mode 100644 index 00000000..fb9aadce --- /dev/null +++ b/testdata/result/statement/not_in_braced.sql.txt @@ -0,0 +1,178 @@ +--- not_in_braced.sql +GRAPH FinGraph +MATCH (p:Person) +RETURN p.name NOT IN { + MATCH (x:Person) + RETURN x.name +} AS flag + +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 15, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "p", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 23, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 24, + NameEnd: 30, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 32, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.InExpr{ + Not: true, + Left: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 39, + NameEnd: 40, + Name: "p", + }, + &ast.Ident{ + NamePos: 41, + NameEnd: 45, + Name: "name", + }, + }, + }, + Right: &ast.GQLSubQueryInCondition{ + Lbrace: 53, + Rbrace: 90, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 57, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 63, + Rparen: 72, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 64, + NameEnd: 65, + Name: "x", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 65, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 66, + NameEnd: 72, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 76, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 83, + NameEnd: 84, + Name: "x", + }, + &ast.Ident{ + NamePos: 85, + NameEnd: 89, + Name: "name", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 92, + Alias: &ast.Ident{ + NamePos: 95, + NameEnd: 99, + Name: "flag", + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (p:Person) RETURN p.name NOT IN { MATCH (x:Person) RETURN x.name } AS flag diff --git a/testdata/result/statement/update_in_braced_gql_graph_clause.sql.txt b/testdata/result/statement/update_in_braced_gql_graph_clause.sql.txt new file mode 100644 index 00000000..aec77de1 --- /dev/null +++ b/testdata/result/statement/update_in_braced_gql_graph_clause.sql.txt @@ -0,0 +1,126 @@ +--- update_in_braced_gql_graph_clause.sql +UPDATE Account +SET is_active = TRUE +WHERE id IN { + GRAPH FinGraph + MATCH (a) + RETURN a.id +} + +--- AST +&ast.Update{ + TableName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 7, + NameEnd: 14, + Name: "Account", + }, + }, + }, + Updates: []ast.UpdateItem{ + &ast.UpdateItemSetValue{ + Path: []*ast.Ident{ + &ast.Ident{ + NamePos: 19, + NameEnd: 28, + Name: "is_active", + }, + }, + DefaultExpr: &ast.DefaultExpr{ + DefaultPos: -1, + Expr: &ast.BoolLiteral{ + ValuePos: 31, + Value: true, + }, + }, + }, + }, + Where: &ast.Where{ + Where: 36, + Expr: &ast.InExpr{ + Left: &ast.Ident{ + NamePos: 42, + NameEnd: 44, + Name: "id", + }, + Right: &ast.GQLSubQueryInCondition{ + Lbrace: 48, + Rbrace: 93, + GraphClause: &ast.GQLGraphClause{ + Graph: 52, + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 58, + NameEnd: 66, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 69, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 75, + Rparen: 77, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 76, + NameEnd: 77, + Name: "a", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 81, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 88, + NameEnd: 89, + Name: "a", + }, + &ast.Ident{ + NamePos: 90, + NameEnd: 92, + Name: "id", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +UPDATE Account SET is_active = TRUE WHERE id IN { GRAPH FinGraph MATCH (a) RETURN a.id } diff --git a/testdata/result/statement/value_braced.sql.txt b/testdata/result/statement/value_braced.sql.txt new file mode 100644 index 00000000..274f1525 --- /dev/null +++ b/testdata/result/statement/value_braced.sql.txt @@ -0,0 +1,214 @@ +--- value_braced.sql +GRAPH FinGraph +MATCH (p:Person) +RETURN VALUE { + MATCH (p)-[:Owns]->(a:Account) + RETURN a.id AS id + LIMIT 1 +} AS account_id + +--- AST +&ast.GQLGraphQuery{ + GraphClause: &ast.GQLGraphClause{ + PropertyGraphName: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 6, + NameEnd: 14, + Name: "FinGraph", + }, + }, + }, + }, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 15, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 21, + Rparen: 30, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 22, + NameEnd: 23, + Name: "p", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 23, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 24, + NameEnd: 30, + Name: "Person", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 32, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.ValueGQLSubQuery{ + Value: 39, + Rbrace: 110, + Query: &ast.GQLMultiLinearQueryStatement{ + Statements: []ast.GQLLinearQueryStatement{ + &ast.GQLSimpleLinearQueryStatement{ + Statements: []ast.GQLPrimitiveQueryStatement{ + &ast.GQLMatch{ + Optional: -1, + Match: 49, + Pattern: &ast.GQLGraphPattern{ + Paths: []*ast.GQLTopLevelPathPattern{ + &ast.GQLTopLevelPathPattern{ + Path: &ast.GQLPathPattern{ + Terms: []*ast.GQLPathTerm{ + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 55, + Rparen: 57, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 56, + NameEnd: 57, + Name: "p", + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLEdgePattern{ + StartPos: 58, + EndPos: 68, + Direction: "RIGHT", + Filler: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 60, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 61, + NameEnd: 65, + Name: "Owns", + }, + }, + }, + }, + }, + }, + &ast.GQLPathTerm{ + Primary: &ast.GQLNodePattern{ + Lparen: 68, + Rparen: 78, + Pattern: &ast.GQLElementPatternFiller{ + EmptyPos: -1, + Variable: &ast.Ident{ + NamePos: 69, + NameEnd: 70, + Name: "a", + }, + Label: &ast.GQLLabelFilter{ + Is: -1, + Colon: 70, + Expr: &ast.GQLNameLabel{ + Name: &ast.Ident{ + NamePos: 71, + NameEnd: 78, + Name: "Account", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + &ast.GQLReturn{ + Return: 82, + Items: []*ast.GQLReturnItem{ + &ast.GQLReturnItem{ + Star: -1, + Expr: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 89, + NameEnd: 90, + Name: "a", + }, + &ast.Ident{ + NamePos: 91, + NameEnd: 93, + Name: "id", + }, + }, + }, + Alias: &ast.AsAlias{ + As: 94, + Alias: &ast.Ident{ + NamePos: 97, + NameEnd: 99, + Name: "id", + }, + }, + }, + }, + Limit: &ast.GQLLimit{ + LimitPos: 102, + Limit: &ast.IntLiteral{ + ValuePos: 108, + ValueEnd: 109, + Base: 10, + Value: "1", + }, + }, + }, + }, + }, + }, + }, + }, + Alias: &ast.AsAlias{ + As: 112, + Alias: &ast.Ident{ + NamePos: 115, + NameEnd: 125, + Name: "account_id", + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +GRAPH FinGraph MATCH (p:Person) RETURN VALUE { MATCH (p)-[:Owns]->(a:Account) RETURN a.id AS id LIMIT 1 } AS account_id