From 22b9d6d5beb1493f934213ddbaaf2070f3b6d30b Mon Sep 17 00:00:00 2001 From: apstndb <803393+apstndb@users.noreply.github.com> Date: Thu, 19 Sep 2024 16:14:57 +0900 Subject: [PATCH] Improve GQL schema statements --- ast/ast.go | 294 ++---------------- ast/pos.go | 71 +++++ ast/sql.go | 95 ++++++ parser.go | 224 ++++++------- ...or_replace_property_graph_fingraph.sql.txt | 8 +- ...aph_if_not_exists_fingraph_verbose.sql.txt | 17 +- ...or_replace_property_graph_fingraph.sql.txt | 8 +- ...aph_if_not_exists_fingraph_verbose.sql.txt | 17 +- 8 files changed, 326 insertions(+), 408 deletions(-) diff --git a/ast/ast.go b/ast/ast.go index 75d77ad0..ab29b1a1 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -2413,8 +2413,8 @@ type ArraySchemaType struct { // CreatePropertyGraph is CREATE PROPERTY GRAPH statement node. // -// CREATE {{if.OrReplace }}OR REPLACE{{end}} PROPERTY GRAPH {{if .IfNotExists}}IF NOT EXISTS{{end}} {{.Name | sql}} -// {{.Content | sql}} +// CREATE {{if .OrReplace}}OR REPLACE{{end}} PROPERTY GRAPH {{if .IfNotExists}}IF NOT EXISTS{{end}} {{.Name | sql}} +// {{.Content | sql}} type CreatePropertyGraph struct { // pos = Create // end = Content.end @@ -2426,27 +2426,6 @@ type CreatePropertyGraph struct { Content *PropertyGraphContent } -func (c *CreatePropertyGraph) Pos() token.Pos { - return c.Create -} - -func (c *CreatePropertyGraph) End() token.Pos { - return c.Content.End() -} - -func (c *CreatePropertyGraph) SQL() string { - sql := "CREATE " - if c.OrReplace { - sql += "OR REPLACE " - } - sql += "PROPERTY GRAPH " - if c.IfNotExists { - sql += "IF NOT EXISTS " - } - sql += c.Name.SQL() + " " + c.Content.SQL() - return sql -} - // PropertyGraphContent // // NODE TABLES {{.NodeTables | sql}} {{if not(.EdgeTables | isnil)}}NODE TABLES {{.EdgeTables | sqlOpt}}{{end}} @@ -2459,18 +2438,6 @@ type PropertyGraphContent struct { EdgeTables *PropertyGraphElementList //optional } -func (p *PropertyGraphContent) Pos() token.Pos { - return p.Node -} - -func (p *PropertyGraphContent) End() token.Pos { - return firstValidEnd(p.EdgeTables, p.NodeTables) -} - -func (p *PropertyGraphContent) SQL() string { - return "NODE TABLES" + p.NodeTables.SQL() + sqlOpt(" EDGE TABLES ", p.EdgeTables, "") -} - // PropertyGraphElementList // // ({{.Elements | sqlJoin ", "}}) @@ -2482,52 +2449,32 @@ type PropertyGraphElementList struct { Elements []*PropertyGraphElement } -func (p *PropertyGraphElementList) Pos() token.Pos { - return p.LParen -} - -func (p *PropertyGraphElementList) End() token.Pos { - return p.RParen + 1 -} - -func (p *PropertyGraphElementList) SQL() string { - return "(" + sqlJoin(p.Elements, ", ") + ")" -} - // PropertyGraphElement // -// {{.Ident | sql}} {{.AsAlias | sqlOpt}} {{.Keys | sqlOpt}} {{.Properties | sqlOpt}} +// {{.Ident | sql}} {{if not(.Alias | isnil)}} AS {{.Alias | sql}}{{end}} {{.Keys | sqlOpt}} {{.Properties | sqlOpt}} type PropertyGraphElement struct { // pos = Name.pos // end = (Properties ?? Keys ?? Alias ?? Name).end Name *Ident - Alias *AsAlias // optional + Alias *Ident // optional Keys PropertyGraphElementKeys // optional Properties PropertyGraphProperties // optional } -func (p *PropertyGraphElement) Pos() token.Pos { - return p.Name.Pos() -} - -func (p *PropertyGraphElement) End() token.Pos { - return firstValidEnd(p.Properties, p.Keys, p.Alias, p.Name) -} - -func (p *PropertyGraphElement) SQL() string { - return p.Name.SQL() + - sqlOpt(" ", p.Alias, "") + - sqlOpt(" ", p.Keys, "") + - sqlOpt(" ", p.Properties, "") -} - type PropertyGraphProperties interface { Node isPropertyGraphProperties() } -// PropertyGraphLabelAndPropertiesList +func (*PropertyGraphNoProperties) isPropertyGraphProperties() {} +func (*PropertyGraphPropertiesAre) isPropertyGraphProperties() {} +func (*PropertyGraphElementLabelLabelName) isPropertyGraphProperties() {} +func (*PropertyGraphLabelAndPropertiesList) isPropertyGraphProperties() {} +func (*PropertyGraphElementLabelDefaultLabel) isPropertyGraphProperties() {} + +// PropertyGraphLabelAndPropertiesList represents whitespace-separated list of PropertyGraphLabelAndProperties. +// It implements PropertyGraphProperties. // // {{.LabelAndProperties | sqlJoin " "}} type PropertyGraphLabelAndPropertiesList struct { @@ -2537,19 +2484,9 @@ type PropertyGraphLabelAndPropertiesList struct { LabelAndProperties []*PropertyGraphLabelAndProperties } -func (p *PropertyGraphLabelAndPropertiesList) Pos() token.Pos { - return firstPos(p.LabelAndProperties) -} - -func (p *PropertyGraphLabelAndPropertiesList) End() token.Pos { - return lastEnd(p.LabelAndProperties) -} - -func (p *PropertyGraphLabelAndPropertiesList) SQL() string { - return sqlJoin(p.LabelAndProperties, " ") -} - // PropertyGraphLabelAndProperties +// +// {{.Label | sql}} {{.Properties | sqlOpt}} type PropertyGraphLabelAndProperties struct { // pos = Label.pos // end = (Properties ?? Label).end @@ -2558,18 +2495,6 @@ type PropertyGraphLabelAndProperties struct { Properties PropertyGraphElementProperties // optional } -func (p *PropertyGraphLabelAndProperties) Pos() token.Pos { - return p.Label.Pos() -} - -func (p *PropertyGraphLabelAndProperties) End() token.Pos { - return firstValidEnd(p.Properties, p.Label) -} - -func (p *PropertyGraphLabelAndProperties) SQL() string { - return p.Label.SQL() + sqlOpt(" ", p.Properties, "") -} - type PropertyGraphElementLabel interface { Node isPropertyGraphElementLabel() @@ -2589,20 +2514,6 @@ type PropertyGraphElementLabelLabelName struct { Name *Ident } -func (*PropertyGraphElementLabelLabelName) isPropertyGraphProperties() {} - -func (p *PropertyGraphElementLabelLabelName) Pos() token.Pos { - return p.Label -} - -func (p *PropertyGraphElementLabelLabelName) End() token.Pos { - return p.Name.End() -} - -func (p *PropertyGraphElementLabelLabelName) SQL() string { - return "LABEL " + p.Name.SQL() -} - // PropertyGraphElementLabelDefaultLabel // // DEFAULT LABEL @@ -2613,22 +2524,6 @@ type PropertyGraphElementLabelDefaultLabel struct { Label token.Pos } -func (*PropertyGraphElementLabelDefaultLabel) isPropertyGraphProperties() {} - -func (p *PropertyGraphElementLabelDefaultLabel) Pos() token.Pos { - return p.Default -} - -func (p *PropertyGraphElementLabelDefaultLabel) End() token.Pos { - return p.Label + 5 -} - -func (p *PropertyGraphElementLabelDefaultLabel) SQL() string { - return "DEFAULT LABEL" -} - -func (*PropertyGraphLabelAndPropertiesList) isPropertyGraphProperties() {} - type PropertyGraphElementKeys interface { Node isPropertyGraphElementKeys() @@ -2637,42 +2532,28 @@ type PropertyGraphElementKeys interface { func (*PropertyGraphNodeElementKey) isPropertyGraphElementKeys() {} func (*PropertyGraphEdgeElementKeys) isPropertyGraphElementKeys() {} -// PropertyGraphNodeElementKey is a wrapper of PropertyGraphElementKey +// PropertyGraphNodeElementKey is a wrapper of PropertyGraphElementKey to implement PropertyGraphElementKeys +// without deeper AST hierarchy. +// +// {{.PropertyGraphElementKey | sql}} type PropertyGraphNodeElementKey struct { - PropertyGraphElementKey -} - -func (p *PropertyGraphNodeElementKey) Pos() token.Pos { - return p.PropertyGraphElementKey.Pos() -} + // pos = PropertyGraphElementKey.pos + // end = PropertyGraphElementKey.end -func (p *PropertyGraphNodeElementKey) End() token.Pos { - return p.PropertyGraphElementKey.End() -} - -func (p *PropertyGraphNodeElementKey) SQL() string { - return p.PropertyGraphElementKey.SQL() + PropertyGraphElementKey } // PropertyGraphEdgeElementKeys // -// {{.Element | sql}} {{.Source | sql}} {{.Destination | sql}} +// {{.Element | sqlOpt}} {{.Source | sql}} {{.Destination | sql}} type PropertyGraphEdgeElementKeys struct { // pos = Element.pos // end = Destination.end - Element *PropertyGraphElementKey + Element *PropertyGraphElementKey // optional Source *PropertyGraphSourceKey Destination *PropertyGraphDestinationKey } -func (p *PropertyGraphEdgeElementKeys) Pos() token.Pos { - return p.Element.Pos() -} - -func (p *PropertyGraphEdgeElementKeys) End() token.Pos { - return p.Destination.End() -} - func (p *PropertyGraphEdgeElementKeys) SQL() string { return sqlOpt("", p.Element, " ") + p.Source.SQL() + " " + p.Destination.SQL() } @@ -2688,18 +2569,6 @@ type PropertyGraphElementKey struct { Keys *PropertyGraphColumnNameList } -func (p *PropertyGraphElementKey) Pos() token.Pos { - return p.Key -} - -func (p *PropertyGraphElementKey) End() token.Pos { - return p.Keys.End() -} - -func (p *PropertyGraphElementKey) SQL() string { - return "KEY " + p.Keys.SQL() -} - // PropertyGraphSourceKey // // SOURCE KEY {{.Keys | sql}} @@ -2714,20 +2583,6 @@ type PropertyGraphSourceKey struct { ReferenceColumns *PropertyGraphColumnNameList // optional } -func (p *PropertyGraphSourceKey) Pos() token.Pos { - return p.Source -} - -func (p *PropertyGraphSourceKey) End() token.Pos { - return firstValidEnd(p.ReferenceColumns, p.ElementReference) -} - -func (p *PropertyGraphSourceKey) SQL() string { - return "SOURCE KEY " + p.Keys.SQL() + - " REFERENCES " + p.ElementReference.SQL() + - sqlOpt(" ", p.ReferenceColumns, "") -} - // PropertyGraphDestinationKey // // DESTINATION KEY {{.Keys | sql}} @@ -2742,20 +2597,6 @@ type PropertyGraphDestinationKey struct { ReferenceColumns *PropertyGraphColumnNameList // optional } -func (p *PropertyGraphDestinationKey) Pos() token.Pos { - return p.Destination -} - -func (p *PropertyGraphDestinationKey) End() token.Pos { - return firstValidEnd(p.ElementReference, p.ElementReference) -} - -func (p *PropertyGraphDestinationKey) SQL() string { - return "DESTINATION KEY " + p.Keys.SQL() + - " REFERENCES " + p.ElementReference.SQL() + - sqlOpt(" ", p.ReferenceColumns, "") -} - // PropertyGraphColumnNameList // // ({{.ColumnNameList | sqlJoin ", "}}) @@ -2766,18 +2607,6 @@ type PropertyGraphColumnNameList struct { ColumnNameList []*Ident } -func (p *PropertyGraphColumnNameList) Pos() token.Pos { - return p.LParen -} - -func (p *PropertyGraphColumnNameList) End() token.Pos { - return p.RParen + 1 -} - -func (p *PropertyGraphColumnNameList) SQL() string { - return "(" + sqlJoin(p.ColumnNameList, ", ") + ")" -} - // Element properties definition // https://cloud.google.com/spanner/docs/reference/standard-sql/graph-schema-statements#element_table_property_definition @@ -2786,8 +2615,6 @@ type PropertyGraphElementProperties interface { isPropertyGraphElementProperties() } -func (p *PropertyGraphNoProperties) isPropertyGraphElementProperties() {} - // PropertyGraphNoProperties // // NO PROPERTIES @@ -2798,20 +2625,6 @@ type PropertyGraphNoProperties struct { No, Properties token.Pos // position of "NO" and "PROPERTIES" } -func (p *PropertyGraphNoProperties) isPropertyGraphProperties() {} - -func (p *PropertyGraphNoProperties) Pos() token.Pos { - return p.No -} - -func (p PropertyGraphNoProperties) End() token.Pos { - return p.Properties + 10 -} - -func (p PropertyGraphNoProperties) SQL() string { - return "NO PROPERTIES" -} - // PropertyGraphPropertiesAre // // PROPERTIES ARE ALL COLUMNS{{if not(.ExceptColumns | isnil)}} EXCEPT {{.ExceptColumns}}{{end}} @@ -2826,21 +2639,8 @@ type PropertyGraphPropertiesAre struct { } -func (*PropertyGraphPropertiesAre) isPropertyGraphProperties() {} - -func (p *PropertyGraphPropertiesAre) Pos() token.Pos { - return p.Properties -} - -func (p *PropertyGraphPropertiesAre) End() token.Pos { - return p.ExceptColumns.End() -} - -func (p *PropertyGraphPropertiesAre) SQL() string { - return "PROPERTIES ARE ALL COLUMNS" + sqlOpt(" EXCEPT ", p.ExceptColumns, "") -} - -func (*PropertyGraphPropertiesAre) isPropertyGraphElementProperties() {} +func (*PropertyGraphPropertiesAre) isPropertyGraphElementProperties() {} +func (*PropertyGraphDerivedPropertyList) isPropertyGraphElementProperties() {} // PropertyGraphDerivedPropertyList // @@ -2858,20 +2658,6 @@ type PropertyGraphDerivedPropertyList struct { func (*PropertyGraphDerivedPropertyList) isPropertyGraphProperties() {} -func (*PropertyGraphDerivedPropertyList) isPropertyGraphElementProperties() {} - -func (p *PropertyGraphDerivedPropertyList) Pos() token.Pos { - return p.Properties -} - -func (p *PropertyGraphDerivedPropertyList) End() token.Pos { - return p.RParen + 1 -} - -func (p *PropertyGraphDerivedPropertyList) SQL() string { - return "PROPERTIES (" + sqlJoin(p.DerivedProperties, ", ") + ")" -} - // PropertyGraphDerivedProperty // // {{.Expr | sql}}{{if not(.PropertyName | isnil)}} AS {{.PropertyName | sql}}{{end}} @@ -2883,19 +2669,6 @@ type PropertyGraphDerivedProperty struct { PropertyName *Ident //optional } -func (p *PropertyGraphDerivedProperty) Pos() token.Pos { - return p.Expr.Pos() -} - -func (p *PropertyGraphDerivedProperty) End() token.Pos { - return firstValidEnd(p.PropertyName, p.Expr) - -} - -func (p *PropertyGraphDerivedProperty) SQL() string { - return p.Expr.SQL() + sqlOpt(" AS ", p.PropertyName, "") -} - // DropPropertyGraph // // DROP PROPERTY GRAPH {{if .IfExists}}IF EXISTS{{end}} {{.PropertyGraphName | sql}} @@ -2908,23 +2681,6 @@ type DropPropertyGraph struct { Name *Ident } -func (g *DropPropertyGraph) Pos() token.Pos { - return g.Drop -} - -func (g *DropPropertyGraph) End() token.Pos { - return g.Name.End() -} - -func (g *DropPropertyGraph) SQL() string { - sql := "DROP PROPERTY GRAPH " - if g.IfExists { - sql += "IF EXISTS " - } - sql += g.Name.SQL() - return sql -} - // ================================================================================ // // DML diff --git a/ast/pos.go b/ast/pos.go index 48fb51f4..5f9b8a3c 100644 --- a/ast/pos.go +++ b/ast/pos.go @@ -828,6 +828,77 @@ func (e *ExecutePrivilegeOnTableFunction) End() token.Pos { return e.Names[len(e func (r *RolePrivilege) Pos() token.Pos { return r.Role } func (r *RolePrivilege) End() token.Pos { return r.Names[len(r.Names)-1].End() } +// ================================================================================ +// +// GQL schema statements +// +// ================================================================================ + +func (c *CreatePropertyGraph) Pos() token.Pos { return c.Create } +func (c *CreatePropertyGraph) End() token.Pos { return c.Content.End() } + +func (p *PropertyGraphContent) Pos() token.Pos { return p.Node } +func (p *PropertyGraphContent) End() token.Pos { return firstValidEnd(p.EdgeTables, p.NodeTables) } + +func (p *PropertyGraphElementList) Pos() token.Pos { return p.LParen } +func (p *PropertyGraphElementList) End() token.Pos { return p.RParen + 1 } + +func (p *PropertyGraphElement) Pos() token.Pos { return p.Name.Pos() } +func (p *PropertyGraphElement) End() token.Pos { + return firstValidEnd(p.Properties, p.Keys, p.Alias, p.Name) +} + +func (p *PropertyGraphLabelAndPropertiesList) Pos() token.Pos { return firstPos(p.LabelAndProperties) } +func (p *PropertyGraphLabelAndPropertiesList) End() token.Pos { return lastEnd(p.LabelAndProperties) } + +func (p *PropertyGraphLabelAndProperties) Pos() token.Pos { return p.Label.Pos() } +func (p *PropertyGraphLabelAndProperties) End() token.Pos { + return firstValidEnd(p.Properties, p.Label) +} + +func (p *PropertyGraphElementLabelLabelName) Pos() token.Pos { return p.Label } +func (p *PropertyGraphElementLabelLabelName) End() token.Pos { return p.Name.End() } + +func (p *PropertyGraphElementLabelDefaultLabel) Pos() token.Pos { return p.Default } +func (p *PropertyGraphElementLabelDefaultLabel) End() token.Pos { return p.Label + 5 } + +func (p *PropertyGraphNodeElementKey) Pos() token.Pos { return p.PropertyGraphElementKey.Pos() } +func (p *PropertyGraphNodeElementKey) End() token.Pos { return p.PropertyGraphElementKey.End() } + +func (p *PropertyGraphEdgeElementKeys) Pos() token.Pos { return p.Element.Pos() } +func (p *PropertyGraphEdgeElementKeys) End() token.Pos { return p.Destination.End() } + +func (p *PropertyGraphElementKey) Pos() token.Pos { return p.Key } +func (p *PropertyGraphElementKey) End() token.Pos { return p.Keys.End() } + +func (p *PropertyGraphSourceKey) Pos() token.Pos { return p.Source } +func (p *PropertyGraphSourceKey) End() token.Pos { + return firstValidEnd(p.ReferenceColumns, p.ElementReference) +} + +func (p *PropertyGraphDestinationKey) Pos() token.Pos { return p.Destination } +func (p *PropertyGraphDestinationKey) End() token.Pos { + return firstValidEnd(p.ElementReference, p.ElementReference) +} + +func (p *PropertyGraphColumnNameList) Pos() token.Pos { return p.LParen } +func (p *PropertyGraphColumnNameList) End() token.Pos { return p.RParen + 1 } + +func (p *PropertyGraphNoProperties) Pos() token.Pos { return p.No } +func (p *PropertyGraphNoProperties) End() token.Pos { return p.Properties + 10 } + +func (p *PropertyGraphPropertiesAre) Pos() token.Pos { return p.Properties } +func (p *PropertyGraphPropertiesAre) End() token.Pos { return p.ExceptColumns.End() } + +func (p *PropertyGraphDerivedPropertyList) Pos() token.Pos { return p.Properties } +func (p *PropertyGraphDerivedPropertyList) End() token.Pos { return p.RParen + 1 } + +func (p *PropertyGraphDerivedProperty) Pos() token.Pos { return p.Expr.Pos() } +func (p *PropertyGraphDerivedProperty) End() token.Pos { return firstValidEnd(p.PropertyName, p.Expr) } + +func (g *DropPropertyGraph) Pos() token.Pos { return g.Drop } +func (g *DropPropertyGraph) End() token.Pos { return g.Name.End() } + // ================================================================================ // // Types for Schema diff --git a/ast/sql.go b/ast/sql.go index b2f4d3cf..a8a39ad0 100644 --- a/ast/sql.go +++ b/ast/sql.go @@ -1297,6 +1297,101 @@ func (r *RolePrivilege) SQL() string { return sql } +// ================================================================================ +// +// GQL schema statements +// +// ================================================================================ + +func (c *CreatePropertyGraph) SQL() string { + sql := "CREATE " + if c.OrReplace { + sql += "OR REPLACE " + } + sql += "PROPERTY GRAPH " + if c.IfNotExists { + sql += "IF NOT EXISTS " + } + sql += c.Name.SQL() + " " + c.Content.SQL() + return sql +} + +func (p *PropertyGraphContent) SQL() string { + return "NODE TABLES" + p.NodeTables.SQL() + sqlOpt(" EDGE TABLES ", p.EdgeTables, "") +} + +func (p *PropertyGraphElementList) SQL() string { + return "(" + sqlJoin(p.Elements, ", ") + ")" +} + +func (p *PropertyGraphElement) SQL() string { + return p.Name.SQL() + + sqlOpt(" AS ", p.Alias, "") + + sqlOpt(" ", p.Keys, "") + + sqlOpt(" ", p.Properties, "") +} + +func (p *PropertyGraphLabelAndPropertiesList) SQL() string { + return sqlJoin(p.LabelAndProperties, " ") +} + +func (p *PropertyGraphLabelAndProperties) SQL() string { + return p.Label.SQL() + sqlOpt(" ", p.Properties, "") +} + +func (p *PropertyGraphElementLabelLabelName) SQL() string { return "LABEL " + p.Name.SQL() } + +func (p *PropertyGraphElementLabelDefaultLabel) SQL() string { return "DEFAULT LABEL" } + +func (p *PropertyGraphNodeElementKey) SQL() string { return p.PropertyGraphElementKey.SQL() } + +func (p *PropertyGraphElementKey) SQL() string { + return "KEY " + p.Keys.SQL() +} + +func (p *PropertyGraphSourceKey) SQL() string { + return "SOURCE KEY " + p.Keys.SQL() + + " REFERENCES " + p.ElementReference.SQL() + + sqlOpt(" ", p.ReferenceColumns, "") +} + +func (p *PropertyGraphDestinationKey) SQL() string { + return "DESTINATION KEY " + p.Keys.SQL() + + " REFERENCES " + p.ElementReference.SQL() + + sqlOpt(" ", p.ReferenceColumns, "") +} + +func (p *PropertyGraphColumnNameList) SQL() string { + return "(" + sqlJoin(p.ColumnNameList, ", ") + ")" +} + +func (*PropertyGraphNoProperties) isPropertyGraphElementProperties() {} + +func (p *PropertyGraphNoProperties) SQL() string { + return "NO PROPERTIES" +} + +func (p *PropertyGraphPropertiesAre) SQL() string { + return "PROPERTIES ARE ALL COLUMNS" + sqlOpt(" EXCEPT ", p.ExceptColumns, "") +} + +func (p *PropertyGraphDerivedPropertyList) SQL() string { + return "PROPERTIES (" + sqlJoin(p.DerivedProperties, ", ") + ")" +} + +func (p *PropertyGraphDerivedProperty) SQL() string { + return p.Expr.SQL() + sqlOpt(" AS ", p.PropertyName, "") +} + +func (g *DropPropertyGraph) SQL() string { + sql := "DROP PROPERTY GRAPH " + if g.IfExists { + sql += "IF EXISTS " + } + sql += g.Name.SQL() + return sql +} + // ================================================================================ // // Types for Schema diff --git a/parser.go b/parser.go index d11a174a..b734083a 100644 --- a/parser.go +++ b/parser.go @@ -3389,7 +3389,7 @@ func (p *Parser) tryParseTablePrivilegeColumns() ([]*ast.Ident, token.Pos) { return columns, rparen } -// CREATE PROPERTY GRAPH +// begin CREATE PROPERTY GRAPH func (p *Parser) parseCreatePropertyGraph(pos token.Pos, orReplace bool) *ast.CreatePropertyGraph { p.expectKeywordLike("PROPERTY") @@ -3415,8 +3415,7 @@ func (p *Parser) parsePropertyGraphContent() *ast.PropertyGraphContent { nodeTables := p.parsePropertyGraphElementList() var edgeTables *ast.PropertyGraphElementList - if p.Token.IsKeywordLike("EDGE") { - p.expectKeywordLike("EDGE") + if p.tryExpectKeywordLike("EDGE") != nil { p.expectKeywordLike("TABLES") edgeTables = p.parsePropertyGraphElementList() } @@ -3430,7 +3429,7 @@ func (p *Parser) parsePropertyGraphContent() *ast.PropertyGraphContent { func (p *Parser) parsePropertyGraphElementList() *ast.PropertyGraphElementList { lparen := p.expect("(").Pos - elements := parseList(p, ",", p.parsePropertyGraphElement) + elements := parseSeparatedList(p, ",", p.parsePropertyGraphElement) rparen := p.expect(")").Pos return &ast.PropertyGraphElementList{ @@ -3443,9 +3442,9 @@ func (p *Parser) parsePropertyGraphElementList() *ast.PropertyGraphElementList { func (p *Parser) parsePropertyGraphElement() *ast.PropertyGraphElement { name := p.parseIdent() - var alias *ast.AsAlias - if p.Token.Kind == "AS" { - alias = p.tryParseAsAlias() + var alias *ast.Ident + if p.tryExpect("AS") != nil { + alias = p.parseIdent() } keys := p.tryParsePropertyGraphElementKeys() @@ -3459,32 +3458,34 @@ func (p *Parser) parsePropertyGraphElement() *ast.PropertyGraphElement { } } +// parsePropertyGraphLabelAndPropertiesList parses consecutive ast.PropertyGraphLabelAndProperties, +// and returns *ast.PropertyGraphLabelAndPropertiesList. func (p *Parser) parsePropertyGraphLabelAndPropertiesList() *ast.PropertyGraphLabelAndPropertiesList { var list []*ast.PropertyGraphLabelAndProperties for { - var label ast.PropertyGraphElementLabel - if p.Token.Kind == "DEFAULT" { - defaultPos := p.expect("DEFAULT").Pos + if p.Token.Kind != "DEFAULT" && !p.Token.IsKeywordLike("LABEL") { + break + } + + var elemLabel ast.PropertyGraphElementLabel + if def := p.tryExpect("DEFAULT"); def != nil { labelPos := p.expectKeywordLike("LABEL").Pos - label = &ast.PropertyGraphElementLabelDefaultLabel{ - Default: defaultPos, + elemLabel = &ast.PropertyGraphElementLabelDefaultLabel{ + Default: def.Pos, Label: labelPos, } - } else if p.Token.IsKeywordLike("LABEL") { - labelPos := p.expectKeywordLike("LABEL").Pos + } else { + label := p.expectKeywordLike("LABEL") name := p.parseIdent() - - label = &ast.PropertyGraphElementLabelLabelName{ - Label: labelPos, + elemLabel = &ast.PropertyGraphElementLabelLabelName{ + Label: label.Pos, Name: name, } - } else { - break } properties := p.tryParsePropertyGraphElementProperties() list = append(list, &ast.PropertyGraphLabelAndProperties{ - Label: label, + Label: elemLabel, Properties: properties, }) } @@ -3500,71 +3501,58 @@ func (p *Parser) tryParsePropertyGraphElementProperties() ast.PropertyGraphEleme return p.parsePropertyGraphElementProperties() } -func (p *Parser) tryExpect(s token.TokenKind) *token.Token { - if p.Token.Kind != s { - return nil - } - return p.expect(s) -} - -func (p *Parser) tryExpectKeywordLike(s string) *token.Token { - if !p.Token.IsKeywordLike(s) { - return nil +func (p *Parser) parsePropertyGraphElementProperties() ast.PropertyGraphElementProperties { + if p.Token.Kind != "NO" && !p.Token.IsKeywordLike("PROPERTIES") { + p.panicfAtToken(&p.Token, `expect "NO" or "PROPERTIES", but %v`, p.Token.Kind) } - return p.expectKeywordLike(s) -} -func (p *Parser) parsePropertyGraphElementProperties() ast.PropertyGraphElementProperties { - switch { - case p.Token.Kind == "NO": - no := p.expect("NO").Pos + if no := p.tryExpect("NO"); no != nil { properties := p.expectKeywordLike("PROPERTIES").Pos return &ast.PropertyGraphNoProperties{ - No: no, + No: no.Pos, Properties: properties, } - case p.Token.IsKeywordLike("PROPERTIES"): - properties := p.expectKeywordLike("PROPERTIES").Pos - if p.Token.IsKeywordLike("ARE") || p.Token.Kind == "ALL" { - p.tryExpectKeywordLike("ARE") - p.expect("ALL") - columns := p.expectKeywordLike("COLUMNS").Pos - var exceptColumns *ast.PropertyGraphColumnNameList - if p.Token.Kind == "EXCEPT" { - p.expect("EXCEPT") - exceptColumns = p.parsePropertyGraphColumnNameList() - } - return &ast.PropertyGraphPropertiesAre{ - Properties: properties, - Columns: columns, - ExceptColumns: exceptColumns, - } + } + + properties := p.expectKeywordLike("PROPERTIES") + if p.Token.IsKeywordLike("ARE") || p.Token.Kind == "ALL" { + p.tryExpectKeywordLike("ARE") + p.expect("ALL") + columns := p.expectKeywordLike("COLUMNS").Pos + + var exceptColumns *ast.PropertyGraphColumnNameList + if p.tryExpect("EXCEPT") != nil { + exceptColumns = p.parsePropertyGraphColumnNameList() } - p.expect("(") - list := parseList(p, ",", func() *ast.PropertyGraphDerivedProperty { - expr := p.parseExpr() + return &ast.PropertyGraphPropertiesAre{ + Properties: properties.Pos, + Columns: columns, + ExceptColumns: exceptColumns, + } + } - var name *ast.Ident - if p.Token.Kind == "AS" { - p.expect("AS") - name = p.parseIdent() - } - return &ast.PropertyGraphDerivedProperty{ - Expr: expr, - PropertyName: name, - } - }) - rparen := p.expect(")").Pos - return &ast.PropertyGraphDerivedPropertyList{ - RParen: rparen, - Properties: properties, - DerivedProperties: list, + p.expect("(") + list := parseSeparatedList(p, ",", func() *ast.PropertyGraphDerivedProperty { + expr := p.parseExpr() + + var name *ast.Ident + if p.tryExpect("AS") != nil { + name = p.parseIdent() } - default: + + return &ast.PropertyGraphDerivedProperty{ + Expr: expr, + PropertyName: name, + } + }) + rparen := p.expect(")").Pos + + return &ast.PropertyGraphDerivedPropertyList{ + RParen: rparen, + Properties: properties.Pos, + DerivedProperties: list, } - p.panicfAtToken(&p.Token, `expect "NO" or "PROPERTIES", but %v`, p.Token.Kind) - return nil } func (p *Parser) tryParsePropertyGraphProperties() ast.PropertyGraphProperties { @@ -3579,11 +3567,10 @@ func (p *Parser) tryParsePropertyGraphElementKeys() ast.PropertyGraphElementKeys return nil } - if p.Token.IsKeywordLike("KEY") { - key := p.expectKeywordLike("KEY").Pos + if key := p.tryExpectKeywordLike("KEY"); key != nil { keyColumns := p.parsePropertyGraphColumnNameList() elementKey := &ast.PropertyGraphElementKey{ - Key: key, + Key: key.Pos, Keys: keyColumns, } return &ast.PropertyGraphNodeElementKey{ @@ -3597,21 +3584,14 @@ func (p *Parser) tryParsePropertyGraphElementKeys() ast.PropertyGraphElementKeys sourceColumns := p.parsePropertyGraphColumnNameList() p.expectKeywordLike("REFERENCES") sourceReference := p.parseIdent() - - var sourceReferenceColumns *ast.PropertyGraphColumnNameList - if p.Token.Kind == "(" { - sourceReferenceColumns = p.parsePropertyGraphColumnNameList() - } + sourceReferenceColumns := p.tryParsePropertyGraphColumnNameList() destination := p.expectKeywordLike("DESTINATION").Pos p.expectKeywordLike("KEY") destinationColumns := p.parsePropertyGraphColumnNameList() p.expectKeywordLike("REFERENCES") destinationReference := p.parseIdent() - var destinationReferenceColumns *ast.PropertyGraphColumnNameList - if p.Token.Kind == "(" { - destinationReferenceColumns = p.parsePropertyGraphColumnNameList() - } + destinationReferenceColumns := p.tryParsePropertyGraphColumnNameList() return &ast.PropertyGraphEdgeElementKeys{ // Element: elementKey, @@ -3632,7 +3612,7 @@ func (p *Parser) tryParsePropertyGraphElementKeys() ast.PropertyGraphElementKeys func (p *Parser) parsePropertyGraphColumnNameList() *ast.PropertyGraphColumnNameList { lparen := p.expect("(").Pos - list := parseList(p, ",", p.parseIdent) + list := parseSeparatedList(p, ",", p.parseIdent) rparen := p.expect(")").Pos return &ast.PropertyGraphColumnNameList{ LParen: lparen, @@ -3641,34 +3621,14 @@ func (p *Parser) parsePropertyGraphColumnNameList() *ast.PropertyGraphColumnName } } -// This function can't be a method because Go haven't yet supported gemeric methods. -func parseList[T interface { - ast.Node - comparable -}](p *Parser, sep token.TokenKind, doParse func() T) []T { - first := doParse() - var zero T - if first == zero { +func (p *Parser) tryParsePropertyGraphColumnNameList() *ast.PropertyGraphColumnNameList { + if p.Token.Kind != "(" { return nil } - list := []T{first} - - for { - if p.Token.Kind != sep { - break - } - p.nextToken() - - elem := doParse() - if elem == zero { - return list - } - list = append(list, elem) - } - return list + return p.parsePropertyGraphColumnNameList() } -// DROP PROPERTY GRAPH +// end CREATE PROPERTY GRAPH func (p *Parser) parseDropPropertyGraph(pos token.Pos) *ast.DropPropertyGraph { p.expectKeywordLike("PROPERTY") @@ -5000,6 +4960,48 @@ func (p *Parser) expectKeywordLike(s string) *token.Token { return id } +func (p *Parser) tryExpect(s token.TokenKind) *token.Token { + if p.Token.Kind != s { + return nil + } + return p.expect(s) +} + +func (p *Parser) tryExpectKeywordLike(s string) *token.Token { + if !p.Token.IsKeywordLike(s) { + return nil + } + return p.expectKeywordLike(s) +} + +// This function can't be a method because Go haven't yet supported generic methods. +func parseSeparatedList[T interface { + ast.Node + comparable +}](p *Parser, sep token.TokenKind, doParse func() T) []T { + var zero T + + first := doParse() + if first == zero { + return nil + } + + list := []T{first} + for { + if p.Token.Kind != sep { + break + } + p.nextToken() + + elem := doParse() + if elem == zero { + return list + } + list = append(list, elem) + } + return list +} + func (p *Parser) errorfAtToken(tok *token.Token, msg string, params ...interface{}) *Error { return &Error{ Message: fmt.Sprintf(msg, params...), diff --git a/testdata/result/ddl/create_or_replace_property_graph_fingraph.sql.txt b/testdata/result/ddl/create_or_replace_property_graph_fingraph.sql.txt index d328544c..7dd6b1c1 100644 --- a/testdata/result/ddl/create_or_replace_property_graph_fingraph.sql.txt +++ b/testdata/result/ddl/create_or_replace_property_graph_fingraph.sql.txt @@ -36,7 +36,7 @@ CREATE OR REPLACE PROPERTY GRAPH FinGraph NameEnd: 69, Name: "Account", }, - Alias: (*ast.AsAlias)(nil), + Alias: (*ast.Ident)(nil), Keys: nil, Properties: nil, }, @@ -46,7 +46,7 @@ CREATE OR REPLACE PROPERTY GRAPH FinGraph NameEnd: 81, Name: "Person", }, - Alias: (*ast.AsAlias)(nil), + Alias: (*ast.Ident)(nil), Keys: nil, Properties: nil, }, @@ -62,7 +62,7 @@ CREATE OR REPLACE PROPERTY GRAPH FinGraph NameEnd: 122, Name: "PersonOwnAccount", }, - Alias: (*ast.AsAlias)(nil), + Alias: (*ast.Ident)(nil), Keys: &ast.PropertyGraphEdgeElementKeys{ Element: (*ast.PropertyGraphElementKey)(nil), Source: &ast.PropertyGraphSourceKey{ @@ -148,7 +148,7 @@ CREATE OR REPLACE PROPERTY GRAPH FinGraph NameEnd: 271, Name: "AccountTransferAccount", }, - Alias: (*ast.AsAlias)(nil), + Alias: (*ast.Ident)(nil), Keys: &ast.PropertyGraphEdgeElementKeys{ Element: (*ast.PropertyGraphElementKey)(nil), Source: &ast.PropertyGraphSourceKey{ diff --git a/testdata/result/ddl/create_property_graph_if_not_exists_fingraph_verbose.sql.txt b/testdata/result/ddl/create_property_graph_if_not_exists_fingraph_verbose.sql.txt index 02a55722..27ed080d 100644 --- a/testdata/result/ddl/create_property_graph_if_not_exists_fingraph_verbose.sql.txt +++ b/testdata/result/ddl/create_property_graph_if_not_exists_fingraph_verbose.sql.txt @@ -40,13 +40,10 @@ CREATE PROPERTY GRAPH IF NOT EXISTS FinGraph NameEnd: 72, Name: "Account", }, - Alias: &ast.AsAlias{ - As: -1, - Alias: &ast.Ident{ - NamePos: 76, - NameEnd: 83, - Name: "Account", - }, + Alias: &ast.Ident{ + NamePos: 76, + NameEnd: 83, + Name: "Account", }, Keys: nil, Properties: &ast.PropertyGraphLabelAndPropertiesList{ @@ -100,7 +97,7 @@ CREATE PROPERTY GRAPH IF NOT EXISTS FinGraph NameEnd: 179, Name: "Person", }, - Alias: (*ast.AsAlias)(nil), + Alias: (*ast.Ident)(nil), Keys: nil, Properties: &ast.PropertyGraphLabelAndPropertiesList{ LabelAndProperties: []*ast.PropertyGraphLabelAndProperties{ @@ -134,7 +131,7 @@ CREATE PROPERTY GRAPH IF NOT EXISTS FinGraph NameEnd: 270, Name: "PersonOwnAccount", }, - Alias: (*ast.AsAlias)(nil), + Alias: (*ast.Ident)(nil), Keys: &ast.PropertyGraphEdgeElementKeys{ Element: (*ast.PropertyGraphElementKey)(nil), Source: &ast.PropertyGraphSourceKey{ @@ -234,7 +231,7 @@ CREATE PROPERTY GRAPH IF NOT EXISTS FinGraph NameEnd: 453, Name: "AccountTransferAccount", }, - Alias: (*ast.AsAlias)(nil), + Alias: (*ast.Ident)(nil), Keys: &ast.PropertyGraphEdgeElementKeys{ Element: (*ast.PropertyGraphElementKey)(nil), Source: &ast.PropertyGraphSourceKey{ diff --git a/testdata/result/statement/create_or_replace_property_graph_fingraph.sql.txt b/testdata/result/statement/create_or_replace_property_graph_fingraph.sql.txt index d328544c..7dd6b1c1 100644 --- a/testdata/result/statement/create_or_replace_property_graph_fingraph.sql.txt +++ b/testdata/result/statement/create_or_replace_property_graph_fingraph.sql.txt @@ -36,7 +36,7 @@ CREATE OR REPLACE PROPERTY GRAPH FinGraph NameEnd: 69, Name: "Account", }, - Alias: (*ast.AsAlias)(nil), + Alias: (*ast.Ident)(nil), Keys: nil, Properties: nil, }, @@ -46,7 +46,7 @@ CREATE OR REPLACE PROPERTY GRAPH FinGraph NameEnd: 81, Name: "Person", }, - Alias: (*ast.AsAlias)(nil), + Alias: (*ast.Ident)(nil), Keys: nil, Properties: nil, }, @@ -62,7 +62,7 @@ CREATE OR REPLACE PROPERTY GRAPH FinGraph NameEnd: 122, Name: "PersonOwnAccount", }, - Alias: (*ast.AsAlias)(nil), + Alias: (*ast.Ident)(nil), Keys: &ast.PropertyGraphEdgeElementKeys{ Element: (*ast.PropertyGraphElementKey)(nil), Source: &ast.PropertyGraphSourceKey{ @@ -148,7 +148,7 @@ CREATE OR REPLACE PROPERTY GRAPH FinGraph NameEnd: 271, Name: "AccountTransferAccount", }, - Alias: (*ast.AsAlias)(nil), + Alias: (*ast.Ident)(nil), Keys: &ast.PropertyGraphEdgeElementKeys{ Element: (*ast.PropertyGraphElementKey)(nil), Source: &ast.PropertyGraphSourceKey{ diff --git a/testdata/result/statement/create_property_graph_if_not_exists_fingraph_verbose.sql.txt b/testdata/result/statement/create_property_graph_if_not_exists_fingraph_verbose.sql.txt index 02a55722..27ed080d 100644 --- a/testdata/result/statement/create_property_graph_if_not_exists_fingraph_verbose.sql.txt +++ b/testdata/result/statement/create_property_graph_if_not_exists_fingraph_verbose.sql.txt @@ -40,13 +40,10 @@ CREATE PROPERTY GRAPH IF NOT EXISTS FinGraph NameEnd: 72, Name: "Account", }, - Alias: &ast.AsAlias{ - As: -1, - Alias: &ast.Ident{ - NamePos: 76, - NameEnd: 83, - Name: "Account", - }, + Alias: &ast.Ident{ + NamePos: 76, + NameEnd: 83, + Name: "Account", }, Keys: nil, Properties: &ast.PropertyGraphLabelAndPropertiesList{ @@ -100,7 +97,7 @@ CREATE PROPERTY GRAPH IF NOT EXISTS FinGraph NameEnd: 179, Name: "Person", }, - Alias: (*ast.AsAlias)(nil), + Alias: (*ast.Ident)(nil), Keys: nil, Properties: &ast.PropertyGraphLabelAndPropertiesList{ LabelAndProperties: []*ast.PropertyGraphLabelAndProperties{ @@ -134,7 +131,7 @@ CREATE PROPERTY GRAPH IF NOT EXISTS FinGraph NameEnd: 270, Name: "PersonOwnAccount", }, - Alias: (*ast.AsAlias)(nil), + Alias: (*ast.Ident)(nil), Keys: &ast.PropertyGraphEdgeElementKeys{ Element: (*ast.PropertyGraphElementKey)(nil), Source: &ast.PropertyGraphSourceKey{ @@ -234,7 +231,7 @@ CREATE PROPERTY GRAPH IF NOT EXISTS FinGraph NameEnd: 453, Name: "AccountTransferAccount", }, - Alias: (*ast.AsAlias)(nil), + Alias: (*ast.Ident)(nil), Keys: &ast.PropertyGraphEdgeElementKeys{ Element: (*ast.PropertyGraphElementKey)(nil), Source: &ast.PropertyGraphSourceKey{