From 6ab0f40e1c5df22ec72337035e0be64a607919c7 Mon Sep 17 00:00:00 2001 From: Hiroya Fujinami Date: Mon, 13 Jan 2025 16:48:21 +0900 Subject: [PATCH] Add `walk` package to traverse an AST (#265) * Add `walk` package to traverse an AST - Add `ast-gen-walk` - Remove `ColumnDefOptions` and `VectorIndexOption` because they are unused. * Move `Walk` functions to `ast` package * Fix format * Fix BinaryExpr.end * Do make gen * Add pos-end test into parser_test.go * Fix Join.end * Fix Selector.end * Fix AlterDatabase.end * Fix CreateTable.end * Fix AlterIndex.end * Fix end of ChangeStream * Fix TableConstraint.ConstraintPos * Make gen * go test --update * Fix interval notation * Update pos/end test * Add `Field` and `Index` to `Visitor` * Add `WalkMany` and other `Many` variants * Check and fix the field order * Update conventions * Update ast/ast.go Co-authored-by: apstndb <803393+apstndb@users.noreply.github.com> * Update Go version on CI * Update walk_internal.go * Fix PropertyGraphEdgeElementKeys pos * Insert an empty line --------- Co-authored-by: apstndb <803393+apstndb@users.noreply.github.com> --- .github/workflows/go.yml | 4 +- ast/ast.go | 68 +- ast/node_wrapper.go | 22 + ast/pos.go | 34 +- ast/pos_util.go | 13 - ast/walk.go | 112 +++ ast/walk_internal.go | 943 ++++++++++++++++++ go.mod | 2 +- parser.go | 14 +- parser_test.go | 105 ++ .../result/ddl/alter_table_add_check.sql.txt | 3 +- .../ddl/alter_table_add_foreign_key.sql.txt | 3 +- .../create_change_stream_for_table.sql.txt | 1 + .../create_change_stream_for_tables.sql.txt | 2 + testdata/result/ddl/create_table.sql.txt | 5 +- .../result/ddl/create_table_cluster.sql.txt | 5 +- ...le_cluster_and_row_deletion_policy.sql.txt | 5 +- ..._table_cluster_on_delete_no_action.sql.txt | 5 +- ...create_table_cluster_set_on_delete.sql.txt | 5 +- .../ddl/create_table_for_format_test.sql.txt | 7 +- .../ddl/create_table_fulltext_albums.sql.txt | 7 +- .../ddl/create_table_if_not_exists.sql.txt | 7 +- .../create_table_row_deletion_policy.sql.txt | 5 +- .../result/ddl/create_table_synonyms.sql.txt | 5 +- .../create_table_synonyms_abnormal.sql.txt | 7 +- .../ddl/create_table_trailing_comma.sql.txt | 5 +- .../result/ddl/create_table_types.sql.txt | 7 +- ...create_table_with_identity_columns.sql.txt | 5 +- ...reate_table_with_sequence_function.sql.txt | 5 +- ...med_schema_create_table_backquoted.sql.txt | 5 +- .../ddl/named_schemas_create_table.sql.txt | 5 +- ...d_schemas_create_table_foreign_key.sql.txt | 5 +- ...ed_schemas_create_table_interleave.sql.txt | 5 +- testdata/result/query/select_cast.sql.txt | 4 +- .../select_complex_with_array_path.sql.txt | 2 +- ...ect_complex_with_unnest_array_path.sql.txt | 2 +- .../query/select_from_implicit_unnest.sql.txt | 2 +- ...ct_from_inner_join_path_table_expr.sql.txt | 2 +- .../query/select_from_join_unnest.sql.txt | 2 +- ...ect_from_left_join_path_table_expr.sql.txt | 2 +- .../select_from_ml_predict_textbison.sql.txt | 2 +- .../result/query/select_nest_complex.sql.txt | 2 +- .../select_singer_with_cross_join.sql.txt | 2 +- .../select_singer_with_full_join.sql.txt | 2 +- .../select_singer_with_hash_join.sql.txt | 8 +- .../query/select_singer_with_join.sql.txt | 2 +- .../select_singer_with_join_hint.sql.txt | 164 +-- .../select_singer_with_join_twice.sql.txt | 4 +- .../select_singer_with_join_using.sql.txt | 2 +- .../select_singer_with_join_various.sql.txt | 18 +- .../select_singer_with_tablesample.sql.txt | 4 +- ...ect_singer_with_toplevel_join_hint.sql.txt | 2 +- ...select_tablesample_with_cross_join.sql.txt | 2 +- .../statement/alter_table_add_check.sql.txt | 3 +- .../alter_table_add_foreign_key.sql.txt | 3 +- .../create_change_stream_for_table.sql.txt | 1 + .../create_change_stream_for_tables.sql.txt | 2 + .../result/statement/create_table.sql.txt | 5 +- .../statement/create_table_cluster.sql.txt | 5 +- ...le_cluster_and_row_deletion_policy.sql.txt | 5 +- ..._table_cluster_on_delete_no_action.sql.txt | 5 +- ...create_table_cluster_set_on_delete.sql.txt | 5 +- .../create_table_for_format_test.sql.txt | 7 +- .../create_table_fulltext_albums.sql.txt | 7 +- .../create_table_if_not_exists.sql.txt | 7 +- .../create_table_row_deletion_policy.sql.txt | 5 +- .../statement/create_table_synonyms.sql.txt | 5 +- .../create_table_synonyms_abnormal.sql.txt | 7 +- .../create_table_trailing_comma.sql.txt | 5 +- .../statement/create_table_types.sql.txt | 7 +- ...create_table_with_identity_columns.sql.txt | 5 +- ...reate_table_with_sequence_function.sql.txt | 5 +- ...med_schema_create_table_backquoted.sql.txt | 5 +- .../named_schemas_create_table.sql.txt | 5 +- ...d_schemas_create_table_foreign_key.sql.txt | 5 +- ...ed_schemas_create_table_interleave.sql.txt | 5 +- testdata/result/statement/select_cast.sql.txt | 4 +- .../select_complex_with_array_path.sql.txt | 2 +- ...ect_complex_with_unnest_array_path.sql.txt | 2 +- .../select_from_implicit_unnest.sql.txt | 2 +- ...ct_from_inner_join_path_table_expr.sql.txt | 2 +- .../statement/select_from_join_unnest.sql.txt | 2 +- ...ect_from_left_join_path_table_expr.sql.txt | 2 +- .../select_from_ml_predict_textbison.sql.txt | 2 +- .../statement/select_nest_complex.sql.txt | 2 +- .../select_singer_with_cross_join.sql.txt | 2 +- .../select_singer_with_full_join.sql.txt | 2 +- .../select_singer_with_hash_join.sql.txt | 8 +- .../statement/select_singer_with_join.sql.txt | 2 +- .../select_singer_with_join_hint.sql.txt | 164 +-- .../select_singer_with_join_twice.sql.txt | 4 +- .../select_singer_with_join_using.sql.txt | 2 +- .../select_singer_with_join_various.sql.txt | 18 +- .../select_singer_with_tablesample.sql.txt | 4 +- ...ect_singer_with_toplevel_join_hint.sql.txt | 2 +- ...select_tablesample_with_cross_join.sql.txt | 2 +- tools/gen-ast-walk/main.go | 157 +++ tools/util/astcatalog/load.go | 4 +- 98 files changed, 1754 insertions(+), 412 deletions(-) create mode 100644 ast/node_wrapper.go create mode 100644 ast/walk.go create mode 100644 ast/walk_internal.go create mode 100644 tools/gen-ast-walk/main.go diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 45e0ecf5..b166784e 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -11,10 +11,10 @@ jobs: runs-on: ubuntu-latest steps: - - name: Set up Go 1.20 + - name: Set up Go uses: actions/setup-go@v5 with: - go-version: "1.22" + go-version: "1.23" id: go - name: Check out code into the Go module directory diff --git a/ast/ast.go b/ast/ast.go index f6da0e2c..0d941103 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -39,11 +39,12 @@ package ast // // Conventions: // -// - Each node interface (except for Node) should have isXXX method (XXX must be a name of the interface itself). -// - `isXXX` methods should be defined after the interface definition -// and the receiver should be the non-pointer node struct type. -// - Each node struct should have pos and end comments. -// - Each node struct should have template lines in its doc comment. +// - Each node interface (except for Node) must have isXXX method (XXX is a name of the interface itself). +// - `isXXX` methods must be defined after the interface definition +// and the receiver must be the non-pointer node struct type. +// - Each node struct must have pos and end comments. +// - Each node struct must have template lines in its doc comment. +// - The fields of each node must be ordered by the position. //go:generate go run ../tools/gen-ast-pos/main.go -astfile ast.go -constfile ast_const.go -outfile pos.go @@ -1177,12 +1178,13 @@ type ParenTableExpr struct { // {{.Cond | sqlOpt}} type Join struct { // pos = Left.pos - // end = (Cond ?? Right).pos + // end = (Cond ?? Right).end - Op JoinOp - Method JoinMethod - Hint *Hint // optional - Left, Right TableExpr + Left TableExpr + Op JoinOp + Method JoinMethod + Hint *Hint // optional + Right TableExpr // nil when Op is CrossJoin // optional when Right is PathTableExpr or Unnest @@ -1252,7 +1254,7 @@ type TableSampleSize struct { // {{.Left | sql}} {{.Op}} {{.Right | sql}} type BinaryExpr struct { // pos = Left.pos - // end = Right.pos + // end = Right.end Op BinaryOp @@ -1364,7 +1366,7 @@ type BetweenExpr struct { // {{.Expr | sql}}.{{.Ident | sql}} type SelectorExpr struct { // pos = Expr.pos - // end = Ident.pos + // end = Ident.end Expr Expr Ident *Ident @@ -2255,7 +2257,7 @@ type CreateDatabase struct { // ALTER DATABASE {{.Name | sql}} SET {{.Options | sql}} type AlterDatabase struct { // pos = Alter - // end = Name.end + // end = Options.end Alter token.Pos // position of "ALTER" keyword @@ -2383,10 +2385,11 @@ type DropProtoBundle struct { // the original order of them, please sort them by their `Pos()`. type CreateTable struct { // pos = Create - // end = RowDeletionPolicy.end || Cluster.end || Rparen + 1 + // end = RowDeletionPolicy.end || Cluster.end || PrimaryKeyRparen + 1 || Rparen + 1 - Create token.Pos // position of "CREATE" keyword - Rparen token.Pos // position of ")" of PRIMARY KEY clause + Create token.Pos // position of "CREATE" keyword + Rparen token.Pos // position of ")" of end of column definitions + PrimaryKeyRparen token.Pos // position of ")" of PRIMARY KEY clause, optional IfNotExists bool Name *Path @@ -2530,19 +2533,6 @@ type IdentityColumn struct { Params []SequenceParam // if Rparen.Invalid() then len(Param) = 0 else len(Param) > 0 } -// ColumnDefOption is options for column definition. -// -// OPTIONS(allow_commit_timestamp = {{if .AllowCommitTimestamp}}true{{else}null{{end}}}) -type ColumnDefOptions struct { - // pos = Options - // end = Rparen + 1 - - Options token.Pos // position of "OPTIONS" keyword - Rparen token.Pos // position of ")" - - AllowCommitTimestamp bool -} - // TableConstraint is table constraint in CREATE TABLE and ALTER TABLE. // // {{if .Name}}CONSTRAINT {{.Name}}{{end}}{{.Constraint | sql}} @@ -2703,7 +2693,7 @@ type AlterIndex struct { // {{.NoSkipRange | sqlOpt}} type AlterSequence struct { // pos = Alter - // end = Options.end + // end = (NoSkipRange ?? SkipRange ?? RestartCounterWith ?? Options).end Alter token.Pos // position of "ALTER" keyword @@ -3062,17 +3052,6 @@ type CreateVectorIndex struct { Options *Options } -// VectorIndexOption is OPTIONS record node. -// -// {{.Key | sql}}={{.Expr | sql}} -type VectorIndexOption struct { - // pos = Key.pos - // end = Value.end - - Key *Ident - Value Expr -} - // CreateChangeStream is CREATE CHANGE STREAM statement node. // // CREATE CHANGE STREAM {{.Name | sql}} {{.For | sqlOpt}} {{.Options | sqlOpt}} @@ -3092,7 +3071,7 @@ type CreateChangeStream struct { // FOR ALL type ChangeStreamForAll struct { // pos = For - // end = All + // end = All + 3 For token.Pos // position of "FOR" keyword All token.Pos // position of "ALL" keyword @@ -3115,7 +3094,7 @@ type ChangeStreamForTables struct { // {{.TableName | sql}}{{if .Columns}}({{.Columns | sqlJoin ","}}){{end}} type ChangeStreamForTable struct { // pos = TableName.pos - // end = TableName.end || Rparen + 1 + // end = Rparen + 1 || TableName.end Rparen token.Pos // position of ")" @@ -3808,8 +3787,9 @@ type PropertyGraphNodeElementKey struct { // // {{.Element | sqlOpt}} {{.Source | sql}} {{.Destination | sql}} type PropertyGraphEdgeElementKeys struct { - // pos = Element.pos + // pos = (Element ?? Source).pos // end = Destination.end + Element *PropertyGraphElementKey // optional Source *PropertyGraphSourceKey Destination *PropertyGraphDestinationKey diff --git a/ast/node_wrapper.go b/ast/node_wrapper.go new file mode 100644 index 00000000..b5c181a2 --- /dev/null +++ b/ast/node_wrapper.go @@ -0,0 +1,22 @@ +package ast + +type comparableNode = interface { + comparable + Node +} + +func wrapNode[T comparableNode](node T) Node { + var zero T + if node == zero { + return nil + } + return node +} + +func wrapNodes[T Node](nodes []T) []Node { + result := make([]Node, 0, len(nodes)) + for _, node := range nodes { + result = append(result, node) + } + return result +} diff --git a/ast/pos.go b/ast/pos.go index d53ead76..b6ff62b2 100644 --- a/ast/pos.go +++ b/ast/pos.go @@ -371,7 +371,7 @@ func (j *Join) Pos() token.Pos { } func (j *Join) End() token.Pos { - return nodePos(nodeChoice(wrapNode(j.Cond), wrapNode(j.Right))) + return nodeEnd(nodeChoice(wrapNode(j.Cond), wrapNode(j.Right))) } func (o *On) Pos() token.Pos { @@ -411,7 +411,7 @@ func (b *BinaryExpr) Pos() token.Pos { } func (b *BinaryExpr) End() token.Pos { - return nodePos(wrapNode(b.Right)) + return nodeEnd(wrapNode(b.Right)) } func (u *UnaryExpr) Pos() token.Pos { @@ -483,7 +483,7 @@ func (s *SelectorExpr) Pos() token.Pos { } func (s *SelectorExpr) End() token.Pos { - return nodePos(wrapNode(s.Ident)) + return nodeEnd(wrapNode(s.Ident)) } func (i *IndexExpr) Pos() token.Pos { @@ -1011,7 +1011,7 @@ func (a *AlterDatabase) Pos() token.Pos { } func (a *AlterDatabase) End() token.Pos { - return nodeEnd(wrapNode(a.Name)) + return nodeEnd(wrapNode(a.Options)) } func (c *CreatePlacement) Pos() token.Pos { @@ -1083,7 +1083,7 @@ func (c *CreateTable) Pos() token.Pos { } func (c *CreateTable) End() token.Pos { - return posChoice(nodeEnd(wrapNode(c.RowDeletionPolicy)), nodeEnd(wrapNode(c.Cluster)), posAdd(c.Rparen, 1)) + return posChoice(nodeEnd(wrapNode(c.RowDeletionPolicy)), nodeEnd(wrapNode(c.Cluster)), posAdd(c.PrimaryKeyRparen, 1), posAdd(c.Rparen, 1)) } func (s *Synonym) Pos() token.Pos { @@ -1158,14 +1158,6 @@ func (i *IdentityColumn) End() token.Pos { return posChoice(posAdd(i.Rparen, 1), posAdd(i.Identity, 8)) } -func (c *ColumnDefOptions) Pos() token.Pos { - return c.Options -} - -func (c *ColumnDefOptions) End() token.Pos { - return posAdd(c.Rparen, 1) -} - func (t *TableConstraint) Pos() token.Pos { return posChoice(t.ConstraintPos, nodePos(wrapNode(t.Constraint))) } @@ -1259,7 +1251,7 @@ func (a *AlterSequence) Pos() token.Pos { } func (a *AlterSequence) End() token.Pos { - return nodeEnd(wrapNode(a.Options)) + return nodeEnd(nodeChoice(wrapNode(a.NoSkipRange), wrapNode(a.SkipRange), wrapNode(a.RestartCounterWith), wrapNode(a.Options))) } func (a *AlterChangeStream) Pos() token.Pos { @@ -1478,14 +1470,6 @@ func (c *CreateVectorIndex) End() token.Pos { return nodeEnd(wrapNode(c.Options)) } -func (v *VectorIndexOption) Pos() token.Pos { - return nodePos(wrapNode(v.Key)) -} - -func (v *VectorIndexOption) End() token.Pos { - return nodeEnd(wrapNode(v.Value)) -} - func (c *CreateChangeStream) Pos() token.Pos { return c.Create } @@ -1499,7 +1483,7 @@ func (c *ChangeStreamForAll) Pos() token.Pos { } func (c *ChangeStreamForAll) End() token.Pos { - return c.All + return posAdd(c.All, 3) } func (c *ChangeStreamForTables) Pos() token.Pos { @@ -1515,7 +1499,7 @@ func (c *ChangeStreamForTable) Pos() token.Pos { } func (c *ChangeStreamForTable) End() token.Pos { - return posChoice(nodeEnd(wrapNode(c.TableName)), posAdd(c.Rparen, 1)) + return posChoice(posAdd(c.Rparen, 1), nodeEnd(wrapNode(c.TableName))) } func (c *ChangeStreamSetFor) Pos() token.Pos { @@ -1911,7 +1895,7 @@ func (p *PropertyGraphNodeElementKey) End() token.Pos { } func (p *PropertyGraphEdgeElementKeys) Pos() token.Pos { - return nodePos(wrapNode(p.Element)) + return nodePos(nodeChoice(wrapNode(p.Element), wrapNode(p.Source))) } func (p *PropertyGraphEdgeElementKeys) End() token.Pos { diff --git a/ast/pos_util.go b/ast/pos_util.go index afbe987f..1f7a478a 100644 --- a/ast/pos_util.go +++ b/ast/pos_util.go @@ -6,11 +6,6 @@ import ( // This file contains utility types/functions for pos.go. -type comparableNode = interface { - comparable - Node -} - func nodePos(n Node) token.Pos { if n == nil { return token.InvalidPos @@ -42,14 +37,6 @@ func posAdd(p token.Pos, x int) token.Pos { return token.Pos(int(p) + x) } -func wrapNode[T comparableNode](node T) Node { - var zero T - if node == zero { - return nil - } - return node -} - func nodeChoice(ns ...Node) Node { for _, n := range ns { if n != nil { diff --git a/ast/walk.go b/ast/walk.go new file mode 100644 index 00000000..c2540dbb --- /dev/null +++ b/ast/walk.go @@ -0,0 +1,112 @@ +package ast + +import ( + "iter" +) + +//go:generate go run ../tools/gen-ast-walk/main.go -astfile ast.go -constfile ast_const.go -outfile walk_internal.go + +// Visitor is an interface for visiting AST nodes. +// If the result of Visit is nil, the node will not be traversed. +type Visitor interface { + Visit(node Node) Visitor + VisitMany(nodes []Node) Visitor + Field(name string) Visitor + Index(index int) Visitor +} + +type stackItem struct { + node Node + nodes []Node + visitor Visitor +} + +// Walk traverses an AST node in depth-first order. +func Walk(node Node, v Visitor) { + stack := []*stackItem{{node: node, visitor: v}} + walkMain(stack) +} + +// Walk traverses AST nodes in depth-first order. +func WalkMany[T Node](nodes []T, v Visitor) { + stack := []*stackItem{{nodes: wrapNodes(nodes), visitor: v}} + walkMain(stack) +} + +func walkMain(stack []*stackItem) { + for len(stack) > 0 { + last := stack[len(stack)-1] + stack = stack[:len(stack)-1] + if last.node == nil && last.nodes == nil { + continue + } + + if last.nodes != nil { + v := last.visitor.VisitMany(last.nodes) + for i := len(last.nodes) - 1; i >= 0; i-- { + stack = append(stack, &stackItem{node: last.nodes[i], visitor: v.Index(i)}) + } + continue + } + + v := last.visitor.Visit(last.node) + if v == nil { + continue + } + + stack = walkInternal(last.node, v, stack) + } +} + +type inspector func(Node) bool + +func (f inspector) Visit(node Node) Visitor { + if f(node) { + return f + } + return nil +} + +func (f inspector) VisitMany(nodes []Node) Visitor { + return f +} + +func (f inspector) Field(name string) Visitor { + return f +} + +func (f inspector) Index(index int) Visitor { + return f +} + +// Inspect traverses an AST node in depth-first order and calls f for each node. +func Inspect(node Node, f func(Node) bool) { + Walk(node, inspector(f)) +} + +// InspectMany traverses AST nodes in depth-first order and calls f for each node. +func InspectMany[T Node](nodes []T, f func(Node) bool) { + WalkMany(nodes, inspector(f)) +} + +// Preorder returns an iterator that traverses an AST in depth-first preorder. +func Preorder(node Node) iter.Seq[Node] { + return func(yield func(Node) bool) { + ok := true + Inspect(node, func(n Node) bool { + ok = ok && yield(n) + return ok + }) + } +} + +// PreorderMany returns an iterator that traverses AST nodes in depth-first preorder. +func PreorderMany[T Node](nodes []T) iter.Seq[Node] { + return func(yield func(Node) bool) { + ok := true + InspectMany(nodes, func(n Node) bool { + ok = ok && yield(n) + return ok + }) + } +} diff --git a/ast/walk_internal.go b/ast/walk_internal.go new file mode 100644 index 00000000..f6a1bb12 --- /dev/null +++ b/ast/walk_internal.go @@ -0,0 +1,943 @@ +// Code generated by tools/gen-ast-walk; DO NOT EDIT. + +package ast + +func walkInternal(node Node, v Visitor, stack []*stackItem) []*stackItem { + switch n := node.(type) { + case *BadNode: + // nothing to do + + case *BadStatement: + stack = append(stack, &stackItem{node: wrapNode(n.BadNode), visitor: v.Field("BadNode")}) + + case *BadQueryExpr: + stack = append(stack, &stackItem{node: wrapNode(n.BadNode), visitor: v.Field("BadNode")}) + + case *BadExpr: + stack = append(stack, &stackItem{node: wrapNode(n.BadNode), visitor: v.Field("BadNode")}) + + case *BadType: + stack = append(stack, &stackItem{node: wrapNode(n.BadNode), visitor: v.Field("BadNode")}) + + case *BadDDL: + stack = append(stack, &stackItem{node: wrapNode(n.BadNode), visitor: v.Field("BadNode")}) + + case *BadDML: + stack = append(stack, &stackItem{node: wrapNode(n.BadNode), visitor: v.Field("BadNode")}) + + case *QueryStatement: + 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 *Query: + stack = append(stack, &stackItem{nodes: wrapNodes(n.PipeOperators), visitor: v.Field("PipeOperators")}) + stack = append(stack, &stackItem{node: wrapNode(n.Limit), visitor: v.Field("Limit")}) + stack = append(stack, &stackItem{node: wrapNode(n.OrderBy), visitor: v.Field("OrderBy")}) + stack = append(stack, &stackItem{node: wrapNode(n.Query), visitor: v.Field("Query")}) + stack = append(stack, &stackItem{node: wrapNode(n.With), visitor: v.Field("With")}) + + case *Hint: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Records), visitor: v.Field("Records")}) + + case *HintRecord: + stack = append(stack, &stackItem{node: wrapNode(n.Value), visitor: v.Field("Value")}) + stack = append(stack, &stackItem{node: wrapNode(n.Key), visitor: v.Field("Key")}) + + case *With: + stack = append(stack, &stackItem{nodes: wrapNodes(n.CTEs), visitor: v.Field("CTEs")}) + + case *CTE: + stack = append(stack, &stackItem{node: wrapNode(n.QueryExpr), visitor: v.Field("QueryExpr")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *Select: + stack = append(stack, &stackItem{node: wrapNode(n.Having), visitor: v.Field("Having")}) + stack = append(stack, &stackItem{node: wrapNode(n.GroupBy), visitor: v.Field("GroupBy")}) + stack = append(stack, &stackItem{node: wrapNode(n.Where), visitor: v.Field("Where")}) + stack = append(stack, &stackItem{node: wrapNode(n.From), visitor: v.Field("From")}) + stack = append(stack, &stackItem{nodes: wrapNodes(n.Results), visitor: v.Field("Results")}) + stack = append(stack, &stackItem{node: wrapNode(n.As), visitor: v.Field("As")}) + + case *AsStruct: + // nothing to do + + case *AsValue: + // nothing to do + + case *AsTypeName: + stack = append(stack, &stackItem{node: wrapNode(n.TypeName), visitor: v.Field("TypeName")}) + + case *FromQuery: + stack = append(stack, &stackItem{node: wrapNode(n.From), visitor: v.Field("From")}) + + case *CompoundQuery: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Queries), visitor: v.Field("Queries")}) + + case *SubQuery: + stack = append(stack, &stackItem{node: wrapNode(n.Query), visitor: v.Field("Query")}) + + case *StarModifierExcept: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Columns), visitor: v.Field("Columns")}) + + case *StarModifierReplaceItem: + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *StarModifierReplace: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Columns), visitor: v.Field("Columns")}) + + case *Star: + stack = append(stack, &stackItem{node: wrapNode(n.Replace), visitor: v.Field("Replace")}) + stack = append(stack, &stackItem{node: wrapNode(n.Except), visitor: v.Field("Except")}) + + case *DotStar: + stack = append(stack, &stackItem{node: wrapNode(n.Replace), visitor: v.Field("Replace")}) + stack = append(stack, &stackItem{node: wrapNode(n.Except), visitor: v.Field("Except")}) + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *Alias: + stack = append(stack, &stackItem{node: wrapNode(n.As), visitor: v.Field("As")}) + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *AsAlias: + stack = append(stack, &stackItem{node: wrapNode(n.Alias), visitor: v.Field("Alias")}) + + case *ExprSelectItem: + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *From: + stack = append(stack, &stackItem{node: wrapNode(n.Source), visitor: v.Field("Source")}) + + case *Where: + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *GroupBy: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Exprs), visitor: v.Field("Exprs")}) + + case *Having: + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *OrderBy: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Items), visitor: v.Field("Items")}) + + case *OrderByItem: + stack = append(stack, &stackItem{node: wrapNode(n.Collate), visitor: v.Field("Collate")}) + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *Collate: + stack = append(stack, &stackItem{node: wrapNode(n.Value), visitor: v.Field("Value")}) + + case *Limit: + stack = append(stack, &stackItem{node: wrapNode(n.Offset), visitor: v.Field("Offset")}) + stack = append(stack, &stackItem{node: wrapNode(n.Count), visitor: v.Field("Count")}) + + case *Offset: + stack = append(stack, &stackItem{node: wrapNode(n.Value), visitor: v.Field("Value")}) + + case *PipeSelect: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Results), visitor: v.Field("Results")}) + stack = append(stack, &stackItem{node: wrapNode(n.As), visitor: v.Field("As")}) + + case *PipeWhere: + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *Unnest: + stack = append(stack, &stackItem{node: wrapNode(n.Sample), visitor: v.Field("Sample")}) + stack = append(stack, &stackItem{node: wrapNode(n.WithOffset), visitor: v.Field("WithOffset")}) + stack = append(stack, &stackItem{node: wrapNode(n.As), visitor: v.Field("As")}) + stack = append(stack, &stackItem{node: wrapNode(n.Hint), visitor: v.Field("Hint")}) + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *WithOffset: + stack = append(stack, &stackItem{node: wrapNode(n.As), visitor: v.Field("As")}) + + case *TableName: + stack = append(stack, &stackItem{node: wrapNode(n.Sample), visitor: v.Field("Sample")}) + stack = append(stack, &stackItem{node: wrapNode(n.As), visitor: v.Field("As")}) + stack = append(stack, &stackItem{node: wrapNode(n.Hint), visitor: v.Field("Hint")}) + stack = append(stack, &stackItem{node: wrapNode(n.Table), visitor: v.Field("Table")}) + + case *PathTableExpr: + stack = append(stack, &stackItem{node: wrapNode(n.Sample), visitor: v.Field("Sample")}) + stack = append(stack, &stackItem{node: wrapNode(n.WithOffset), visitor: v.Field("WithOffset")}) + stack = append(stack, &stackItem{node: wrapNode(n.As), visitor: v.Field("As")}) + stack = append(stack, &stackItem{node: wrapNode(n.Hint), visitor: v.Field("Hint")}) + stack = append(stack, &stackItem{node: wrapNode(n.Path), visitor: v.Field("Path")}) + + case *SubQueryTableExpr: + stack = append(stack, &stackItem{node: wrapNode(n.Sample), visitor: v.Field("Sample")}) + stack = append(stack, &stackItem{node: wrapNode(n.As), visitor: v.Field("As")}) + stack = append(stack, &stackItem{node: wrapNode(n.Query), visitor: v.Field("Query")}) + + case *ParenTableExpr: + stack = append(stack, &stackItem{node: wrapNode(n.Sample), visitor: v.Field("Sample")}) + stack = append(stack, &stackItem{node: wrapNode(n.Source), visitor: v.Field("Source")}) + + case *Join: + stack = append(stack, &stackItem{node: wrapNode(n.Cond), visitor: v.Field("Cond")}) + stack = append(stack, &stackItem{node: wrapNode(n.Right), visitor: v.Field("Right")}) + stack = append(stack, &stackItem{node: wrapNode(n.Hint), visitor: v.Field("Hint")}) + stack = append(stack, &stackItem{node: wrapNode(n.Left), visitor: v.Field("Left")}) + + case *On: + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *Using: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Idents), visitor: v.Field("Idents")}) + + case *TableSample: + stack = append(stack, &stackItem{node: wrapNode(n.Size), visitor: v.Field("Size")}) + + case *TableSampleSize: + stack = append(stack, &stackItem{node: wrapNode(n.Value), visitor: v.Field("Value")}) + + case *BinaryExpr: + stack = append(stack, &stackItem{node: wrapNode(n.Right), visitor: v.Field("Right")}) + stack = append(stack, &stackItem{node: wrapNode(n.Left), visitor: v.Field("Left")}) + + case *UnaryExpr: + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *InExpr: + stack = append(stack, &stackItem{node: wrapNode(n.Right), visitor: v.Field("Right")}) + stack = append(stack, &stackItem{node: wrapNode(n.Left), visitor: v.Field("Left")}) + + case *UnnestInCondition: + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *SubQueryInCondition: + stack = append(stack, &stackItem{node: wrapNode(n.Query), visitor: v.Field("Query")}) + + case *ValuesInCondition: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Exprs), visitor: v.Field("Exprs")}) + + case *IsNullExpr: + stack = append(stack, &stackItem{node: wrapNode(n.Left), visitor: v.Field("Left")}) + + case *IsBoolExpr: + stack = append(stack, &stackItem{node: wrapNode(n.Left), visitor: v.Field("Left")}) + + case *BetweenExpr: + stack = append(stack, &stackItem{node: wrapNode(n.RightEnd), visitor: v.Field("RightEnd")}) + stack = append(stack, &stackItem{node: wrapNode(n.RightStart), visitor: v.Field("RightStart")}) + stack = append(stack, &stackItem{node: wrapNode(n.Left), visitor: v.Field("Left")}) + + case *SelectorExpr: + stack = append(stack, &stackItem{node: wrapNode(n.Ident), visitor: v.Field("Ident")}) + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *IndexExpr: + stack = append(stack, &stackItem{node: wrapNode(n.Index), visitor: v.Field("Index")}) + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *SubscriptSpecifierKeyword: + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *CallExpr: + stack = append(stack, &stackItem{node: wrapNode(n.Hint), visitor: v.Field("Hint")}) + stack = append(stack, &stackItem{node: wrapNode(n.Having), visitor: v.Field("Having")}) + stack = append(stack, &stackItem{node: wrapNode(n.NullHandling), visitor: v.Field("NullHandling")}) + stack = append(stack, &stackItem{nodes: wrapNodes(n.NamedArgs), visitor: v.Field("NamedArgs")}) + stack = append(stack, &stackItem{nodes: wrapNodes(n.Args), visitor: v.Field("Args")}) + stack = append(stack, &stackItem{node: wrapNode(n.Func), visitor: v.Field("Func")}) + + case *TVFCallExpr: + stack = append(stack, &stackItem{node: wrapNode(n.Sample), visitor: v.Field("Sample")}) + stack = append(stack, &stackItem{node: wrapNode(n.Hint), visitor: v.Field("Hint")}) + stack = append(stack, &stackItem{nodes: wrapNodes(n.NamedArgs), visitor: v.Field("NamedArgs")}) + stack = append(stack, &stackItem{nodes: wrapNodes(n.Args), visitor: v.Field("Args")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *ExprArg: + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *IntervalArg: + stack = append(stack, &stackItem{node: wrapNode(n.Unit), visitor: v.Field("Unit")}) + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *SequenceArg: + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *LambdaArg: + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + stack = append(stack, &stackItem{nodes: wrapNodes(n.Args), visitor: v.Field("Args")}) + + case *ModelArg: + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *TableArg: + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *NamedArg: + stack = append(stack, &stackItem{node: wrapNode(n.Value), visitor: v.Field("Value")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *IgnoreNulls: + // nothing to do + + case *RespectNulls: + // nothing to do + + case *HavingMax: + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *HavingMin: + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *CountStarExpr: + // nothing to do + + case *ExtractExpr: + stack = append(stack, &stackItem{node: wrapNode(n.AtTimeZone), visitor: v.Field("AtTimeZone")}) + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + stack = append(stack, &stackItem{node: wrapNode(n.Part), visitor: v.Field("Part")}) + + case *ReplaceFieldsArg: + stack = append(stack, &stackItem{node: wrapNode(n.Field), visitor: v.Field("Field")}) + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *ReplaceFieldsExpr: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Fields), visitor: v.Field("Fields")}) + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *AtTimeZone: + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *WithExprVar: + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *WithExpr: + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + stack = append(stack, &stackItem{nodes: wrapNodes(n.Vars), visitor: v.Field("Vars")}) + + case *CastExpr: + stack = append(stack, &stackItem{node: wrapNode(n.Type), visitor: v.Field("Type")}) + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *CaseExpr: + stack = append(stack, &stackItem{node: wrapNode(n.Else), visitor: v.Field("Else")}) + stack = append(stack, &stackItem{nodes: wrapNodes(n.Whens), visitor: v.Field("Whens")}) + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *CaseWhen: + stack = append(stack, &stackItem{node: wrapNode(n.Then), visitor: v.Field("Then")}) + stack = append(stack, &stackItem{node: wrapNode(n.Cond), visitor: v.Field("Cond")}) + + case *CaseElse: + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *IfExpr: + stack = append(stack, &stackItem{node: wrapNode(n.ElseResult), visitor: v.Field("ElseResult")}) + stack = append(stack, &stackItem{node: wrapNode(n.TrueResult), visitor: v.Field("TrueResult")}) + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *ParenExpr: + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *ScalarSubQuery: + stack = append(stack, &stackItem{node: wrapNode(n.Query), visitor: v.Field("Query")}) + + case *ArraySubQuery: + stack = append(stack, &stackItem{node: wrapNode(n.Query), visitor: v.Field("Query")}) + + case *ExistsSubQuery: + stack = append(stack, &stackItem{node: wrapNode(n.Query), visitor: v.Field("Query")}) + stack = append(stack, &stackItem{node: wrapNode(n.Hint), visitor: v.Field("Hint")}) + + case *Param: + // nothing to do + + case *Ident: + // nothing to do + + case *Path: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Idents), visitor: v.Field("Idents")}) + + case *ArrayLiteral: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Values), visitor: v.Field("Values")}) + stack = append(stack, &stackItem{node: wrapNode(n.Type), visitor: v.Field("Type")}) + + case *TupleStructLiteral: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Values), visitor: v.Field("Values")}) + + case *TypedStructLiteral: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Values), visitor: v.Field("Values")}) + stack = append(stack, &stackItem{nodes: wrapNodes(n.Fields), visitor: v.Field("Fields")}) + + case *TypelessStructLiteral: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Values), visitor: v.Field("Values")}) + + case *NullLiteral: + // nothing to do + + case *BoolLiteral: + // nothing to do + + case *IntLiteral: + // nothing to do + + case *FloatLiteral: + // nothing to do + + case *StringLiteral: + // nothing to do + + case *BytesLiteral: + // nothing to do + + case *DateLiteral: + stack = append(stack, &stackItem{node: wrapNode(n.Value), visitor: v.Field("Value")}) + + case *TimestampLiteral: + stack = append(stack, &stackItem{node: wrapNode(n.Value), visitor: v.Field("Value")}) + + case *NumericLiteral: + stack = append(stack, &stackItem{node: wrapNode(n.Value), visitor: v.Field("Value")}) + + case *JSONLiteral: + stack = append(stack, &stackItem{node: wrapNode(n.Value), visitor: v.Field("Value")}) + + case *NewConstructor: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Args), visitor: v.Field("Args")}) + stack = append(stack, &stackItem{node: wrapNode(n.Type), visitor: v.Field("Type")}) + + case *BracedNewConstructor: + stack = append(stack, &stackItem{node: wrapNode(n.Body), visitor: v.Field("Body")}) + stack = append(stack, &stackItem{node: wrapNode(n.Type), visitor: v.Field("Type")}) + + case *BracedConstructor: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Fields), visitor: v.Field("Fields")}) + + case *BracedConstructorField: + stack = append(stack, &stackItem{node: wrapNode(n.Value), visitor: v.Field("Value")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *BracedConstructorFieldValueExpr: + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *SimpleType: + // nothing to do + + case *ArrayType: + stack = append(stack, &stackItem{node: wrapNode(n.Item), visitor: v.Field("Item")}) + + case *StructType: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Fields), visitor: v.Field("Fields")}) + + case *StructField: + stack = append(stack, &stackItem{node: wrapNode(n.Type), visitor: v.Field("Type")}) + stack = append(stack, &stackItem{node: wrapNode(n.Ident), visitor: v.Field("Ident")}) + + case *NamedType: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Path), visitor: v.Field("Path")}) + + case *CastIntValue: + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *CastNumValue: + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *Options: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Records), visitor: v.Field("Records")}) + + case *OptionsDef: + stack = append(stack, &stackItem{node: wrapNode(n.Value), visitor: v.Field("Value")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *CreateSchema: + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *DropSchema: + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *CreateDatabase: + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *AlterDatabase: + stack = append(stack, &stackItem{node: wrapNode(n.Options), visitor: v.Field("Options")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *CreatePlacement: + stack = append(stack, &stackItem{node: wrapNode(n.Options), visitor: v.Field("Options")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *ProtoBundleTypes: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Types), visitor: v.Field("Types")}) + + case *CreateProtoBundle: + stack = append(stack, &stackItem{node: wrapNode(n.Types), visitor: v.Field("Types")}) + + case *AlterProtoBundle: + stack = append(stack, &stackItem{node: wrapNode(n.Delete), visitor: v.Field("Delete")}) + stack = append(stack, &stackItem{node: wrapNode(n.Update), visitor: v.Field("Update")}) + stack = append(stack, &stackItem{node: wrapNode(n.Insert), visitor: v.Field("Insert")}) + + case *AlterProtoBundleInsert: + stack = append(stack, &stackItem{node: wrapNode(n.Types), visitor: v.Field("Types")}) + + case *AlterProtoBundleUpdate: + stack = append(stack, &stackItem{node: wrapNode(n.Types), visitor: v.Field("Types")}) + + case *AlterProtoBundleDelete: + stack = append(stack, &stackItem{node: wrapNode(n.Types), visitor: v.Field("Types")}) + + case *DropProtoBundle: + // nothing to do + + case *CreateTable: + stack = append(stack, &stackItem{node: wrapNode(n.RowDeletionPolicy), visitor: v.Field("RowDeletionPolicy")}) + stack = append(stack, &stackItem{node: wrapNode(n.Cluster), visitor: v.Field("Cluster")}) + stack = append(stack, &stackItem{nodes: wrapNodes(n.Synonyms), visitor: v.Field("Synonyms")}) + stack = append(stack, &stackItem{nodes: wrapNodes(n.PrimaryKeys), visitor: v.Field("PrimaryKeys")}) + stack = append(stack, &stackItem{nodes: wrapNodes(n.TableConstraints), visitor: v.Field("TableConstraints")}) + stack = append(stack, &stackItem{nodes: wrapNodes(n.Columns), visitor: v.Field("Columns")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *Synonym: + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *CreateSequence: + stack = append(stack, &stackItem{node: wrapNode(n.Options), visitor: v.Field("Options")}) + stack = append(stack, &stackItem{nodes: wrapNodes(n.Params), visitor: v.Field("Params")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *SkipRange: + stack = append(stack, &stackItem{node: wrapNode(n.Max), visitor: v.Field("Max")}) + stack = append(stack, &stackItem{node: wrapNode(n.Min), visitor: v.Field("Min")}) + + case *StartCounterWith: + stack = append(stack, &stackItem{node: wrapNode(n.Counter), visitor: v.Field("Counter")}) + + case *BitReversedPositive: + // nothing to do + + case *ColumnDef: + stack = append(stack, &stackItem{node: wrapNode(n.Options), visitor: v.Field("Options")}) + stack = append(stack, &stackItem{node: wrapNode(n.DefaultSemantics), visitor: v.Field("DefaultSemantics")}) + stack = append(stack, &stackItem{node: wrapNode(n.Type), visitor: v.Field("Type")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *ColumnDefaultExpr: + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *GeneratedColumnExpr: + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *IdentityColumn: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Params), visitor: v.Field("Params")}) + + case *TableConstraint: + stack = append(stack, &stackItem{node: wrapNode(n.Constraint), visitor: v.Field("Constraint")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *ForeignKey: + stack = append(stack, &stackItem{nodes: wrapNodes(n.ReferenceColumns), visitor: v.Field("ReferenceColumns")}) + stack = append(stack, &stackItem{node: wrapNode(n.ReferenceTable), visitor: v.Field("ReferenceTable")}) + stack = append(stack, &stackItem{nodes: wrapNodes(n.Columns), visitor: v.Field("Columns")}) + + case *Check: + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *IndexKey: + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *Cluster: + stack = append(stack, &stackItem{node: wrapNode(n.TableName), visitor: v.Field("TableName")}) + + case *CreateRowDeletionPolicy: + stack = append(stack, &stackItem{node: wrapNode(n.RowDeletionPolicy), visitor: v.Field("RowDeletionPolicy")}) + + case *RowDeletionPolicy: + stack = append(stack, &stackItem{node: wrapNode(n.NumDays), visitor: v.Field("NumDays")}) + stack = append(stack, &stackItem{node: wrapNode(n.ColumnName), visitor: v.Field("ColumnName")}) + + case *CreateView: + stack = append(stack, &stackItem{node: wrapNode(n.Query), visitor: v.Field("Query")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *DropView: + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *AlterTable: + stack = append(stack, &stackItem{node: wrapNode(n.TableAlteration), visitor: v.Field("TableAlteration")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *AlterIndex: + stack = append(stack, &stackItem{node: wrapNode(n.IndexAlteration), visitor: v.Field("IndexAlteration")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *AlterSequence: + stack = append(stack, &stackItem{node: wrapNode(n.NoSkipRange), visitor: v.Field("NoSkipRange")}) + stack = append(stack, &stackItem{node: wrapNode(n.SkipRange), visitor: v.Field("SkipRange")}) + stack = append(stack, &stackItem{node: wrapNode(n.RestartCounterWith), visitor: v.Field("RestartCounterWith")}) + stack = append(stack, &stackItem{node: wrapNode(n.Options), visitor: v.Field("Options")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *AlterChangeStream: + stack = append(stack, &stackItem{node: wrapNode(n.ChangeStreamAlteration), visitor: v.Field("ChangeStreamAlteration")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *AddSynonym: + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *DropSynonym: + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *RenameTo: + stack = append(stack, &stackItem{node: wrapNode(n.AddSynonym), visitor: v.Field("AddSynonym")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *AddColumn: + stack = append(stack, &stackItem{node: wrapNode(n.Column), visitor: v.Field("Column")}) + + case *AddTableConstraint: + stack = append(stack, &stackItem{node: wrapNode(n.TableConstraint), visitor: v.Field("TableConstraint")}) + + case *AddRowDeletionPolicy: + stack = append(stack, &stackItem{node: wrapNode(n.RowDeletionPolicy), visitor: v.Field("RowDeletionPolicy")}) + + case *DropColumn: + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *DropConstraint: + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *DropRowDeletionPolicy: + // nothing to do + + case *ReplaceRowDeletionPolicy: + stack = append(stack, &stackItem{node: wrapNode(n.RowDeletionPolicy), visitor: v.Field("RowDeletionPolicy")}) + + case *SetOnDelete: + // nothing to do + + case *AlterColumn: + stack = append(stack, &stackItem{node: wrapNode(n.Alteration), visitor: v.Field("Alteration")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *AlterColumnType: + stack = append(stack, &stackItem{node: wrapNode(n.DefaultExpr), visitor: v.Field("DefaultExpr")}) + stack = append(stack, &stackItem{node: wrapNode(n.Type), visitor: v.Field("Type")}) + + case *AlterColumnSetOptions: + stack = append(stack, &stackItem{node: wrapNode(n.Options), visitor: v.Field("Options")}) + + case *AlterColumnSetDefault: + stack = append(stack, &stackItem{node: wrapNode(n.DefaultExpr), visitor: v.Field("DefaultExpr")}) + + case *AlterColumnDropDefault: + // nothing to do + + case *AlterColumnAlterIdentity: + stack = append(stack, &stackItem{node: wrapNode(n.Alteration), visitor: v.Field("Alteration")}) + + case *RestartCounterWith: + stack = append(stack, &stackItem{node: wrapNode(n.Counter), visitor: v.Field("Counter")}) + + case *SetSkipRange: + stack = append(stack, &stackItem{node: wrapNode(n.SkipRange), visitor: v.Field("SkipRange")}) + + case *NoSkipRange: + // nothing to do + + case *SetNoSkipRange: + stack = append(stack, &stackItem{node: wrapNode(n.NoSkipRange), visitor: v.Field("NoSkipRange")}) + + case *DropTable: + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *RenameTable: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Tos), visitor: v.Field("Tos")}) + + case *RenameTableTo: + stack = append(stack, &stackItem{node: wrapNode(n.New), visitor: v.Field("New")}) + stack = append(stack, &stackItem{node: wrapNode(n.Old), visitor: v.Field("Old")}) + + case *CreateIndex: + stack = append(stack, &stackItem{node: wrapNode(n.InterleaveIn), visitor: v.Field("InterleaveIn")}) + stack = append(stack, &stackItem{node: wrapNode(n.Storing), visitor: v.Field("Storing")}) + stack = append(stack, &stackItem{nodes: wrapNodes(n.Keys), visitor: v.Field("Keys")}) + stack = append(stack, &stackItem{node: wrapNode(n.TableName), visitor: v.Field("TableName")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *CreateVectorIndex: + stack = append(stack, &stackItem{node: wrapNode(n.Options), visitor: v.Field("Options")}) + stack = append(stack, &stackItem{node: wrapNode(n.Where), visitor: v.Field("Where")}) + stack = append(stack, &stackItem{node: wrapNode(n.ColumnName), visitor: v.Field("ColumnName")}) + stack = append(stack, &stackItem{node: wrapNode(n.TableName), visitor: v.Field("TableName")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *CreateChangeStream: + stack = append(stack, &stackItem{node: wrapNode(n.Options), visitor: v.Field("Options")}) + stack = append(stack, &stackItem{node: wrapNode(n.For), visitor: v.Field("For")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *ChangeStreamForAll: + // nothing to do + + case *ChangeStreamForTables: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Tables), visitor: v.Field("Tables")}) + + case *ChangeStreamForTable: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Columns), visitor: v.Field("Columns")}) + stack = append(stack, &stackItem{node: wrapNode(n.TableName), visitor: v.Field("TableName")}) + + case *ChangeStreamSetFor: + stack = append(stack, &stackItem{node: wrapNode(n.For), visitor: v.Field("For")}) + + case *ChangeStreamDropForAll: + // nothing to do + + case *ChangeStreamSetOptions: + stack = append(stack, &stackItem{node: wrapNode(n.Options), visitor: v.Field("Options")}) + + case *Storing: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Columns), visitor: v.Field("Columns")}) + + case *InterleaveIn: + stack = append(stack, &stackItem{node: wrapNode(n.TableName), visitor: v.Field("TableName")}) + + case *AddStoredColumn: + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *DropStoredColumn: + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *DropIndex: + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *DropVectorIndex: + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *DropSequence: + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *CreateRole: + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *DropRole: + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *DropChangeStream: + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *Grant: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Roles), visitor: v.Field("Roles")}) + stack = append(stack, &stackItem{node: wrapNode(n.Privilege), visitor: v.Field("Privilege")}) + + case *Revoke: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Roles), visitor: v.Field("Roles")}) + stack = append(stack, &stackItem{node: wrapNode(n.Privilege), visitor: v.Field("Privilege")}) + + case *PrivilegeOnTable: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Names), visitor: v.Field("Names")}) + stack = append(stack, &stackItem{nodes: wrapNodes(n.Privileges), visitor: v.Field("Privileges")}) + + case *SelectPrivilege: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Columns), visitor: v.Field("Columns")}) + + case *InsertPrivilege: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Columns), visitor: v.Field("Columns")}) + + case *UpdatePrivilege: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Columns), visitor: v.Field("Columns")}) + + case *DeletePrivilege: + // nothing to do + + case *SelectPrivilegeOnChangeStream: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Names), visitor: v.Field("Names")}) + + case *SelectPrivilegeOnView: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Names), visitor: v.Field("Names")}) + + case *ExecutePrivilegeOnTableFunction: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Names), visitor: v.Field("Names")}) + + case *RolePrivilege: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Names), visitor: v.Field("Names")}) + + case *AlterStatistics: + stack = append(stack, &stackItem{node: wrapNode(n.Options), visitor: v.Field("Options")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *Analyze: + // nothing to do + + case *CreateModelColumn: + stack = append(stack, &stackItem{node: wrapNode(n.Options), visitor: v.Field("Options")}) + stack = append(stack, &stackItem{node: wrapNode(n.DataType), visitor: v.Field("DataType")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *CreateModelInputOutput: + stack = append(stack, &stackItem{nodes: wrapNodes(n.OutputColumns), visitor: v.Field("OutputColumns")}) + stack = append(stack, &stackItem{nodes: wrapNodes(n.InputColumns), visitor: v.Field("InputColumns")}) + + case *CreateModel: + stack = append(stack, &stackItem{node: wrapNode(n.Options), visitor: v.Field("Options")}) + stack = append(stack, &stackItem{node: wrapNode(n.InputOutput), visitor: v.Field("InputOutput")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *AlterModel: + stack = append(stack, &stackItem{node: wrapNode(n.Options), visitor: v.Field("Options")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *DropModel: + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *ScalarSchemaType: + // nothing to do + + case *SizedSchemaType: + stack = append(stack, &stackItem{node: wrapNode(n.Size), visitor: v.Field("Size")}) + + case *ArraySchemaType: + stack = append(stack, &stackItem{nodes: wrapNodes(n.NamedArgs), visitor: v.Field("NamedArgs")}) + stack = append(stack, &stackItem{node: wrapNode(n.Item), visitor: v.Field("Item")}) + + case *CreateSearchIndex: + stack = append(stack, &stackItem{node: wrapNode(n.Options), visitor: v.Field("Options")}) + stack = append(stack, &stackItem{node: wrapNode(n.Interleave), visitor: v.Field("Interleave")}) + stack = append(stack, &stackItem{node: wrapNode(n.Where), visitor: v.Field("Where")}) + stack = append(stack, &stackItem{node: wrapNode(n.OrderBy), visitor: v.Field("OrderBy")}) + stack = append(stack, &stackItem{nodes: wrapNodes(n.PartitionColumns), visitor: v.Field("PartitionColumns")}) + stack = append(stack, &stackItem{node: wrapNode(n.Storing), visitor: v.Field("Storing")}) + stack = append(stack, &stackItem{nodes: wrapNodes(n.TokenListPart), visitor: v.Field("TokenListPart")}) + stack = append(stack, &stackItem{node: wrapNode(n.TableName), visitor: v.Field("TableName")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *DropSearchIndex: + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *AlterSearchIndex: + stack = append(stack, &stackItem{node: wrapNode(n.IndexAlteration), visitor: v.Field("IndexAlteration")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *CreatePropertyGraph: + stack = append(stack, &stackItem{node: wrapNode(n.Content), visitor: v.Field("Content")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *PropertyGraphContent: + stack = append(stack, &stackItem{node: wrapNode(n.EdgeTables), visitor: v.Field("EdgeTables")}) + stack = append(stack, &stackItem{node: wrapNode(n.NodeTables), visitor: v.Field("NodeTables")}) + + case *PropertyGraphNodeTables: + stack = append(stack, &stackItem{node: wrapNode(n.Tables), visitor: v.Field("Tables")}) + + case *PropertyGraphEdgeTables: + stack = append(stack, &stackItem{node: wrapNode(n.Tables), visitor: v.Field("Tables")}) + + case *PropertyGraphElementList: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Elements), visitor: v.Field("Elements")}) + + case *PropertyGraphElement: + stack = append(stack, &stackItem{node: wrapNode(n.Properties), visitor: v.Field("Properties")}) + stack = append(stack, &stackItem{node: wrapNode(n.Keys), visitor: v.Field("Keys")}) + stack = append(stack, &stackItem{node: wrapNode(n.Alias), visitor: v.Field("Alias")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *PropertyGraphSingleProperties: + stack = append(stack, &stackItem{node: wrapNode(n.Properties), visitor: v.Field("Properties")}) + + case *PropertyGraphLabelAndPropertiesList: + stack = append(stack, &stackItem{nodes: wrapNodes(n.LabelAndProperties), visitor: v.Field("LabelAndProperties")}) + + case *PropertyGraphLabelAndProperties: + stack = append(stack, &stackItem{node: wrapNode(n.Properties), visitor: v.Field("Properties")}) + stack = append(stack, &stackItem{node: wrapNode(n.Label), visitor: v.Field("Label")}) + + case *PropertyGraphElementLabelLabelName: + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *PropertyGraphElementLabelDefaultLabel: + // nothing to do + + case *PropertyGraphNodeElementKey: + stack = append(stack, &stackItem{node: wrapNode(n.Key), visitor: v.Field("Key")}) + + case *PropertyGraphEdgeElementKeys: + stack = append(stack, &stackItem{node: wrapNode(n.Destination), visitor: v.Field("Destination")}) + stack = append(stack, &stackItem{node: wrapNode(n.Source), visitor: v.Field("Source")}) + stack = append(stack, &stackItem{node: wrapNode(n.Element), visitor: v.Field("Element")}) + + case *PropertyGraphElementKey: + stack = append(stack, &stackItem{node: wrapNode(n.Keys), visitor: v.Field("Keys")}) + + case *PropertyGraphSourceKey: + stack = append(stack, &stackItem{node: wrapNode(n.ReferenceColumns), visitor: v.Field("ReferenceColumns")}) + stack = append(stack, &stackItem{node: wrapNode(n.ElementReference), visitor: v.Field("ElementReference")}) + stack = append(stack, &stackItem{node: wrapNode(n.Keys), visitor: v.Field("Keys")}) + + case *PropertyGraphDestinationKey: + stack = append(stack, &stackItem{node: wrapNode(n.ReferenceColumns), visitor: v.Field("ReferenceColumns")}) + stack = append(stack, &stackItem{node: wrapNode(n.ElementReference), visitor: v.Field("ElementReference")}) + stack = append(stack, &stackItem{node: wrapNode(n.Keys), visitor: v.Field("Keys")}) + + case *PropertyGraphColumnNameList: + stack = append(stack, &stackItem{nodes: wrapNodes(n.ColumnNameList), visitor: v.Field("ColumnNameList")}) + + case *PropertyGraphNoProperties: + // nothing to do + + case *PropertyGraphPropertiesAre: + stack = append(stack, &stackItem{node: wrapNode(n.ExceptColumns), visitor: v.Field("ExceptColumns")}) + + case *PropertyGraphDerivedPropertyList: + stack = append(stack, &stackItem{nodes: wrapNodes(n.DerivedProperties), visitor: v.Field("DerivedProperties")}) + + case *PropertyGraphDerivedProperty: + stack = append(stack, &stackItem{node: wrapNode(n.Alias), visitor: v.Field("Alias")}) + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *DropPropertyGraph: + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + + case *WithAction: + stack = append(stack, &stackItem{node: wrapNode(n.Alias), visitor: v.Field("Alias")}) + + case *ThenReturn: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Items), visitor: v.Field("Items")}) + stack = append(stack, &stackItem{node: wrapNode(n.WithAction), visitor: v.Field("WithAction")}) + + case *Insert: + stack = append(stack, &stackItem{node: wrapNode(n.ThenReturn), visitor: v.Field("ThenReturn")}) + stack = append(stack, &stackItem{node: wrapNode(n.Input), visitor: v.Field("Input")}) + stack = append(stack, &stackItem{nodes: wrapNodes(n.Columns), visitor: v.Field("Columns")}) + stack = append(stack, &stackItem{node: wrapNode(n.TableName), visitor: v.Field("TableName")}) + + case *ValuesInput: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Rows), visitor: v.Field("Rows")}) + + case *ValuesRow: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Exprs), visitor: v.Field("Exprs")}) + + case *DefaultExpr: + stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) + + case *SubQueryInput: + stack = append(stack, &stackItem{node: wrapNode(n.Query), visitor: v.Field("Query")}) + + case *Delete: + stack = append(stack, &stackItem{node: wrapNode(n.ThenReturn), visitor: v.Field("ThenReturn")}) + stack = append(stack, &stackItem{node: wrapNode(n.Where), visitor: v.Field("Where")}) + stack = append(stack, &stackItem{node: wrapNode(n.As), visitor: v.Field("As")}) + stack = append(stack, &stackItem{node: wrapNode(n.TableName), visitor: v.Field("TableName")}) + + case *Update: + stack = append(stack, &stackItem{node: wrapNode(n.ThenReturn), visitor: v.Field("ThenReturn")}) + stack = append(stack, &stackItem{node: wrapNode(n.Where), visitor: v.Field("Where")}) + stack = append(stack, &stackItem{nodes: wrapNodes(n.Updates), visitor: v.Field("Updates")}) + stack = append(stack, &stackItem{node: wrapNode(n.As), visitor: v.Field("As")}) + stack = append(stack, &stackItem{node: wrapNode(n.TableName), visitor: v.Field("TableName")}) + + case *UpdateItem: + stack = append(stack, &stackItem{node: wrapNode(n.DefaultExpr), visitor: v.Field("DefaultExpr")}) + stack = append(stack, &stackItem{nodes: wrapNodes(n.Path), visitor: v.Field("Path")}) + + case *Call: + stack = append(stack, &stackItem{nodes: wrapNodes(n.Args), visitor: v.Field("Args")}) + stack = append(stack, &stackItem{node: wrapNode(n.Name), visitor: v.Field("Name")}) + } + return stack +} diff --git a/go.mod b/go.mod index a91f42ee..533a428d 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/cloudspannerecosystem/memefish -go 1.22.0 +go 1.23.0 toolchain go1.23.4 diff --git a/parser.go b/parser.go index 5eb33e2e..cb5f8eb9 100644 --- a/parser.go +++ b/parser.go @@ -3108,11 +3108,11 @@ func (p *Parser) parseCreateTable(pos token.Pos) *ast.CreateTable { } p.nextToken() } - p.expect(")") + rparen := p.expect(")").Pos // PRIMARY KEY clause is now optional var keys []*ast.IndexKey - rparen := token.InvalidPos + primaryKeyRparen := token.InvalidPos if p.Token.IsKeywordLike("PRIMARY") { p.nextToken() p.expectKeywordLike("KEY") @@ -3128,7 +3128,7 @@ func (p *Parser) parseCreateTable(pos token.Pos) *ast.CreateTable { } p.nextToken() } - rparen = p.expect(")").Pos + primaryKeyRparen = p.expect(")").Pos } cluster := p.tryParseCluster() @@ -3137,6 +3137,7 @@ func (p *Parser) parseCreateTable(pos token.Pos) *ast.CreateTable { return &ast.CreateTable{ Create: pos, Rparen: rparen, + PrimaryKeyRparen: primaryKeyRparen, IfNotExists: ifNotExists, Name: name, Columns: columns, @@ -3780,6 +3781,7 @@ func (p *Parser) parseChangeStreamFor() ast.ChangeStreamFor { tname := p.parseIdent() forTable := ast.ChangeStreamForTable{ TableName: tname, + Rparen: token.InvalidPos, } if p.Token.Kind == "(" { @@ -3901,7 +3903,8 @@ func (p *Parser) parseAlterTableAdd() ast.TableAlteration { alteration = &ast.AddTableConstraint{ Add: pos, TableConstraint: &ast.TableConstraint{ - Constraint: fk, + ConstraintPos: token.InvalidPos, + Constraint: fk, }, } case p.Token.IsKeywordLike("CHECK"): @@ -3909,7 +3912,8 @@ func (p *Parser) parseAlterTableAdd() ast.TableAlteration { alteration = &ast.AddTableConstraint{ Add: pos, TableConstraint: &ast.TableConstraint{ - Constraint: c, + ConstraintPos: token.InvalidPos, + Constraint: c, }, } case p.Token.IsKeywordLike("ROW"): diff --git a/parser_test.go b/parser_test.go index e8e07b2e..b1c2b480 100644 --- a/parser_test.go +++ b/parser_test.go @@ -20,6 +20,41 @@ import ( var update = flag.Bool("update", false, "update result files") +type pathVisitor struct { + f func(path string, node ast.Node) bool + path string +} + +func (v *pathVisitor) Visit(node ast.Node) ast.Visitor { + ok := v.f(v.path, node) + if !ok { + return nil + } + + return &pathVisitor{ + f: v.f, + path: v.path, + } +} + +func (v *pathVisitor) VisitMany(nodes []ast.Node) ast.Visitor { + return v +} + +func (v *pathVisitor) Field(name string) ast.Visitor { + return &pathVisitor{ + f: v.f, + path: fmt.Sprintf("%s.%s", v.path, name), + } +} + +func (v *pathVisitor) Index(index int) ast.Visitor { + return &pathVisitor{ + f: v.f, + path: fmt.Sprintf("%s[%d]", v.path, index), + } +} + func testParser(t *testing.T, inputPath, resultPath string, parse func(p *memefish.Parser) (ast.Node, error)) { if *update { _, err := os.Stat(resultPath) @@ -83,6 +118,76 @@ func testParser(t *testing.T, inputPath, resultPath string, parse func(p *memefi } } + // pos/end test + if !bad { + ok := true + + ast.Walk(node, &pathVisitor{ + path: "", + f: func(path string, node ast.Node) bool { + if !ok { + return false + } + + if node.Pos().Invalid() { + t.Errorf("pos must be valid, but got invalid on %v: %v", path, node.SQL()) + ok = false + return false + } + + if node.End().Invalid() { + t.Errorf("end must be valid, but got invalid on %v: %v", path, node.SQL()) + ok = false + return false + } + + if node.End() <= node.Pos() { + t.Errorf("pos must be smaller than end, but got pos: %v, end: %v on %v: %v", node.Pos(), node.End(), path, node.SQL()) + ok = false + return false + } + + // FIXME: The fields of `CreateTable` are not ordered by position for now, + // so we skips to check the order of positions of `CreateTable` fields. + _, isCreateTable := node.(*ast.CreateTable) + + lastEnd := token.InvalidPos + ast.Walk(node, &pathVisitor{ + path: path, + f: func(childPath string, child ast.Node) bool { + if !ok { + return false + } + + if node == child { + return true + } + + if child.Pos() < lastEnd { + if !isCreateTable { + t.Errorf("pos must be larger or equal than end of last node pos %v, but got pos: %v on %v: %v", lastEnd, child.Pos(), childPath, child.SQL()) + } else { + t.Logf("pos must be larger or equal than end of last node pos %v, but got pos: %v on %v: %v", lastEnd, child.Pos(), childPath, child.SQL()) + } + ok = false + return false + } + lastEnd = child.End() + + if child.Pos() < node.Pos() || node.End() < child.End() { + t.Errorf("child position must be in node position [%v, %v], but got [%v, %v] on %v: %v", node.Pos(), node.End(), child.Pos(), child.End(), childPath, child.SQL()) + ok = false + } + + return false + }, + }) + + return ok + }, + }) + } + fmt.Fprintf(&buf, "--- AST\n") _, _ = pprinter.Fprintln(&buf, node) fmt.Fprintln(&buf) diff --git a/testdata/result/ddl/alter_table_add_check.sql.txt b/testdata/result/ddl/alter_table_add_check.sql.txt index 16e305bb..8ace2608 100644 --- a/testdata/result/ddl/alter_table_add_check.sql.txt +++ b/testdata/result/ddl/alter_table_add_check.sql.txt @@ -15,7 +15,8 @@ alter table foo add check (c1 > 0) TableAlteration: &ast.AddTableConstraint{ Add: 16, TableConstraint: &ast.TableConstraint{ - Constraint: &ast.Check{ + ConstraintPos: -1, + Constraint: &ast.Check{ Check: 20, Rparen: 33, Expr: &ast.BinaryExpr{ diff --git a/testdata/result/ddl/alter_table_add_foreign_key.sql.txt b/testdata/result/ddl/alter_table_add_foreign_key.sql.txt index 510a9c43..02bc8ecf 100644 --- a/testdata/result/ddl/alter_table_add_foreign_key.sql.txt +++ b/testdata/result/ddl/alter_table_add_foreign_key.sql.txt @@ -15,7 +15,8 @@ alter table foo add foreign key (bar) references t2 (t2key1) TableAlteration: &ast.AddTableConstraint{ Add: 16, TableConstraint: &ast.TableConstraint{ - Constraint: &ast.ForeignKey{ + ConstraintPos: -1, + Constraint: &ast.ForeignKey{ Foreign: 20, Rparen: 59, OnDeleteEnd: -1, diff --git a/testdata/result/ddl/create_change_stream_for_table.sql.txt b/testdata/result/ddl/create_change_stream_for_table.sql.txt index ebde8b19..ad46ec92 100644 --- a/testdata/result/ddl/create_change_stream_for_table.sql.txt +++ b/testdata/result/ddl/create_change_stream_for_table.sql.txt @@ -12,6 +12,7 @@ CREATE CHANGE STREAM change_stream_name FOR table_name For: 40, Tables: []*ast.ChangeStreamForTable{ &ast.ChangeStreamForTable{ + Rparen: -1, TableName: &ast.Ident{ NamePos: 44, NameEnd: 54, diff --git a/testdata/result/ddl/create_change_stream_for_tables.sql.txt b/testdata/result/ddl/create_change_stream_for_tables.sql.txt index 3cfd2415..9c0bc622 100644 --- a/testdata/result/ddl/create_change_stream_for_tables.sql.txt +++ b/testdata/result/ddl/create_change_stream_for_tables.sql.txt @@ -12,6 +12,7 @@ CREATE CHANGE STREAM change_stream_name FOR table_name1, table_name2 For: 40, Tables: []*ast.ChangeStreamForTable{ &ast.ChangeStreamForTable{ + Rparen: -1, TableName: &ast.Ident{ NamePos: 44, NameEnd: 55, @@ -19,6 +20,7 @@ CREATE CHANGE STREAM change_stream_name FOR table_name1, table_name2 }, }, &ast.ChangeStreamForTable{ + Rparen: -1, TableName: &ast.Ident{ NamePos: 57, NameEnd: 68, diff --git a/testdata/result/ddl/create_table.sql.txt b/testdata/result/ddl/create_table.sql.txt index 47df64b5..abfa24b5 100644 --- a/testdata/result/ddl/create_table.sql.txt +++ b/testdata/result/ddl/create_table.sql.txt @@ -16,8 +16,9 @@ create table foo ( --- AST &ast.CreateTable{ - Rparen: 573, - Name: &ast.Path{ + Rparen: 550, + PrimaryKeyRparen: 573, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 13, diff --git a/testdata/result/ddl/create_table_cluster.sql.txt b/testdata/result/ddl/create_table_cluster.sql.txt index de344c18..b49f28c5 100644 --- a/testdata/result/ddl/create_table_cluster.sql.txt +++ b/testdata/result/ddl/create_table_cluster.sql.txt @@ -7,8 +7,9 @@ create table foo ( --- AST &ast.CreateTable{ - Rparen: 59, - Name: &ast.Path{ + Rparen: 44, + PrimaryKeyRparen: 59, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 13, diff --git a/testdata/result/ddl/create_table_cluster_and_row_deletion_policy.sql.txt b/testdata/result/ddl/create_table_cluster_and_row_deletion_policy.sql.txt index 6f01e0b7..54b627b0 100644 --- a/testdata/result/ddl/create_table_cluster_and_row_deletion_policy.sql.txt +++ b/testdata/result/ddl/create_table_cluster_and_row_deletion_policy.sql.txt @@ -9,8 +9,9 @@ create table foo ( --- AST &ast.CreateTable{ - Rparen: 77, - Name: &ast.Path{ + Rparen: 62, + PrimaryKeyRparen: 77, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 13, diff --git a/testdata/result/ddl/create_table_cluster_on_delete_no_action.sql.txt b/testdata/result/ddl/create_table_cluster_on_delete_no_action.sql.txt index 5bbfd8b6..afc8cc0b 100644 --- a/testdata/result/ddl/create_table_cluster_on_delete_no_action.sql.txt +++ b/testdata/result/ddl/create_table_cluster_on_delete_no_action.sql.txt @@ -6,8 +6,9 @@ create table foo ( on delete no action --- AST &ast.CreateTable{ - Rparen: 50, - Name: &ast.Path{ + Rparen: 32, + PrimaryKeyRparen: 50, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 13, diff --git a/testdata/result/ddl/create_table_cluster_set_on_delete.sql.txt b/testdata/result/ddl/create_table_cluster_set_on_delete.sql.txt index 38a950da..f3784a48 100644 --- a/testdata/result/ddl/create_table_cluster_set_on_delete.sql.txt +++ b/testdata/result/ddl/create_table_cluster_set_on_delete.sql.txt @@ -6,8 +6,9 @@ create table foo ( on delete cascade --- AST &ast.CreateTable{ - Rparen: 49, - Name: &ast.Path{ + Rparen: 31, + PrimaryKeyRparen: 49, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 13, diff --git a/testdata/result/ddl/create_table_for_format_test.sql.txt b/testdata/result/ddl/create_table_for_format_test.sql.txt index 49f89708..8824cce8 100644 --- a/testdata/result/ddl/create_table_for_format_test.sql.txt +++ b/testdata/result/ddl/create_table_for_format_test.sql.txt @@ -15,9 +15,10 @@ create table if not exists foo ( --- AST &ast.CreateTable{ - Rparen: 461, - IfNotExists: true, - Name: &ast.Path{ + Rparen: 443, + PrimaryKeyRparen: 461, + IfNotExists: true, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 27, diff --git a/testdata/result/ddl/create_table_fulltext_albums.sql.txt b/testdata/result/ddl/create_table_fulltext_albums.sql.txt index fe0d66bf..4f75acb1 100644 --- a/testdata/result/ddl/create_table_fulltext_albums.sql.txt +++ b/testdata/result/ddl/create_table_fulltext_albums.sql.txt @@ -11,9 +11,10 @@ CREATE TABLE Albums ( ) PRIMARY KEY(AlbumId) --- AST &ast.CreateTable{ - Create: 105, - Rparen: 575, - Name: &ast.Path{ + Create: 105, + Rparen: 554, + PrimaryKeyRparen: 575, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 118, diff --git a/testdata/result/ddl/create_table_if_not_exists.sql.txt b/testdata/result/ddl/create_table_if_not_exists.sql.txt index ad4b82a5..102c6a4c 100644 --- a/testdata/result/ddl/create_table_if_not_exists.sql.txt +++ b/testdata/result/ddl/create_table_if_not_exists.sql.txt @@ -6,9 +6,10 @@ create table if not exists foo ( --- AST &ast.CreateTable{ - Rparen: 93, - IfNotExists: true, - Name: &ast.Path{ + Rparen: 70, + PrimaryKeyRparen: 93, + IfNotExists: true, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 27, diff --git a/testdata/result/ddl/create_table_row_deletion_policy.sql.txt b/testdata/result/ddl/create_table_row_deletion_policy.sql.txt index 41636dff..42297b24 100644 --- a/testdata/result/ddl/create_table_row_deletion_policy.sql.txt +++ b/testdata/result/ddl/create_table_row_deletion_policy.sql.txt @@ -8,8 +8,9 @@ create table foo ( --- AST &ast.CreateTable{ - Rparen: 77, - Name: &ast.Path{ + Rparen: 62, + PrimaryKeyRparen: 77, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 13, diff --git a/testdata/result/ddl/create_table_synonyms.sql.txt b/testdata/result/ddl/create_table_synonyms.sql.txt index c241363c..14dc0774 100644 --- a/testdata/result/ddl/create_table_synonyms.sql.txt +++ b/testdata/result/ddl/create_table_synonyms.sql.txt @@ -6,8 +6,9 @@ CREATE TABLE Singers ( ) PRIMARY KEY (SingerId) --- AST &ast.CreateTable{ - Rparen: 126, - Name: &ast.Path{ + Rparen: 103, + PrimaryKeyRparen: 126, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 13, diff --git a/testdata/result/ddl/create_table_synonyms_abnormal.sql.txt b/testdata/result/ddl/create_table_synonyms_abnormal.sql.txt index 05111cf3..1e02107f 100644 --- a/testdata/result/ddl/create_table_synonyms_abnormal.sql.txt +++ b/testdata/result/ddl/create_table_synonyms_abnormal.sql.txt @@ -8,9 +8,10 @@ CREATE TABLE Singers ( ) PRIMARY KEY (SingerId) --- AST &ast.CreateTable{ - Create: 45, - Rparen: 194, - Name: &ast.Path{ + Create: 45, + Rparen: 171, + PrimaryKeyRparen: 194, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 58, diff --git a/testdata/result/ddl/create_table_trailing_comma.sql.txt b/testdata/result/ddl/create_table_trailing_comma.sql.txt index f8af47cd..128e3924 100644 --- a/testdata/result/ddl/create_table_trailing_comma.sql.txt +++ b/testdata/result/ddl/create_table_trailing_comma.sql.txt @@ -9,8 +9,9 @@ create table foo ( --- AST &ast.CreateTable{ - Rparen: 83, - Name: &ast.Path{ + Rparen: 45, + PrimaryKeyRparen: 83, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 13, diff --git a/testdata/result/ddl/create_table_types.sql.txt b/testdata/result/ddl/create_table_types.sql.txt index c5baf99c..0ff8f67e 100644 --- a/testdata/result/ddl/create_table_types.sql.txt +++ b/testdata/result/ddl/create_table_types.sql.txt @@ -31,9 +31,10 @@ create table types ( --- AST &ast.CreateTable{ - Create: 100, - Rparen: 796, - Name: &ast.Path{ + Create: 100, + Rparen: 780, + PrimaryKeyRparen: 796, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 113, diff --git a/testdata/result/ddl/create_table_with_identity_columns.sql.txt b/testdata/result/ddl/create_table_with_identity_columns.sql.txt index 689d2640..28c9cae6 100644 --- a/testdata/result/ddl/create_table_with_identity_columns.sql.txt +++ b/testdata/result/ddl/create_table_with_identity_columns.sql.txt @@ -12,8 +12,9 @@ create table foo ( --- AST &ast.CreateTable{ - Rparen: -1, - Name: &ast.Path{ + Rparen: 460, + PrimaryKeyRparen: -1, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 13, diff --git a/testdata/result/ddl/create_table_with_sequence_function.sql.txt b/testdata/result/ddl/create_table_with_sequence_function.sql.txt index 44635803..7da3ed10 100644 --- a/testdata/result/ddl/create_table_with_sequence_function.sql.txt +++ b/testdata/result/ddl/create_table_with_sequence_function.sql.txt @@ -8,8 +8,9 @@ CREATE TABLE foo --- AST &ast.CreateTable{ - Rparen: 143, - Name: &ast.Path{ + Rparen: 127, + PrimaryKeyRparen: 143, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 13, diff --git a/testdata/result/ddl/named_schema_create_table_backquoted.sql.txt b/testdata/result/ddl/named_schema_create_table_backquoted.sql.txt index 955d8e88..e5778c1f 100644 --- a/testdata/result/ddl/named_schema_create_table_backquoted.sql.txt +++ b/testdata/result/ddl/named_schema_create_table_backquoted.sql.txt @@ -2,8 +2,9 @@ CREATE TABLE `ORDER`.`ORDER`(PK INT64) PRIMARY KEY(PK) --- AST &ast.CreateTable{ - Rparen: 53, - Name: &ast.Path{ + Rparen: 37, + PrimaryKeyRparen: 53, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 13, diff --git a/testdata/result/ddl/named_schemas_create_table.sql.txt b/testdata/result/ddl/named_schemas_create_table.sql.txt index 90c87f6a..b3c76833 100644 --- a/testdata/result/ddl/named_schemas_create_table.sql.txt +++ b/testdata/result/ddl/named_schemas_create_table.sql.txt @@ -7,8 +7,9 @@ CREATE TABLE sch1.Singers ( ) PRIMARY KEY(SingerId) --- AST &ast.CreateTable{ - Rparen: 161, - Name: &ast.Path{ + Rparen: 139, + PrimaryKeyRparen: 161, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 13, diff --git a/testdata/result/ddl/named_schemas_create_table_foreign_key.sql.txt b/testdata/result/ddl/named_schemas_create_table_foreign_key.sql.txt index cb4bce8f..fe0a7438 100644 --- a/testdata/result/ddl/named_schemas_create_table_foreign_key.sql.txt +++ b/testdata/result/ddl/named_schemas_create_table_foreign_key.sql.txt @@ -8,8 +8,9 @@ CREATE TABLE sch1.ShoppingCarts ( ) PRIMARY KEY(CartId) --- AST &ast.CreateTable{ - Rparen: 296, - Name: &ast.Path{ + Rparen: 276, + PrimaryKeyRparen: 296, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 13, diff --git a/testdata/result/ddl/named_schemas_create_table_interleave.sql.txt b/testdata/result/ddl/named_schemas_create_table_interleave.sql.txt index dcb2ea27..cb68b1f4 100644 --- a/testdata/result/ddl/named_schemas_create_table_interleave.sql.txt +++ b/testdata/result/ddl/named_schemas_create_table_interleave.sql.txt @@ -7,8 +7,9 @@ CREATE TABLE sch1.Albums ( INTERLEAVE IN PARENT sch1.Singers ON DELETE CASCADE --- AST &ast.CreateTable{ - Rparen: 137, - Name: &ast.Path{ + Rparen: 106, + PrimaryKeyRparen: 137, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 13, diff --git a/testdata/result/query/select_cast.sql.txt b/testdata/result/query/select_cast.sql.txt index db776822..be9059d3 100644 --- a/testdata/result/query/select_cast.sql.txt +++ b/testdata/result/query/select_cast.sql.txt @@ -156,9 +156,7 @@ limit cast(1 as INT64) offset cast(@foo as INT64) From: &ast.From{ From: 149, Source: &ast.Join{ - Op: ",", Left: &ast.Join{ - Op: ",", Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 154, @@ -185,6 +183,7 @@ limit cast(1 as INT64) offset cast(@foo as INT64) }, }, }, + Op: ",", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 215, @@ -213,6 +212,7 @@ limit cast(1 as INT64) offset cast(@foo as INT64) }, }, }, + Op: ",", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 269, diff --git a/testdata/result/query/select_complex_with_array_path.sql.txt b/testdata/result/query/select_complex_with_array_path.sql.txt index cbfb49b5..7290fd52 100644 --- a/testdata/result/query/select_complex_with_array_path.sql.txt +++ b/testdata/result/query/select_complex_with_array_path.sql.txt @@ -16,7 +16,6 @@ FROM From: &ast.From{ From: 11, Source: &ast.Join{ - Op: ",", Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 18, @@ -24,6 +23,7 @@ FROM Name: "ComplexTable", }, }, + Op: ",", Right: &ast.PathTableExpr{ Path: &ast.Path{ Idents: []*ast.Ident{ diff --git a/testdata/result/query/select_complex_with_unnest_array_path.sql.txt b/testdata/result/query/select_complex_with_unnest_array_path.sql.txt index 5008c0b7..c74cef04 100644 --- a/testdata/result/query/select_complex_with_unnest_array_path.sql.txt +++ b/testdata/result/query/select_complex_with_unnest_array_path.sql.txt @@ -16,7 +16,6 @@ FROM From: &ast.From{ From: 11, Source: &ast.Join{ - Op: ",", Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 18, @@ -24,6 +23,7 @@ FROM Name: "ComplexTable", }, }, + Op: ",", Right: &ast.Unnest{ Unnest: 34, Rparen: 62, diff --git a/testdata/result/query/select_from_implicit_unnest.sql.txt b/testdata/result/query/select_from_implicit_unnest.sql.txt index 06a9e39a..0a0f7600 100644 --- a/testdata/result/query/select_from_implicit_unnest.sql.txt +++ b/testdata/result/query/select_from_implicit_unnest.sql.txt @@ -24,7 +24,6 @@ FROM UNNEST([STRUCT>(["foo"])]) AS t, From: &ast.From{ From: 14, Source: &ast.Join{ - Op: ",", Left: &ast.Unnest{ Unnest: 19, Rparen: 62, @@ -79,6 +78,7 @@ FROM UNNEST([STRUCT>(["foo"])]) AS t, }, }, }, + Op: ",", Right: &ast.PathTableExpr{ Path: &ast.Path{ Idents: []*ast.Ident{ diff --git a/testdata/result/query/select_from_inner_join_path_table_expr.sql.txt b/testdata/result/query/select_from_inner_join_path_table_expr.sql.txt index bd1a9ff0..dc67ffe5 100644 --- a/testdata/result/query/select_from_inner_join_path_table_expr.sql.txt +++ b/testdata/result/query/select_from_inner_join_path_table_expr.sql.txt @@ -45,7 +45,6 @@ FROM From: &ast.From{ From: 113, Source: &ast.Join{ - Op: "INNER JOIN", Left: &ast.Unnest{ Unnest: 120, Rparen: 268, @@ -163,6 +162,7 @@ FROM }, }, }, + Op: "INNER JOIN", Right: &ast.PathTableExpr{ Path: &ast.Path{ Idents: []*ast.Ident{ diff --git a/testdata/result/query/select_from_join_unnest.sql.txt b/testdata/result/query/select_from_join_unnest.sql.txt index 8838628b..4056de45 100644 --- a/testdata/result/query/select_from_join_unnest.sql.txt +++ b/testdata/result/query/select_from_join_unnest.sql.txt @@ -24,7 +24,6 @@ FROM From: &ast.From{ From: 102, Source: &ast.Join{ - Op: "INNER JOIN", Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 109, @@ -32,6 +31,7 @@ FROM Name: "Roster", }, }, + Op: "INNER JOIN", Right: &ast.Unnest{ Unnest: 127, Rparen: 256, diff --git a/testdata/result/query/select_from_left_join_path_table_expr.sql.txt b/testdata/result/query/select_from_left_join_path_table_expr.sql.txt index ab108403..79145378 100644 --- a/testdata/result/query/select_from_left_join_path_table_expr.sql.txt +++ b/testdata/result/query/select_from_left_join_path_table_expr.sql.txt @@ -85,7 +85,6 @@ FROM From: &ast.From{ From: 156, Source: &ast.Join{ - Op: "LEFT OUTER JOIN", Left: &ast.Unnest{ Unnest: 163, Rparen: 311, @@ -203,6 +202,7 @@ FROM }, }, }, + Op: "LEFT OUTER JOIN", Right: &ast.PathTableExpr{ Path: &ast.Path{ Idents: []*ast.Ident{ diff --git a/testdata/result/query/select_from_ml_predict_textbison.sql.txt b/testdata/result/query/select_from_ml_predict_textbison.sql.txt index 60bf3d4c..7ee83491 100644 --- a/testdata/result/query/select_from_ml_predict_textbison.sql.txt +++ b/testdata/result/query/select_from_ml_predict_textbison.sql.txt @@ -251,7 +251,6 @@ FROM ML.PREDICT( From: &ast.From{ From: 410, Source: &ast.Join{ - Op: "INNER JOIN", Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 424, @@ -267,6 +266,7 @@ FROM ML.PREDICT( }, }, }, + Op: "INNER JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 449, diff --git a/testdata/result/query/select_nest_complex.sql.txt b/testdata/result/query/select_nest_complex.sql.txt index 19613b2e..c2ecbc46 100644 --- a/testdata/result/query/select_nest_complex.sql.txt +++ b/testdata/result/query/select_nest_complex.sql.txt @@ -20,7 +20,6 @@ from ( Lparen: 14, Rparen: 131, Source: &ast.Join{ - Op: "INNER JOIN", Left: &ast.SubQueryTableExpr{ Lparen: 20, Rparen: 90, @@ -111,6 +110,7 @@ from ( }, }, }, + Op: "INNER JOIN", Right: &ast.SubQueryTableExpr{ Lparen: 103, Rparen: 119, diff --git a/testdata/result/query/select_singer_with_cross_join.sql.txt b/testdata/result/query/select_singer_with_cross_join.sql.txt index 34362fa4..cc86b273 100644 --- a/testdata/result/query/select_singer_with_cross_join.sql.txt +++ b/testdata/result/query/select_singer_with_cross_join.sql.txt @@ -17,7 +17,6 @@ FROM From: &ast.From{ From: 11, Source: &ast.Join{ - Op: "CROSS JOIN", Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 18, @@ -33,6 +32,7 @@ FROM }, }, }, + Op: "CROSS JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 43, diff --git a/testdata/result/query/select_singer_with_full_join.sql.txt b/testdata/result/query/select_singer_with_full_join.sql.txt index 4036f6ce..f7187a9b 100644 --- a/testdata/result/query/select_singer_with_full_join.sql.txt +++ b/testdata/result/query/select_singer_with_full_join.sql.txt @@ -16,7 +16,6 @@ FROM From: &ast.From{ From: 11, Source: &ast.Join{ - Op: ",", Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 18, @@ -32,6 +31,7 @@ FROM }, }, }, + Op: ",", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 34, diff --git a/testdata/result/query/select_singer_with_hash_join.sql.txt b/testdata/result/query/select_singer_with_hash_join.sql.txt index cd61e22e..dc61c560 100644 --- a/testdata/result/query/select_singer_with_hash_join.sql.txt +++ b/testdata/result/query/select_singer_with_hash_join.sql.txt @@ -17,9 +17,7 @@ FROM From: &ast.From{ From: 11, Source: &ast.Join{ - Op: "INNER JOIN", - Method: "HASH", - Left: &ast.TableName{ + Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 18, NameEnd: 25, @@ -34,7 +32,9 @@ FROM }, }, }, - Right: &ast.TableName{ + Op: "INNER JOIN", + Method: "HASH", + Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 42, NameEnd: 49, diff --git a/testdata/result/query/select_singer_with_join.sql.txt b/testdata/result/query/select_singer_with_join.sql.txt index 70831a2f..d825ac6e 100644 --- a/testdata/result/query/select_singer_with_join.sql.txt +++ b/testdata/result/query/select_singer_with_join.sql.txt @@ -18,7 +18,6 @@ FROM From: &ast.From{ From: 11, Source: &ast.Join{ - Op: "LEFT OUTER JOIN", Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 18, @@ -34,6 +33,7 @@ FROM }, }, }, + Op: "LEFT OUTER JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 48, diff --git a/testdata/result/query/select_singer_with_join_hint.sql.txt b/testdata/result/query/select_singer_with_join_hint.sql.txt index 06d8a6ef..7dbf20a8 100644 --- a/testdata/result/query/select_singer_with_join_hint.sql.txt +++ b/testdata/result/query/select_singer_with_join_hint.sql.txt @@ -27,78 +27,24 @@ FROM From: &ast.From{ From: 11, Source: &ast.Join{ - Op: "INNER JOIN", - Hint: &ast.Hint{ - Atmark: 258, - Rbrace: 279, - Records: []*ast.HintRecord{ - &ast.HintRecord{ - Key: &ast.Path{ - Idents: []*ast.Ident{ - &ast.Ident{ - NamePos: 260, - NameEnd: 269, - Name: "JOIN_TYPE", - }, - }, - }, - Value: &ast.Ident{ - NamePos: 270, - NameEnd: 279, - Name: "LOOP_JOIN", - }, - }, - }, - }, Left: &ast.Join{ - Op: "INNER JOIN", - Hint: &ast.Hint{ - Atmark: 187, - Rbrace: 209, - Records: []*ast.HintRecord{ - &ast.HintRecord{ - Key: &ast.Path{ - Idents: []*ast.Ident{ - &ast.Ident{ - NamePos: 189, - NameEnd: 198, - Name: "JOIN_TYPE", - }, - }, - }, - Value: &ast.Ident{ - NamePos: 199, - NameEnd: 209, - Name: "APPLY_JOIN", - }, - }, - }, - }, Left: &ast.Join{ - Op: "INNER JOIN", - Hint: &ast.Hint{ - Atmark: 117, - Rbrace: 138, - Records: []*ast.HintRecord{ - &ast.HintRecord{ - Key: &ast.Path{ - Idents: []*ast.Ident{ - &ast.Ident{ - NamePos: 119, - NameEnd: 128, - Name: "JOIN_TYPE", - }, - }, - }, - Value: &ast.Ident{ - NamePos: 129, - NameEnd: 138, - Name: "HASH_JOIN", + Left: &ast.Join{ + Left: &ast.TableName{ + Table: &ast.Ident{ + NamePos: 18, + NameEnd: 25, + Name: "Singers", + }, + As: &ast.AsAlias{ + As: -1, + Alias: &ast.Ident{ + NamePos: 26, + NameEnd: 27, + Name: "A", }, }, }, - }, - Left: &ast.Join{ Op: "LEFT OUTER JOIN", Hint: &ast.Hint{ Atmark: 45, @@ -121,21 +67,6 @@ FROM }, }, }, - Left: &ast.TableName{ - Table: &ast.Ident{ - NamePos: 18, - NameEnd: 25, - Name: "Singers", - }, - As: &ast.AsAlias{ - As: -1, - Alias: &ast.Ident{ - NamePos: 26, - NameEnd: 27, - Name: "A", - }, - }, - }, Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 72, @@ -186,6 +117,29 @@ FROM }, }, }, + Op: "INNER JOIN", + Hint: &ast.Hint{ + Atmark: 117, + Rbrace: 138, + Records: []*ast.HintRecord{ + &ast.HintRecord{ + Key: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 119, + NameEnd: 128, + Name: "JOIN_TYPE", + }, + }, + }, + Value: &ast.Ident{ + NamePos: 129, + NameEnd: 138, + Name: "HASH_JOIN", + }, + }, + }, + }, Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 142, @@ -236,6 +190,29 @@ FROM }, }, }, + Op: "INNER JOIN", + Hint: &ast.Hint{ + Atmark: 187, + Rbrace: 209, + Records: []*ast.HintRecord{ + &ast.HintRecord{ + Key: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 189, + NameEnd: 198, + Name: "JOIN_TYPE", + }, + }, + }, + Value: &ast.Ident{ + NamePos: 199, + NameEnd: 209, + Name: "APPLY_JOIN", + }, + }, + }, + }, Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 213, @@ -286,6 +263,29 @@ FROM }, }, }, + Op: "INNER JOIN", + Hint: &ast.Hint{ + Atmark: 258, + Rbrace: 279, + Records: []*ast.HintRecord{ + &ast.HintRecord{ + Key: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 260, + NameEnd: 269, + Name: "JOIN_TYPE", + }, + }, + }, + Value: &ast.Ident{ + NamePos: 270, + NameEnd: 279, + Name: "LOOP_JOIN", + }, + }, + }, + }, Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 283, diff --git a/testdata/result/query/select_singer_with_join_twice.sql.txt b/testdata/result/query/select_singer_with_join_twice.sql.txt index c2f8e6dd..c442579e 100644 --- a/testdata/result/query/select_singer_with_join_twice.sql.txt +++ b/testdata/result/query/select_singer_with_join_twice.sql.txt @@ -21,9 +21,7 @@ FROM From: &ast.From{ From: 11, Source: &ast.Join{ - Op: "INNER JOIN", Left: &ast.Join{ - Op: "INNER JOIN", Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 18, @@ -39,6 +37,7 @@ FROM }, }, }, + Op: "INNER JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 37, @@ -89,6 +88,7 @@ FROM }, }, }, + Op: "INNER JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 91, diff --git a/testdata/result/query/select_singer_with_join_using.sql.txt b/testdata/result/query/select_singer_with_join_using.sql.txt index a68579f7..4ec72a0f 100644 --- a/testdata/result/query/select_singer_with_join_using.sql.txt +++ b/testdata/result/query/select_singer_with_join_using.sql.txt @@ -18,7 +18,6 @@ FROM From: &ast.From{ From: 11, Source: &ast.Join{ - Op: "LEFT OUTER JOIN", Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 18, @@ -34,6 +33,7 @@ FROM }, }, }, + Op: "LEFT OUTER JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 48, diff --git a/testdata/result/query/select_singer_with_join_various.sql.txt b/testdata/result/query/select_singer_with_join_various.sql.txt index 17f7be5a..25df4633 100644 --- a/testdata/result/query/select_singer_with_join_various.sql.txt +++ b/testdata/result/query/select_singer_with_join_various.sql.txt @@ -41,23 +41,14 @@ FROM From: &ast.From{ From: 11, Source: &ast.Join{ - Op: "RIGHT OUTER JOIN", Left: &ast.Join{ - Op: "RIGHT OUTER JOIN", Left: &ast.Join{ - Op: "LEFT OUTER JOIN", Left: &ast.Join{ - Op: "LEFT OUTER JOIN", Left: &ast.Join{ - Op: "FULL OUTER JOIN", Left: &ast.Join{ - Op: "FULL OUTER JOIN", Left: &ast.Join{ - Op: "CROSS JOIN", Left: &ast.Join{ - Op: "INNER JOIN", Left: &ast.Join{ - Op: "INNER JOIN", Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 18, @@ -73,6 +64,7 @@ FROM }, }, }, + Op: "INNER JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 37, @@ -123,6 +115,7 @@ FROM }, }, }, + Op: "INNER JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 91, @@ -173,6 +166,7 @@ FROM }, }, }, + Op: "CROSS JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 145, @@ -189,6 +183,7 @@ FROM }, }, }, + Op: "FULL OUTER JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 169, @@ -239,6 +234,7 @@ FROM }, }, }, + Op: "FULL OUTER JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 228, @@ -289,6 +285,7 @@ FROM }, }, }, + Op: "LEFT OUTER JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 281, @@ -339,6 +336,7 @@ FROM }, }, }, + Op: "LEFT OUTER JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 340, @@ -389,6 +387,7 @@ FROM }, }, }, + Op: "RIGHT OUTER JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 394, @@ -439,6 +438,7 @@ FROM }, }, }, + Op: "RIGHT OUTER JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 454, diff --git a/testdata/result/query/select_singer_with_tablesample.sql.txt b/testdata/result/query/select_singer_with_tablesample.sql.txt index 6f4e56c8..4cb6cc9a 100644 --- a/testdata/result/query/select_singer_with_tablesample.sql.txt +++ b/testdata/result/query/select_singer_with_tablesample.sql.txt @@ -17,9 +17,7 @@ FROM From: &ast.From{ From: 11, Source: &ast.Join{ - Op: ",", Left: &ast.Join{ - Op: ",", Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 18, @@ -50,6 +48,7 @@ FROM }, }, }, + Op: ",", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 67, @@ -80,6 +79,7 @@ FROM }, }, }, + Op: ",", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 119, diff --git a/testdata/result/query/select_singer_with_toplevel_join_hint.sql.txt b/testdata/result/query/select_singer_with_toplevel_join_hint.sql.txt index 0b7ededc..70992dc0 100644 --- a/testdata/result/query/select_singer_with_toplevel_join_hint.sql.txt +++ b/testdata/result/query/select_singer_with_toplevel_join_hint.sql.txt @@ -40,7 +40,6 @@ FROM From: &ast.From{ From: 36, Source: &ast.Join{ - Op: "LEFT OUTER JOIN", Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 43, @@ -56,6 +55,7 @@ FROM }, }, }, + Op: "LEFT OUTER JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 73, diff --git a/testdata/result/query/select_tablesample_with_cross_join.sql.txt b/testdata/result/query/select_tablesample_with_cross_join.sql.txt index f89a73f5..56a2b950 100644 --- a/testdata/result/query/select_tablesample_with_cross_join.sql.txt +++ b/testdata/result/query/select_tablesample_with_cross_join.sql.txt @@ -18,7 +18,6 @@ FROM From: &ast.From{ From: 11, Source: &ast.Join{ - Op: "CROSS JOIN", Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 18, @@ -34,6 +33,7 @@ FROM }, }, }, + Op: "CROSS JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 43, diff --git a/testdata/result/statement/alter_table_add_check.sql.txt b/testdata/result/statement/alter_table_add_check.sql.txt index 16e305bb..8ace2608 100644 --- a/testdata/result/statement/alter_table_add_check.sql.txt +++ b/testdata/result/statement/alter_table_add_check.sql.txt @@ -15,7 +15,8 @@ alter table foo add check (c1 > 0) TableAlteration: &ast.AddTableConstraint{ Add: 16, TableConstraint: &ast.TableConstraint{ - Constraint: &ast.Check{ + ConstraintPos: -1, + Constraint: &ast.Check{ Check: 20, Rparen: 33, Expr: &ast.BinaryExpr{ diff --git a/testdata/result/statement/alter_table_add_foreign_key.sql.txt b/testdata/result/statement/alter_table_add_foreign_key.sql.txt index 510a9c43..02bc8ecf 100644 --- a/testdata/result/statement/alter_table_add_foreign_key.sql.txt +++ b/testdata/result/statement/alter_table_add_foreign_key.sql.txt @@ -15,7 +15,8 @@ alter table foo add foreign key (bar) references t2 (t2key1) TableAlteration: &ast.AddTableConstraint{ Add: 16, TableConstraint: &ast.TableConstraint{ - Constraint: &ast.ForeignKey{ + ConstraintPos: -1, + Constraint: &ast.ForeignKey{ Foreign: 20, Rparen: 59, OnDeleteEnd: -1, diff --git a/testdata/result/statement/create_change_stream_for_table.sql.txt b/testdata/result/statement/create_change_stream_for_table.sql.txt index ebde8b19..ad46ec92 100644 --- a/testdata/result/statement/create_change_stream_for_table.sql.txt +++ b/testdata/result/statement/create_change_stream_for_table.sql.txt @@ -12,6 +12,7 @@ CREATE CHANGE STREAM change_stream_name FOR table_name For: 40, Tables: []*ast.ChangeStreamForTable{ &ast.ChangeStreamForTable{ + Rparen: -1, TableName: &ast.Ident{ NamePos: 44, NameEnd: 54, diff --git a/testdata/result/statement/create_change_stream_for_tables.sql.txt b/testdata/result/statement/create_change_stream_for_tables.sql.txt index 3cfd2415..9c0bc622 100644 --- a/testdata/result/statement/create_change_stream_for_tables.sql.txt +++ b/testdata/result/statement/create_change_stream_for_tables.sql.txt @@ -12,6 +12,7 @@ CREATE CHANGE STREAM change_stream_name FOR table_name1, table_name2 For: 40, Tables: []*ast.ChangeStreamForTable{ &ast.ChangeStreamForTable{ + Rparen: -1, TableName: &ast.Ident{ NamePos: 44, NameEnd: 55, @@ -19,6 +20,7 @@ CREATE CHANGE STREAM change_stream_name FOR table_name1, table_name2 }, }, &ast.ChangeStreamForTable{ + Rparen: -1, TableName: &ast.Ident{ NamePos: 57, NameEnd: 68, diff --git a/testdata/result/statement/create_table.sql.txt b/testdata/result/statement/create_table.sql.txt index 47df64b5..abfa24b5 100644 --- a/testdata/result/statement/create_table.sql.txt +++ b/testdata/result/statement/create_table.sql.txt @@ -16,8 +16,9 @@ create table foo ( --- AST &ast.CreateTable{ - Rparen: 573, - Name: &ast.Path{ + Rparen: 550, + PrimaryKeyRparen: 573, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 13, diff --git a/testdata/result/statement/create_table_cluster.sql.txt b/testdata/result/statement/create_table_cluster.sql.txt index de344c18..b49f28c5 100644 --- a/testdata/result/statement/create_table_cluster.sql.txt +++ b/testdata/result/statement/create_table_cluster.sql.txt @@ -7,8 +7,9 @@ create table foo ( --- AST &ast.CreateTable{ - Rparen: 59, - Name: &ast.Path{ + Rparen: 44, + PrimaryKeyRparen: 59, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 13, diff --git a/testdata/result/statement/create_table_cluster_and_row_deletion_policy.sql.txt b/testdata/result/statement/create_table_cluster_and_row_deletion_policy.sql.txt index 6f01e0b7..54b627b0 100644 --- a/testdata/result/statement/create_table_cluster_and_row_deletion_policy.sql.txt +++ b/testdata/result/statement/create_table_cluster_and_row_deletion_policy.sql.txt @@ -9,8 +9,9 @@ create table foo ( --- AST &ast.CreateTable{ - Rparen: 77, - Name: &ast.Path{ + Rparen: 62, + PrimaryKeyRparen: 77, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 13, diff --git a/testdata/result/statement/create_table_cluster_on_delete_no_action.sql.txt b/testdata/result/statement/create_table_cluster_on_delete_no_action.sql.txt index 5bbfd8b6..afc8cc0b 100644 --- a/testdata/result/statement/create_table_cluster_on_delete_no_action.sql.txt +++ b/testdata/result/statement/create_table_cluster_on_delete_no_action.sql.txt @@ -6,8 +6,9 @@ create table foo ( on delete no action --- AST &ast.CreateTable{ - Rparen: 50, - Name: &ast.Path{ + Rparen: 32, + PrimaryKeyRparen: 50, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 13, diff --git a/testdata/result/statement/create_table_cluster_set_on_delete.sql.txt b/testdata/result/statement/create_table_cluster_set_on_delete.sql.txt index 38a950da..f3784a48 100644 --- a/testdata/result/statement/create_table_cluster_set_on_delete.sql.txt +++ b/testdata/result/statement/create_table_cluster_set_on_delete.sql.txt @@ -6,8 +6,9 @@ create table foo ( on delete cascade --- AST &ast.CreateTable{ - Rparen: 49, - Name: &ast.Path{ + Rparen: 31, + PrimaryKeyRparen: 49, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 13, diff --git a/testdata/result/statement/create_table_for_format_test.sql.txt b/testdata/result/statement/create_table_for_format_test.sql.txt index 49f89708..8824cce8 100644 --- a/testdata/result/statement/create_table_for_format_test.sql.txt +++ b/testdata/result/statement/create_table_for_format_test.sql.txt @@ -15,9 +15,10 @@ create table if not exists foo ( --- AST &ast.CreateTable{ - Rparen: 461, - IfNotExists: true, - Name: &ast.Path{ + Rparen: 443, + PrimaryKeyRparen: 461, + IfNotExists: true, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 27, diff --git a/testdata/result/statement/create_table_fulltext_albums.sql.txt b/testdata/result/statement/create_table_fulltext_albums.sql.txt index fe0d66bf..4f75acb1 100644 --- a/testdata/result/statement/create_table_fulltext_albums.sql.txt +++ b/testdata/result/statement/create_table_fulltext_albums.sql.txt @@ -11,9 +11,10 @@ CREATE TABLE Albums ( ) PRIMARY KEY(AlbumId) --- AST &ast.CreateTable{ - Create: 105, - Rparen: 575, - Name: &ast.Path{ + Create: 105, + Rparen: 554, + PrimaryKeyRparen: 575, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 118, diff --git a/testdata/result/statement/create_table_if_not_exists.sql.txt b/testdata/result/statement/create_table_if_not_exists.sql.txt index ad4b82a5..102c6a4c 100644 --- a/testdata/result/statement/create_table_if_not_exists.sql.txt +++ b/testdata/result/statement/create_table_if_not_exists.sql.txt @@ -6,9 +6,10 @@ create table if not exists foo ( --- AST &ast.CreateTable{ - Rparen: 93, - IfNotExists: true, - Name: &ast.Path{ + Rparen: 70, + PrimaryKeyRparen: 93, + IfNotExists: true, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 27, diff --git a/testdata/result/statement/create_table_row_deletion_policy.sql.txt b/testdata/result/statement/create_table_row_deletion_policy.sql.txt index 41636dff..42297b24 100644 --- a/testdata/result/statement/create_table_row_deletion_policy.sql.txt +++ b/testdata/result/statement/create_table_row_deletion_policy.sql.txt @@ -8,8 +8,9 @@ create table foo ( --- AST &ast.CreateTable{ - Rparen: 77, - Name: &ast.Path{ + Rparen: 62, + PrimaryKeyRparen: 77, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 13, diff --git a/testdata/result/statement/create_table_synonyms.sql.txt b/testdata/result/statement/create_table_synonyms.sql.txt index c241363c..14dc0774 100644 --- a/testdata/result/statement/create_table_synonyms.sql.txt +++ b/testdata/result/statement/create_table_synonyms.sql.txt @@ -6,8 +6,9 @@ CREATE TABLE Singers ( ) PRIMARY KEY (SingerId) --- AST &ast.CreateTable{ - Rparen: 126, - Name: &ast.Path{ + Rparen: 103, + PrimaryKeyRparen: 126, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 13, diff --git a/testdata/result/statement/create_table_synonyms_abnormal.sql.txt b/testdata/result/statement/create_table_synonyms_abnormal.sql.txt index 05111cf3..1e02107f 100644 --- a/testdata/result/statement/create_table_synonyms_abnormal.sql.txt +++ b/testdata/result/statement/create_table_synonyms_abnormal.sql.txt @@ -8,9 +8,10 @@ CREATE TABLE Singers ( ) PRIMARY KEY (SingerId) --- AST &ast.CreateTable{ - Create: 45, - Rparen: 194, - Name: &ast.Path{ + Create: 45, + Rparen: 171, + PrimaryKeyRparen: 194, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 58, diff --git a/testdata/result/statement/create_table_trailing_comma.sql.txt b/testdata/result/statement/create_table_trailing_comma.sql.txt index f8af47cd..128e3924 100644 --- a/testdata/result/statement/create_table_trailing_comma.sql.txt +++ b/testdata/result/statement/create_table_trailing_comma.sql.txt @@ -9,8 +9,9 @@ create table foo ( --- AST &ast.CreateTable{ - Rparen: 83, - Name: &ast.Path{ + Rparen: 45, + PrimaryKeyRparen: 83, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 13, diff --git a/testdata/result/statement/create_table_types.sql.txt b/testdata/result/statement/create_table_types.sql.txt index c5baf99c..0ff8f67e 100644 --- a/testdata/result/statement/create_table_types.sql.txt +++ b/testdata/result/statement/create_table_types.sql.txt @@ -31,9 +31,10 @@ create table types ( --- AST &ast.CreateTable{ - Create: 100, - Rparen: 796, - Name: &ast.Path{ + Create: 100, + Rparen: 780, + PrimaryKeyRparen: 796, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 113, diff --git a/testdata/result/statement/create_table_with_identity_columns.sql.txt b/testdata/result/statement/create_table_with_identity_columns.sql.txt index 689d2640..28c9cae6 100644 --- a/testdata/result/statement/create_table_with_identity_columns.sql.txt +++ b/testdata/result/statement/create_table_with_identity_columns.sql.txt @@ -12,8 +12,9 @@ create table foo ( --- AST &ast.CreateTable{ - Rparen: -1, - Name: &ast.Path{ + Rparen: 460, + PrimaryKeyRparen: -1, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 13, diff --git a/testdata/result/statement/create_table_with_sequence_function.sql.txt b/testdata/result/statement/create_table_with_sequence_function.sql.txt index 44635803..7da3ed10 100644 --- a/testdata/result/statement/create_table_with_sequence_function.sql.txt +++ b/testdata/result/statement/create_table_with_sequence_function.sql.txt @@ -8,8 +8,9 @@ CREATE TABLE foo --- AST &ast.CreateTable{ - Rparen: 143, - Name: &ast.Path{ + Rparen: 127, + PrimaryKeyRparen: 143, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 13, diff --git a/testdata/result/statement/named_schema_create_table_backquoted.sql.txt b/testdata/result/statement/named_schema_create_table_backquoted.sql.txt index 955d8e88..e5778c1f 100644 --- a/testdata/result/statement/named_schema_create_table_backquoted.sql.txt +++ b/testdata/result/statement/named_schema_create_table_backquoted.sql.txt @@ -2,8 +2,9 @@ CREATE TABLE `ORDER`.`ORDER`(PK INT64) PRIMARY KEY(PK) --- AST &ast.CreateTable{ - Rparen: 53, - Name: &ast.Path{ + Rparen: 37, + PrimaryKeyRparen: 53, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 13, diff --git a/testdata/result/statement/named_schemas_create_table.sql.txt b/testdata/result/statement/named_schemas_create_table.sql.txt index 90c87f6a..b3c76833 100644 --- a/testdata/result/statement/named_schemas_create_table.sql.txt +++ b/testdata/result/statement/named_schemas_create_table.sql.txt @@ -7,8 +7,9 @@ CREATE TABLE sch1.Singers ( ) PRIMARY KEY(SingerId) --- AST &ast.CreateTable{ - Rparen: 161, - Name: &ast.Path{ + Rparen: 139, + PrimaryKeyRparen: 161, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 13, diff --git a/testdata/result/statement/named_schemas_create_table_foreign_key.sql.txt b/testdata/result/statement/named_schemas_create_table_foreign_key.sql.txt index cb4bce8f..fe0a7438 100644 --- a/testdata/result/statement/named_schemas_create_table_foreign_key.sql.txt +++ b/testdata/result/statement/named_schemas_create_table_foreign_key.sql.txt @@ -8,8 +8,9 @@ CREATE TABLE sch1.ShoppingCarts ( ) PRIMARY KEY(CartId) --- AST &ast.CreateTable{ - Rparen: 296, - Name: &ast.Path{ + Rparen: 276, + PrimaryKeyRparen: 296, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 13, diff --git a/testdata/result/statement/named_schemas_create_table_interleave.sql.txt b/testdata/result/statement/named_schemas_create_table_interleave.sql.txt index dcb2ea27..cb68b1f4 100644 --- a/testdata/result/statement/named_schemas_create_table_interleave.sql.txt +++ b/testdata/result/statement/named_schemas_create_table_interleave.sql.txt @@ -7,8 +7,9 @@ CREATE TABLE sch1.Albums ( INTERLEAVE IN PARENT sch1.Singers ON DELETE CASCADE --- AST &ast.CreateTable{ - Rparen: 137, - Name: &ast.Path{ + Rparen: 106, + PrimaryKeyRparen: 137, + Name: &ast.Path{ Idents: []*ast.Ident{ &ast.Ident{ NamePos: 13, diff --git a/testdata/result/statement/select_cast.sql.txt b/testdata/result/statement/select_cast.sql.txt index db776822..be9059d3 100644 --- a/testdata/result/statement/select_cast.sql.txt +++ b/testdata/result/statement/select_cast.sql.txt @@ -156,9 +156,7 @@ limit cast(1 as INT64) offset cast(@foo as INT64) From: &ast.From{ From: 149, Source: &ast.Join{ - Op: ",", Left: &ast.Join{ - Op: ",", Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 154, @@ -185,6 +183,7 @@ limit cast(1 as INT64) offset cast(@foo as INT64) }, }, }, + Op: ",", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 215, @@ -213,6 +212,7 @@ limit cast(1 as INT64) offset cast(@foo as INT64) }, }, }, + Op: ",", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 269, diff --git a/testdata/result/statement/select_complex_with_array_path.sql.txt b/testdata/result/statement/select_complex_with_array_path.sql.txt index cbfb49b5..7290fd52 100644 --- a/testdata/result/statement/select_complex_with_array_path.sql.txt +++ b/testdata/result/statement/select_complex_with_array_path.sql.txt @@ -16,7 +16,6 @@ FROM From: &ast.From{ From: 11, Source: &ast.Join{ - Op: ",", Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 18, @@ -24,6 +23,7 @@ FROM Name: "ComplexTable", }, }, + Op: ",", Right: &ast.PathTableExpr{ Path: &ast.Path{ Idents: []*ast.Ident{ diff --git a/testdata/result/statement/select_complex_with_unnest_array_path.sql.txt b/testdata/result/statement/select_complex_with_unnest_array_path.sql.txt index 5008c0b7..c74cef04 100644 --- a/testdata/result/statement/select_complex_with_unnest_array_path.sql.txt +++ b/testdata/result/statement/select_complex_with_unnest_array_path.sql.txt @@ -16,7 +16,6 @@ FROM From: &ast.From{ From: 11, Source: &ast.Join{ - Op: ",", Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 18, @@ -24,6 +23,7 @@ FROM Name: "ComplexTable", }, }, + Op: ",", Right: &ast.Unnest{ Unnest: 34, Rparen: 62, diff --git a/testdata/result/statement/select_from_implicit_unnest.sql.txt b/testdata/result/statement/select_from_implicit_unnest.sql.txt index 06a9e39a..0a0f7600 100644 --- a/testdata/result/statement/select_from_implicit_unnest.sql.txt +++ b/testdata/result/statement/select_from_implicit_unnest.sql.txt @@ -24,7 +24,6 @@ FROM UNNEST([STRUCT>(["foo"])]) AS t, From: &ast.From{ From: 14, Source: &ast.Join{ - Op: ",", Left: &ast.Unnest{ Unnest: 19, Rparen: 62, @@ -79,6 +78,7 @@ FROM UNNEST([STRUCT>(["foo"])]) AS t, }, }, }, + Op: ",", Right: &ast.PathTableExpr{ Path: &ast.Path{ Idents: []*ast.Ident{ diff --git a/testdata/result/statement/select_from_inner_join_path_table_expr.sql.txt b/testdata/result/statement/select_from_inner_join_path_table_expr.sql.txt index bd1a9ff0..dc67ffe5 100644 --- a/testdata/result/statement/select_from_inner_join_path_table_expr.sql.txt +++ b/testdata/result/statement/select_from_inner_join_path_table_expr.sql.txt @@ -45,7 +45,6 @@ FROM From: &ast.From{ From: 113, Source: &ast.Join{ - Op: "INNER JOIN", Left: &ast.Unnest{ Unnest: 120, Rparen: 268, @@ -163,6 +162,7 @@ FROM }, }, }, + Op: "INNER JOIN", Right: &ast.PathTableExpr{ Path: &ast.Path{ Idents: []*ast.Ident{ diff --git a/testdata/result/statement/select_from_join_unnest.sql.txt b/testdata/result/statement/select_from_join_unnest.sql.txt index 8838628b..4056de45 100644 --- a/testdata/result/statement/select_from_join_unnest.sql.txt +++ b/testdata/result/statement/select_from_join_unnest.sql.txt @@ -24,7 +24,6 @@ FROM From: &ast.From{ From: 102, Source: &ast.Join{ - Op: "INNER JOIN", Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 109, @@ -32,6 +31,7 @@ FROM Name: "Roster", }, }, + Op: "INNER JOIN", Right: &ast.Unnest{ Unnest: 127, Rparen: 256, diff --git a/testdata/result/statement/select_from_left_join_path_table_expr.sql.txt b/testdata/result/statement/select_from_left_join_path_table_expr.sql.txt index ab108403..79145378 100644 --- a/testdata/result/statement/select_from_left_join_path_table_expr.sql.txt +++ b/testdata/result/statement/select_from_left_join_path_table_expr.sql.txt @@ -85,7 +85,6 @@ FROM From: &ast.From{ From: 156, Source: &ast.Join{ - Op: "LEFT OUTER JOIN", Left: &ast.Unnest{ Unnest: 163, Rparen: 311, @@ -203,6 +202,7 @@ FROM }, }, }, + Op: "LEFT OUTER JOIN", Right: &ast.PathTableExpr{ Path: &ast.Path{ Idents: []*ast.Ident{ diff --git a/testdata/result/statement/select_from_ml_predict_textbison.sql.txt b/testdata/result/statement/select_from_ml_predict_textbison.sql.txt index 60bf3d4c..7ee83491 100644 --- a/testdata/result/statement/select_from_ml_predict_textbison.sql.txt +++ b/testdata/result/statement/select_from_ml_predict_textbison.sql.txt @@ -251,7 +251,6 @@ FROM ML.PREDICT( From: &ast.From{ From: 410, Source: &ast.Join{ - Op: "INNER JOIN", Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 424, @@ -267,6 +266,7 @@ FROM ML.PREDICT( }, }, }, + Op: "INNER JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 449, diff --git a/testdata/result/statement/select_nest_complex.sql.txt b/testdata/result/statement/select_nest_complex.sql.txt index 19613b2e..c2ecbc46 100644 --- a/testdata/result/statement/select_nest_complex.sql.txt +++ b/testdata/result/statement/select_nest_complex.sql.txt @@ -20,7 +20,6 @@ from ( Lparen: 14, Rparen: 131, Source: &ast.Join{ - Op: "INNER JOIN", Left: &ast.SubQueryTableExpr{ Lparen: 20, Rparen: 90, @@ -111,6 +110,7 @@ from ( }, }, }, + Op: "INNER JOIN", Right: &ast.SubQueryTableExpr{ Lparen: 103, Rparen: 119, diff --git a/testdata/result/statement/select_singer_with_cross_join.sql.txt b/testdata/result/statement/select_singer_with_cross_join.sql.txt index 34362fa4..cc86b273 100644 --- a/testdata/result/statement/select_singer_with_cross_join.sql.txt +++ b/testdata/result/statement/select_singer_with_cross_join.sql.txt @@ -17,7 +17,6 @@ FROM From: &ast.From{ From: 11, Source: &ast.Join{ - Op: "CROSS JOIN", Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 18, @@ -33,6 +32,7 @@ FROM }, }, }, + Op: "CROSS JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 43, diff --git a/testdata/result/statement/select_singer_with_full_join.sql.txt b/testdata/result/statement/select_singer_with_full_join.sql.txt index 4036f6ce..f7187a9b 100644 --- a/testdata/result/statement/select_singer_with_full_join.sql.txt +++ b/testdata/result/statement/select_singer_with_full_join.sql.txt @@ -16,7 +16,6 @@ FROM From: &ast.From{ From: 11, Source: &ast.Join{ - Op: ",", Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 18, @@ -32,6 +31,7 @@ FROM }, }, }, + Op: ",", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 34, diff --git a/testdata/result/statement/select_singer_with_hash_join.sql.txt b/testdata/result/statement/select_singer_with_hash_join.sql.txt index cd61e22e..dc61c560 100644 --- a/testdata/result/statement/select_singer_with_hash_join.sql.txt +++ b/testdata/result/statement/select_singer_with_hash_join.sql.txt @@ -17,9 +17,7 @@ FROM From: &ast.From{ From: 11, Source: &ast.Join{ - Op: "INNER JOIN", - Method: "HASH", - Left: &ast.TableName{ + Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 18, NameEnd: 25, @@ -34,7 +32,9 @@ FROM }, }, }, - Right: &ast.TableName{ + Op: "INNER JOIN", + Method: "HASH", + Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 42, NameEnd: 49, diff --git a/testdata/result/statement/select_singer_with_join.sql.txt b/testdata/result/statement/select_singer_with_join.sql.txt index 70831a2f..d825ac6e 100644 --- a/testdata/result/statement/select_singer_with_join.sql.txt +++ b/testdata/result/statement/select_singer_with_join.sql.txt @@ -18,7 +18,6 @@ FROM From: &ast.From{ From: 11, Source: &ast.Join{ - Op: "LEFT OUTER JOIN", Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 18, @@ -34,6 +33,7 @@ FROM }, }, }, + Op: "LEFT OUTER JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 48, diff --git a/testdata/result/statement/select_singer_with_join_hint.sql.txt b/testdata/result/statement/select_singer_with_join_hint.sql.txt index 06d8a6ef..7dbf20a8 100644 --- a/testdata/result/statement/select_singer_with_join_hint.sql.txt +++ b/testdata/result/statement/select_singer_with_join_hint.sql.txt @@ -27,78 +27,24 @@ FROM From: &ast.From{ From: 11, Source: &ast.Join{ - Op: "INNER JOIN", - Hint: &ast.Hint{ - Atmark: 258, - Rbrace: 279, - Records: []*ast.HintRecord{ - &ast.HintRecord{ - Key: &ast.Path{ - Idents: []*ast.Ident{ - &ast.Ident{ - NamePos: 260, - NameEnd: 269, - Name: "JOIN_TYPE", - }, - }, - }, - Value: &ast.Ident{ - NamePos: 270, - NameEnd: 279, - Name: "LOOP_JOIN", - }, - }, - }, - }, Left: &ast.Join{ - Op: "INNER JOIN", - Hint: &ast.Hint{ - Atmark: 187, - Rbrace: 209, - Records: []*ast.HintRecord{ - &ast.HintRecord{ - Key: &ast.Path{ - Idents: []*ast.Ident{ - &ast.Ident{ - NamePos: 189, - NameEnd: 198, - Name: "JOIN_TYPE", - }, - }, - }, - Value: &ast.Ident{ - NamePos: 199, - NameEnd: 209, - Name: "APPLY_JOIN", - }, - }, - }, - }, Left: &ast.Join{ - Op: "INNER JOIN", - Hint: &ast.Hint{ - Atmark: 117, - Rbrace: 138, - Records: []*ast.HintRecord{ - &ast.HintRecord{ - Key: &ast.Path{ - Idents: []*ast.Ident{ - &ast.Ident{ - NamePos: 119, - NameEnd: 128, - Name: "JOIN_TYPE", - }, - }, - }, - Value: &ast.Ident{ - NamePos: 129, - NameEnd: 138, - Name: "HASH_JOIN", + Left: &ast.Join{ + Left: &ast.TableName{ + Table: &ast.Ident{ + NamePos: 18, + NameEnd: 25, + Name: "Singers", + }, + As: &ast.AsAlias{ + As: -1, + Alias: &ast.Ident{ + NamePos: 26, + NameEnd: 27, + Name: "A", }, }, }, - }, - Left: &ast.Join{ Op: "LEFT OUTER JOIN", Hint: &ast.Hint{ Atmark: 45, @@ -121,21 +67,6 @@ FROM }, }, }, - Left: &ast.TableName{ - Table: &ast.Ident{ - NamePos: 18, - NameEnd: 25, - Name: "Singers", - }, - As: &ast.AsAlias{ - As: -1, - Alias: &ast.Ident{ - NamePos: 26, - NameEnd: 27, - Name: "A", - }, - }, - }, Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 72, @@ -186,6 +117,29 @@ FROM }, }, }, + Op: "INNER JOIN", + Hint: &ast.Hint{ + Atmark: 117, + Rbrace: 138, + Records: []*ast.HintRecord{ + &ast.HintRecord{ + Key: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 119, + NameEnd: 128, + Name: "JOIN_TYPE", + }, + }, + }, + Value: &ast.Ident{ + NamePos: 129, + NameEnd: 138, + Name: "HASH_JOIN", + }, + }, + }, + }, Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 142, @@ -236,6 +190,29 @@ FROM }, }, }, + Op: "INNER JOIN", + Hint: &ast.Hint{ + Atmark: 187, + Rbrace: 209, + Records: []*ast.HintRecord{ + &ast.HintRecord{ + Key: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 189, + NameEnd: 198, + Name: "JOIN_TYPE", + }, + }, + }, + Value: &ast.Ident{ + NamePos: 199, + NameEnd: 209, + Name: "APPLY_JOIN", + }, + }, + }, + }, Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 213, @@ -286,6 +263,29 @@ FROM }, }, }, + Op: "INNER JOIN", + Hint: &ast.Hint{ + Atmark: 258, + Rbrace: 279, + Records: []*ast.HintRecord{ + &ast.HintRecord{ + Key: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 260, + NameEnd: 269, + Name: "JOIN_TYPE", + }, + }, + }, + Value: &ast.Ident{ + NamePos: 270, + NameEnd: 279, + Name: "LOOP_JOIN", + }, + }, + }, + }, Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 283, diff --git a/testdata/result/statement/select_singer_with_join_twice.sql.txt b/testdata/result/statement/select_singer_with_join_twice.sql.txt index c2f8e6dd..c442579e 100644 --- a/testdata/result/statement/select_singer_with_join_twice.sql.txt +++ b/testdata/result/statement/select_singer_with_join_twice.sql.txt @@ -21,9 +21,7 @@ FROM From: &ast.From{ From: 11, Source: &ast.Join{ - Op: "INNER JOIN", Left: &ast.Join{ - Op: "INNER JOIN", Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 18, @@ -39,6 +37,7 @@ FROM }, }, }, + Op: "INNER JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 37, @@ -89,6 +88,7 @@ FROM }, }, }, + Op: "INNER JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 91, diff --git a/testdata/result/statement/select_singer_with_join_using.sql.txt b/testdata/result/statement/select_singer_with_join_using.sql.txt index a68579f7..4ec72a0f 100644 --- a/testdata/result/statement/select_singer_with_join_using.sql.txt +++ b/testdata/result/statement/select_singer_with_join_using.sql.txt @@ -18,7 +18,6 @@ FROM From: &ast.From{ From: 11, Source: &ast.Join{ - Op: "LEFT OUTER JOIN", Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 18, @@ -34,6 +33,7 @@ FROM }, }, }, + Op: "LEFT OUTER JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 48, diff --git a/testdata/result/statement/select_singer_with_join_various.sql.txt b/testdata/result/statement/select_singer_with_join_various.sql.txt index 17f7be5a..25df4633 100644 --- a/testdata/result/statement/select_singer_with_join_various.sql.txt +++ b/testdata/result/statement/select_singer_with_join_various.sql.txt @@ -41,23 +41,14 @@ FROM From: &ast.From{ From: 11, Source: &ast.Join{ - Op: "RIGHT OUTER JOIN", Left: &ast.Join{ - Op: "RIGHT OUTER JOIN", Left: &ast.Join{ - Op: "LEFT OUTER JOIN", Left: &ast.Join{ - Op: "LEFT OUTER JOIN", Left: &ast.Join{ - Op: "FULL OUTER JOIN", Left: &ast.Join{ - Op: "FULL OUTER JOIN", Left: &ast.Join{ - Op: "CROSS JOIN", Left: &ast.Join{ - Op: "INNER JOIN", Left: &ast.Join{ - Op: "INNER JOIN", Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 18, @@ -73,6 +64,7 @@ FROM }, }, }, + Op: "INNER JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 37, @@ -123,6 +115,7 @@ FROM }, }, }, + Op: "INNER JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 91, @@ -173,6 +166,7 @@ FROM }, }, }, + Op: "CROSS JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 145, @@ -189,6 +183,7 @@ FROM }, }, }, + Op: "FULL OUTER JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 169, @@ -239,6 +234,7 @@ FROM }, }, }, + Op: "FULL OUTER JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 228, @@ -289,6 +285,7 @@ FROM }, }, }, + Op: "LEFT OUTER JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 281, @@ -339,6 +336,7 @@ FROM }, }, }, + Op: "LEFT OUTER JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 340, @@ -389,6 +387,7 @@ FROM }, }, }, + Op: "RIGHT OUTER JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 394, @@ -439,6 +438,7 @@ FROM }, }, }, + Op: "RIGHT OUTER JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 454, diff --git a/testdata/result/statement/select_singer_with_tablesample.sql.txt b/testdata/result/statement/select_singer_with_tablesample.sql.txt index 6f4e56c8..4cb6cc9a 100644 --- a/testdata/result/statement/select_singer_with_tablesample.sql.txt +++ b/testdata/result/statement/select_singer_with_tablesample.sql.txt @@ -17,9 +17,7 @@ FROM From: &ast.From{ From: 11, Source: &ast.Join{ - Op: ",", Left: &ast.Join{ - Op: ",", Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 18, @@ -50,6 +48,7 @@ FROM }, }, }, + Op: ",", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 67, @@ -80,6 +79,7 @@ FROM }, }, }, + Op: ",", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 119, diff --git a/testdata/result/statement/select_singer_with_toplevel_join_hint.sql.txt b/testdata/result/statement/select_singer_with_toplevel_join_hint.sql.txt index 0b7ededc..70992dc0 100644 --- a/testdata/result/statement/select_singer_with_toplevel_join_hint.sql.txt +++ b/testdata/result/statement/select_singer_with_toplevel_join_hint.sql.txt @@ -40,7 +40,6 @@ FROM From: &ast.From{ From: 36, Source: &ast.Join{ - Op: "LEFT OUTER JOIN", Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 43, @@ -56,6 +55,7 @@ FROM }, }, }, + Op: "LEFT OUTER JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 73, diff --git a/testdata/result/statement/select_tablesample_with_cross_join.sql.txt b/testdata/result/statement/select_tablesample_with_cross_join.sql.txt index f89a73f5..56a2b950 100644 --- a/testdata/result/statement/select_tablesample_with_cross_join.sql.txt +++ b/testdata/result/statement/select_tablesample_with_cross_join.sql.txt @@ -18,7 +18,6 @@ FROM From: &ast.From{ From: 11, Source: &ast.Join{ - Op: "CROSS JOIN", Left: &ast.TableName{ Table: &ast.Ident{ NamePos: 18, @@ -34,6 +33,7 @@ FROM }, }, }, + Op: "CROSS JOIN", Right: &ast.TableName{ Table: &ast.Ident{ NamePos: 43, diff --git a/tools/gen-ast-walk/main.go b/tools/gen-ast-walk/main.go new file mode 100644 index 00000000..e011c86b --- /dev/null +++ b/tools/gen-ast-walk/main.go @@ -0,0 +1,157 @@ +package main + +import ( + "bytes" + "flag" + "fmt" + "log" + "os" + "sort" + + "github.com/MakeNowJust/heredoc/v2" + "github.com/cloudspannerecosystem/memefish/tools/util/astcatalog" +) + +var ( + usage = heredoc.Doc(` + Usage of tools/gen-ast-walk.go + + A generator of ast/walk_internal.go. + + Example: + + $ go run ./tools/gen-ast-walk/main.go -astfile ast/ast.go -constfile ast/ast_const.go + Print the generated ast/walk_internal.go to stdout. + + Flags: + `) + prologue = heredoc.Doc(` + // Code generated by tools/gen-ast-walk; DO NOT EDIT. + + package ast + + func walkInternal(node Node, v Visitor, stack []*stackItem) []*stackItem { + switch n := node.(type) { + `) +) + +var ( + astfile = flag.String("astfile", "ast/ast.go", "path to ast/ast.go") + constfile = flag.String("constfile", "ast/ast_const.go", "path to ast/ast_const.go") + outfile = flag.String("outfile", "", "output filename (if it is not specified, the result is printed to stdout.)") +) + +func main() { + flag.Usage = func() { + fmt.Print(usage) + flag.PrintDefaults() + } + + flag.Parse() + + catalog, err := astcatalog.Load(*astfile, *constfile) + if err != nil { + log.Fatal(err) + } + + structs := make([]*astcatalog.NodeStructDef, 0, len(catalog.Structs)) + for _, structDef := range catalog.Structs { + structs = append(structs, structDef) + } + sort.Slice(structs, func(i, j int) bool { + return structs[i].SourcePos < structs[j].SourcePos + }) + + var buffer bytes.Buffer + buffer.WriteString(prologue) + + for i, structDef := range structs { + fmt.Fprintf(&buffer, "\tcase *%s:\n", structDef.Name) + + output := false + + for i := len(structDef.Fields) - 1; i >= 0; i-- { + field := structDef.Fields[i] + + if _, ok := extractNodeStructPointer(field.Type); ok { + fmt.Fprintf(&buffer, "\t\tstack = append(stack, &stackItem{node: wrapNode(n.%s), visitor: v.Field(%q)})\n", field.Name, field.Name) + output = true + continue + } + + if _, ok := extractNodeStructSlice(field.Type); ok { + fmt.Fprintf(&buffer, "\t\tstack = append(stack, &stackItem{nodes: wrapNodes(n.%s), visitor: v.Field(%q)})\n", field.Name, field.Name) + output = true + continue + } + + if _, ok := extractNodeInterface(field.Type); ok { + fmt.Fprintf(&buffer, "\t\tstack = append(stack, &stackItem{node: wrapNode(n.%s), visitor: v.Field(%q)})\n", field.Name, field.Name) + output = true + continue + } + + if _, ok := extractNodeInterfaceSlice(field.Type); ok { + fmt.Fprintf(&buffer, "\t\tstack = append(stack, &stackItem{nodes: wrapNodes(n.%s), visitor: v.Field(%q)})\n", field.Name, field.Name) + output = true + continue + } + } + + if !output { + fmt.Fprintln(&buffer, "\t\t// nothing to do") + } + + if i < len(structs)-1 { + fmt.Fprintln(&buffer) + } + } + + fmt.Fprintln(&buffer, "\t}") + fmt.Fprintln(&buffer, "\treturn stack") + fmt.Fprintln(&buffer, "}") + + if *outfile == "" { + fmt.Print(buffer.String()) + return + } + + err = os.WriteFile(*outfile, buffer.Bytes(), 0666) + if err != nil { + log.Fatal(err) + } +} + +func extractNodeStructPointer(ty astcatalog.Type) (astcatalog.NodeStructType, bool) { + if ty, ok := ty.(*astcatalog.PointerType); ok { + if ty, ok := ty.Type.(astcatalog.NodeStructType); ok { + return ty, true + } + } + + return "", false +} + +func extractNodeStructSlice(ty astcatalog.Type) (astcatalog.NodeStructType, bool) { + if ty, ok := ty.(*astcatalog.SliceType); ok { + return extractNodeStructPointer(ty.Type) + } + + return "", false +} + +func extractNodeInterface(ty astcatalog.Type) (astcatalog.NodeInterfaceType, bool) { + if ty, ok := ty.(astcatalog.NodeInterfaceType); ok { + return ty, ok + } + + return "", false +} + +func extractNodeInterfaceSlice(ty astcatalog.Type) (astcatalog.NodeInterfaceType, bool) { + if ty, ok := ty.(*astcatalog.SliceType); ok { + return extractNodeInterface(ty.Type) + } + + return "", false +} diff --git a/tools/util/astcatalog/load.go b/tools/util/astcatalog/load.go index 16f96d8e..9c0aab1d 100644 --- a/tools/util/astcatalog/load.go +++ b/tools/util/astcatalog/load.go @@ -281,7 +281,7 @@ func loadType(t ast.Expr, interfaces map[NodeInterfaceType]*NodeInterfaceDef, co return nil, err } - return PointerType{Type: ty}, nil + return &PointerType{Type: ty}, nil case *ast.ArrayType: if t.Len != nil { return nil, fmt.Errorf("unexpected array type: %#v", t) @@ -292,7 +292,7 @@ func loadType(t ast.Expr, interfaces map[NodeInterfaceType]*NodeInterfaceDef, co return nil, err } - return SliceType{Type: ty}, nil + return &SliceType{Type: ty}, nil default: return nil, fmt.Errorf("unexpected type: %#v", t) }