Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ func (SubQueryTableExpr) isTableExpr() {}
func (ParenTableExpr) isTableExpr() {}
func (Join) isTableExpr() {}
func (TVFCallExpr) isTableExpr() {}
func (GraphTableExpr) isTableExpr() {}

// JoinCondition represents condition part of JOIN expression.
type JoinCondition interface {
Expand Down Expand Up @@ -4659,6 +4660,55 @@ func (GQLLimit) isGQLPrimitiveQueryStatement() {}
func (GQLOffset) isGQLPrimitiveQueryStatement() {}
func (BadGQLPrimitiveQueryStatement) isGQLPrimitiveQueryStatement() {}

// GraphTableQuery represents a query body in a GRAPH_TABLE operator.
type GraphTableQuery interface {
Node
isGraphTableQuery()
}

func (GQLMultiLinearQueryStatement) isGraphTableQuery() {}
func (GraphTableMatch) isGraphTableQuery() {}

// GraphTableExpr represents GRAPH_TABLE operator in SQL.
//
// GRAPH_TABLE({{.GraphName | sql}} {{.Query | sql}}) {{.As | sqlOpt}} {{.Sample | sqlOpt}}
type GraphTableExpr struct {
// pos = GraphTable
// end = (Sample ?? As).end || Rparen + 1

GraphTable token.Pos // position of "GRAPH_TABLE"
Rparen token.Pos // position of ")"

GraphName *Path
Query GraphTableQuery
As *AsAlias // optional
Sample *TableSample // optional
}

// GraphTableMatch represents the SQL/PGQ form of a GRAPH_TABLE query.
//
// {{.Match | sql}} {{.Columns | sqlOpt}}
type GraphTableMatch struct {
// pos = Match.pos
// end = (Columns ?? Match).end

Match *GQLMatch
Columns *GraphTableColumns // optional
}

// GraphTableColumns is a COLUMNS shape clause in a GRAPH_TABLE query.
//
// COLUMNS({{.Items | sqlJoin ", "}})
type GraphTableColumns struct {
// pos = Columns
// end = Rparen + 1

Columns token.Pos // position of "COLUMNS"
Rparen token.Pos // position of ")"

Items []SelectItem // len(Items) > 0
}

// GQLGraphQuery is a top-level GQL query.
//
// {{.Hint | sqlOpt}} {{.GraphClause | sql}} {{.Query | sql}}
Expand Down
24 changes: 24 additions & 0 deletions ast/pos.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions ast/sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -1540,6 +1540,18 @@ func (c *Call) SQL() string {
//
// ================================================================================

func (n *GraphTableExpr) SQL() string {
return "GRAPH_TABLE(" + n.GraphName.SQL() + " " + n.Query.SQL() + ")" + sqlOpt(" ", n.As, "") + sqlOpt(" ", n.Sample, "")
}

func (m *GraphTableMatch) SQL() string {
return m.Match.SQL() + sqlOpt(" ", m.Columns, "")
}

func (c *GraphTableColumns) SQL() string {
return "COLUMNS(" + sqlJoin(c.Items, ", ") + ")"
}

func (q *GQLGraphQuery) SQL() string {
return sqlOpt("", q.Hint, " ") + q.GraphClause.SQL() + " " + q.Query.SQL()
}
Expand Down
13 changes: 13 additions & 0 deletions ast/walk_internal.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

65 changes: 64 additions & 1 deletion parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -1210,6 +1210,22 @@ func (p *Parser) parseSimpleTableExpr() ast.TableExpr {
return p.parseUnnestSuffix(expr, unnest, rparen)
}

if p.Token.Kind == "GRAPH_TABLE" {
graphTable := p.expect("GRAPH_TABLE").Pos
p.expect("(")
graphName := p.parsePath()
query := p.parseGraphTableQuery()
rparen := p.expect(")").Pos
as := p.tryParseAsAlias(withOptionalAs)
return p.parseTableExprSuffix(&ast.GraphTableExpr{
GraphTable: graphTable,
GraphName: graphName,
Query: query,
Rparen: rparen,
As: as,
})
}

if p.Token.Kind == token.TokenIdent {
ids := p.parseIdentOrPath()
if p.Token.Kind == "(" {
Expand All @@ -1221,7 +1237,52 @@ func (p *Parser) parseSimpleTableExpr() ast.TableExpr {
return p.parsePathTableExprSuffix(&ast.Path{Idents: ids})
}

panic(p.errorfAtToken(&p.Token, "expected token: (, UNNEST, <ident>, but: %s", p.Token.Kind))
panic(p.errorfAtToken(&p.Token, "expected token: (, UNNEST, GRAPH_TABLE, <ident>, but: %s", p.Token.Kind))
}

func (p *Parser) parseGraphTableQuery() ast.GraphTableQuery {
// A single MATCH followed by COLUMNS or the GRAPH_TABLE closing parenthesis
// is the SQL/PGQ form. Other MATCH forms are advanced GQL query blocks.
if p.lookaheadGraphTableMatch() {
return p.parseGraphTableMatch()
}
return p.parseGQLMultiLinearQueryStatement()
}

func (p *Parser) lookaheadGraphTableMatch() bool {
if !p.Token.IsKeywordLike("MATCH") {
return false
}

lexer := p.cloneLexer()
defer func() { p.Lexer = lexer }()

p.parseGQLMatch()
return p.Token.IsKeywordLike("COLUMNS") || p.Token.Kind == ")"
}

func (p *Parser) parseGraphTableMatch() *ast.GraphTableMatch {
match := p.parseGQLMatch()
var columns *ast.GraphTableColumns
if p.Token.IsKeywordLike("COLUMNS") {
columns = p.parseGraphTableColumns()
}
return &ast.GraphTableMatch{
Match: match,
Columns: columns,
}
}

func (p *Parser) parseGraphTableColumns() *ast.GraphTableColumns {
columns := p.expectKeywordLike("COLUMNS").Pos
p.expect("(")
items := parseCommaSeparatedList(p, p.parseSelectItem)
rparen := p.expect(")").Pos
return &ast.GraphTableColumns{
Columns: columns,
Rparen: rparen,
Items: items,
}
}

func (p *Parser) parseTVFCallExpr(ids []*ast.Ident) *ast.TVFCallExpr {
Expand Down Expand Up @@ -1395,6 +1456,8 @@ func (p *Parser) parseTableExprSuffix(join ast.TableExpr) ast.TableExpr {
j.Sample = sample
case *ast.ParenTableExpr:
j.Sample = sample
case *ast.GraphTableExpr:
j.Sample = sample
default:
panic(fmt.Sprintf("BUG: unexpected join: %#v", join))
}
Expand Down
1 change: 1 addition & 0 deletions testdata/input/query/!bad_graph_table_return_columns.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SELECT * FROM GRAPH_TABLE (FinGraph MATCH (n:Person) RETURN n COLUMNS (n.id))
1 change: 1 addition & 0 deletions testdata/input/query/graph_table_alias.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SELECT t.v FROM GRAPH_TABLE (FinGraph LET x = 1 RETURN x AS v) t
1 change: 1 addition & 0 deletions testdata/input/query/graph_table_as_alias.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SELECT t.v FROM GRAPH_TABLE (FinGraph LET x = 1 RETURN x AS v) AS t TABLESAMPLE BERNOULLI (10 PERCENT)
4 changes: 4 additions & 0 deletions testdata/input/query/graph_table_join.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
SELECT a.id, g.v
FROM Accounts AS a
JOIN GRAPH_TABLE (FinGraph LET x = 1 RETURN x AS v) AS g
ON a.id = g.v
1 change: 1 addition & 0 deletions testdata/input/query/graph_table_match_columns.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SELECT name, number FROM GRAPH_TABLE (FinGraph MATCH (n:Person) COLUMNS (n.name AS name, 1 AS number))
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SELECT * FROM GRAPH_TABLE (FinGraph MATCH (n:Person) COLUMNS (n.*, 1 AS number))
1 change: 1 addition & 0 deletions testdata/input/query/graph_table_match_no_columns.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SELECT n.id FROM GRAPH_TABLE (FinGraph MATCH (n:Person)) AS g
1 change: 1 addition & 0 deletions testdata/input/query/graph_table_match_return.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SELECT id FROM GRAPH_TABLE (FinGraph MATCH (n:Person) RETURN n.id AS id)
1 change: 1 addition & 0 deletions testdata/input/query/graph_table_minimal.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SELECT * FROM GRAPH_TABLE (FinGraph LET x = 1 RETURN x AS v)
1 change: 1 addition & 0 deletions testdata/input/query/graph_table_next.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SELECT * FROM GRAPH_TABLE (FinGraph LET a = 1 RETURN a AS x NEXT FILTER WHERE x > 0 RETURN x) AS t
1 change: 1 addition & 0 deletions testdata/input/query/graph_table_qualified_graph.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SELECT * FROM GRAPH_TABLE (schema.FinGraph FILTER WHERE TRUE RETURN 1 AS v) AS t
6 changes: 6 additions & 0 deletions testdata/input/query/graph_table_tablesample.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
SELECT *
FROM GRAPH_TABLE(
FinGraph
LET x = 1
RETURN x AS v
) TABLESAMPLE BERNOULLI (10 PERCENT)
Loading
Loading