diff --git a/ast/ast.go b/ast/ast.go index a6d215e7..d1af042b 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -64,6 +64,9 @@ func (CreateSchema) isStatement() {} func (DropSchema) isStatement() {} func (CreateDatabase) isStatement() {} func (AlterDatabase) isStatement() {} +func (CreateProtoBundle) isStatement() {} +func (AlterProtoBundle) isStatement() {} +func (DropProtoBundle) isStatement() {} func (CreateTable) isStatement() {} func (AlterTable) isStatement() {} func (DropTable) isStatement() {} @@ -303,6 +306,9 @@ func (CreateSchema) isDDL() {} func (DropSchema) isDDL() {} func (CreateDatabase) isDDL() {} func (AlterDatabase) isDDL() {} +func (CreateProtoBundle) isDDL() {} +func (AlterProtoBundle) isDDL() {} +func (DropProtoBundle) isDDL() {} func (CreateTable) isDDL() {} func (AlterTable) isDDL() {} func (DropTable) isDDL() {} @@ -1819,6 +1825,105 @@ type AlterDatabase struct { Options *Options } +// ================================================================================ +// +// PROTO BUNDLE statements +// +// ================================================================================ + +type ProtoBundleAlteration interface { + Node + isProtoBundleAlteration() +} + +func (AlterProtoBundleInsert) isProtoBundleAlteration() {} +func (AlterProtoBundleUpdate) isProtoBundleAlteration() {} +func (AlterProtoBundleDelete) isProtoBundleAlteration() {} + +// ProtoBundleTypes is parenthesized Protocol Buffers type names node IN CREATE/ALTER PROTO BUNDLE statement. +// +// ({{.Types | sqlJoin ", "}}) +type ProtoBundleTypes struct { + // pos = Lparen + // end = Rparen + 1 + + Lparen, Rparen token.Pos + Types []*NamedType +} + +// CreateProtoBundle is CREATE PROTO BUNDLE statement node. +// +// CREATE PROTO BUNDLE {{.Types, | sql}} +type CreateProtoBundle struct { + // pos = Create + // end = Types.end + + Create token.Pos // position of "CREATE" keyword + + Types *ProtoBundleTypes +} + +// AlterProtoBundle is ALTER PROTO BUNDLE statement node. +// +// ALTER PROTO BUNDLE {{.Alteration | sql}} +type AlterProtoBundle struct { + // pos = Alter + // end = Alteration.end + + Alter token.Pos // position of "ALTER" keyword + + Alteration ProtoBundleAlteration +} + +// AlterProtoBundleInsert is INSERT (proto_type_name, ...) node in ALTER PROTO BUNDLE. +// +// INSERT {{.Types | sql}} +type AlterProtoBundleInsert struct { + // pos = Insert + // end = Types.end + + Insert token.Pos // position of "INSERT" pseudo keyword + + Types *ProtoBundleTypes +} + +// AlterProtoBundleUpdate is UPDATE (proto_type_name, ...) node in ALTER PROTO BUNDLE. +// +// UPDATE {{.Types | sql}} +type AlterProtoBundleUpdate struct { + // pos = Update + // end = Types.end + + Update token.Pos // position of "UPDATE" pseudo keyword + + Types *ProtoBundleTypes +} + +// AlterProtoBundleDelete is DELETE (proto_type_name, ...) node in ALTER PROTO BUNDLE. +// +// DELETE {{.Types | sql}} +type AlterProtoBundleDelete struct { + // pos = Delete + // end = Types.end + + Delete token.Pos // position of "DELETE" pseudo keyword + + Types *ProtoBundleTypes +} + +// DropProtoBundle is DROP PROTO BUNDLE statement node. +// +// DROP PROTO BUNDLE +type DropProtoBundle struct { + // pos = Drop + // end = Bundle + 6 + + Drop token.Pos // position of "DROP" pseudo keyword + Bundle token.Pos // position of "BUNDLE" pseudo keyword +} + +// end of PROTO BUNDLE statements + // CreateTable is CREATE TABLE statement node. // // CREATE TABLE {{if .IfNotExists}}IF NOT EXISTS{{end}} {{.Name | sql}} ( diff --git a/ast/pos.go b/ast/pos.go index 4bc700cb..9632bfd2 100644 --- a/ast/pos.go +++ b/ast/pos.go @@ -822,6 +822,62 @@ func (a *AlterDatabase) End() token.Pos { return nodeEnd(wrapNode(a.Name)) } +func (p *ProtoBundleTypes) Pos() token.Pos { + return p.Lparen +} + +func (p *ProtoBundleTypes) End() token.Pos { + return posAdd(p.Rparen, 1) +} + +func (c *CreateProtoBundle) Pos() token.Pos { + return c.Create +} + +func (c *CreateProtoBundle) End() token.Pos { + return nodeEnd(wrapNode(c.Types)) +} + +func (a *AlterProtoBundle) Pos() token.Pos { + return a.Alter +} + +func (a *AlterProtoBundle) End() token.Pos { + return nodeEnd(wrapNode(a.Alteration)) +} + +func (a *AlterProtoBundleInsert) Pos() token.Pos { + return a.Insert +} + +func (a *AlterProtoBundleInsert) End() token.Pos { + return nodeEnd(wrapNode(a.Types)) +} + +func (a *AlterProtoBundleUpdate) Pos() token.Pos { + return a.Update +} + +func (a *AlterProtoBundleUpdate) End() token.Pos { + return nodeEnd(wrapNode(a.Types)) +} + +func (a *AlterProtoBundleDelete) Pos() token.Pos { + return a.Delete +} + +func (a *AlterProtoBundleDelete) End() token.Pos { + return nodeEnd(wrapNode(a.Types)) +} + +func (d *DropProtoBundle) Pos() token.Pos { + return d.Drop +} + +func (d *DropProtoBundle) End() token.Pos { + return posAdd(d.Bundle, 6) +} + func (c *CreateTable) Pos() token.Pos { return c.Create } diff --git a/ast/sql.go b/ast/sql.go index 2af3c5f6..7937c483 100644 --- a/ast/sql.go +++ b/ast/sql.go @@ -794,6 +794,20 @@ func (d *AlterDatabase) SQL() string { return "ALTER DATABASE " + d.Name.SQL() + " SET " + d.Options.SQL() } +func (p *ProtoBundleTypes) SQL() string { return "(" + sqlJoin(p.Types, ", ") + ")" } + +func (b *CreateProtoBundle) SQL() string { return "CREATE PROTO BUNDLE " + b.Types.SQL() } + +func (a *AlterProtoBundle) SQL() string { return "ALTER PROTO BUNDLE " + a.Alteration.SQL() } + +func (a *AlterProtoBundleInsert) SQL() string { return "INSERT " + a.Types.SQL() } + +func (a *AlterProtoBundleUpdate) SQL() string { return "UPDATE " + a.Types.SQL() } + +func (a *AlterProtoBundleDelete) SQL() string { return "DELETE " + a.Types.SQL() } + +func (d *DropProtoBundle) SQL() string { return "DROP PROTO BUNDLE" } + func (c *CreateTable) SQL() string { return "CREATE TABLE " + strOpt(c.IfNotExists, "IF NOT EXISTS ") + diff --git a/parser.go b/parser.go index 57c9032d..a6b3193e 100644 --- a/parser.go +++ b/parser.go @@ -2303,6 +2303,8 @@ func (p *Parser) parseDDL() ast.DDL { return p.parseCreateSchema(pos) case p.Token.IsKeywordLike("DATABASE"): return p.parseCreateDatabase(pos) + case p.Token.Kind == "PROTO": + return p.parseCreateProtoBundle(pos) case p.Token.IsKeywordLike("TABLE"): return p.parseCreateTable(pos) case p.Token.IsKeywordLike("SEQUENCE"): @@ -2328,6 +2330,8 @@ func (p *Parser) parseDDL() ast.DDL { return p.parseAlterTable(pos) case p.Token.IsKeywordLike("DATABASE"): return p.parseAlterDatabase(pos) + case p.Token.Kind == "PROTO": + return p.parseAlterProtoBundle(pos) case p.Token.IsKeywordLike("INDEX"): return p.parseAlterIndex(pos) case p.Token.IsKeywordLike("SEARCH"): @@ -2345,6 +2349,8 @@ func (p *Parser) parseDDL() ast.DDL { switch { case p.Token.IsKeywordLike("SCHEMA"): return p.parseDropSchema(pos) + case p.Token.Kind == "PROTO": + return p.parseDropProtoBundle(pos) case p.Token.IsKeywordLike("TABLE"): return p.parseDropTable(pos) case p.Token.IsKeywordLike("INDEX"): @@ -2421,6 +2427,47 @@ func (p *Parser) parseAlterDatabase(pos token.Pos) *ast.AlterDatabase { } } +func (p *Parser) parseProtoBundleTypes() *ast.ProtoBundleTypes { + lparen := p.expect("(").Pos + types := parseCommaSeparatedList(p, p.parseNamedType) + rparen := p.expect(")").Pos + return &ast.ProtoBundleTypes{ + Lparen: lparen, + Rparen: rparen, + Types: types, + } +} + +func (p *Parser) parseCreateProtoBundle(pos token.Pos) *ast.CreateProtoBundle { + p.expect("PROTO") + p.expectKeywordLike("BUNDLE") + types := p.parseProtoBundleTypes() + + return &ast.CreateProtoBundle{ + Create: pos, + Types: types, + } +} + +func (p *Parser) parseAlterProtoBundle(pos token.Pos) *ast.AlterProtoBundle { + p.expect("PROTO") + p.expectKeywordLike("BUNDLE") + alteration := p.parseProtoBundleAlteration() + return &ast.AlterProtoBundle{ + Alter: pos, + Alteration: alteration, + } +} + +func (p *Parser) parseDropProtoBundle(pos token.Pos) *ast.DropProtoBundle { + p.expect("PROTO") + bundle := p.expectKeywordLike("BUNDLE").Pos + + return &ast.DropProtoBundle{ + Drop: pos, + Bundle: bundle, + } +} func (p *Parser) parseCreateTable(pos token.Pos) *ast.CreateTable { p.expectKeywordLike("TABLE") ifNotExists := p.parseIfNotExists() @@ -4194,3 +4241,34 @@ func (p *Parser) parseRenameTable(pos token.Pos) *ast.RenameTable { } } + +func (p *Parser) parseProtoBundleAlteration() ast.ProtoBundleAlteration { + switch { + case p.Token.IsKeywordLike("INSERT"): + insert := p.expectKeywordLike("INSERT").Pos + types := p.parseProtoBundleTypes() + + return &ast.AlterProtoBundleInsert{ + Insert: insert, + Types: types, + } + case p.Token.IsKeywordLike("UPDATE"): + update := p.expectKeywordLike("UPDATE").Pos + types := p.parseProtoBundleTypes() + + return &ast.AlterProtoBundleUpdate{ + Update: update, + Types: types, + } + case p.Token.IsKeywordLike("DELETE"): + delete := p.expectKeywordLike("DELETE").Pos + types := p.parseProtoBundleTypes() + + return &ast.AlterProtoBundleDelete{ + Delete: delete, + Types: types, + } + default: + panic(p.errorfAtToken(&p.Token, `expected INSERT, UPDATE or DELETE, but: %v`, p.Token.AsString)) + } +} diff --git a/testdata/input/ddl/alter_proto_bundle_delete.sql b/testdata/input/ddl/alter_proto_bundle_delete.sql new file mode 100644 index 00000000..345efbaa --- /dev/null +++ b/testdata/input/ddl/alter_proto_bundle_delete.sql @@ -0,0 +1 @@ +ALTER PROTO BUNDLE DELETE(`examples.shipping.OrderHistory`) \ No newline at end of file diff --git a/testdata/input/ddl/alter_proto_bundle_insert.sql b/testdata/input/ddl/alter_proto_bundle_insert.sql new file mode 100644 index 00000000..8d70937e --- /dev/null +++ b/testdata/input/ddl/alter_proto_bundle_insert.sql @@ -0,0 +1,3 @@ +ALTER PROTO BUNDLE INSERT ( + examples.shipping.OrderHistory +) \ No newline at end of file diff --git a/testdata/input/ddl/alter_proto_bundle_update.sql b/testdata/input/ddl/alter_proto_bundle_update.sql new file mode 100644 index 00000000..ab193b66 --- /dev/null +++ b/testdata/input/ddl/alter_proto_bundle_update.sql @@ -0,0 +1 @@ +ALTER PROTO BUNDLE UPDATE(`examples.shipping.Order`) \ No newline at end of file diff --git a/testdata/input/ddl/create_proto_bundle_backquoted.sql b/testdata/input/ddl/create_proto_bundle_backquoted.sql new file mode 100644 index 00000000..72674a3a --- /dev/null +++ b/testdata/input/ddl/create_proto_bundle_backquoted.sql @@ -0,0 +1,6 @@ +-- If you're using a protocol buffer type and any part of the type name is a Spanner reserved keyword, +-- enclose the entire protocol buffer type name in backticks. +CREATE PROTO BUNDLE ( + `examples.shipping.Order`, + `examples.shipping.Order.Address`, + `examples.shipping.Order.Item`) \ No newline at end of file diff --git a/testdata/input/ddl/drop_proto_bunldle.sql b/testdata/input/ddl/drop_proto_bunldle.sql new file mode 100644 index 00000000..a3d3142c --- /dev/null +++ b/testdata/input/ddl/drop_proto_bunldle.sql @@ -0,0 +1 @@ +DROP PROTO BUNDLE \ No newline at end of file diff --git a/testdata/result/ddl/alter_proto_bundle_delete.sql.txt b/testdata/result/ddl/alter_proto_bundle_delete.sql.txt new file mode 100644 index 00000000..fb2dd121 --- /dev/null +++ b/testdata/result/ddl/alter_proto_bundle_delete.sql.txt @@ -0,0 +1,27 @@ +--- alter_proto_bundle_delete.sql +ALTER PROTO BUNDLE DELETE(`examples.shipping.OrderHistory`) +--- AST +&ast.AlterProtoBundle{ + Alter: 0, + Alteration: &ast.AlterProtoBundleDelete{ + Delete: 19, + Types: &ast.ProtoBundleTypes{ + Lparen: 25, + Rparen: 58, + Types: []*ast.NamedType{ + &ast.NamedType{ + Path: []*ast.Ident{ + &ast.Ident{ + NamePos: 26, + NameEnd: 58, + Name: "examples.shipping.OrderHistory", + }, + }, + }, + }, + }, + }, +} + +--- SQL +ALTER PROTO BUNDLE DELETE (`examples.shipping.OrderHistory`) diff --git a/testdata/result/ddl/alter_proto_bundle_insert.sql.txt b/testdata/result/ddl/alter_proto_bundle_insert.sql.txt new file mode 100644 index 00000000..ebe692b9 --- /dev/null +++ b/testdata/result/ddl/alter_proto_bundle_insert.sql.txt @@ -0,0 +1,39 @@ +--- alter_proto_bundle_insert.sql +ALTER PROTO BUNDLE INSERT ( + examples.shipping.OrderHistory +) +--- AST +&ast.AlterProtoBundle{ + Alter: 0, + Alteration: &ast.AlterProtoBundleInsert{ + Insert: 19, + Types: &ast.ProtoBundleTypes{ + Lparen: 26, + Rparen: 61, + Types: []*ast.NamedType{ + &ast.NamedType{ + Path: []*ast.Ident{ + &ast.Ident{ + NamePos: 30, + NameEnd: 38, + Name: "examples", + }, + &ast.Ident{ + NamePos: 39, + NameEnd: 47, + Name: "shipping", + }, + &ast.Ident{ + NamePos: 48, + NameEnd: 60, + Name: "OrderHistory", + }, + }, + }, + }, + }, + }, +} + +--- SQL +ALTER PROTO BUNDLE INSERT (examples.shipping.OrderHistory) diff --git a/testdata/result/ddl/alter_proto_bundle_update.sql.txt b/testdata/result/ddl/alter_proto_bundle_update.sql.txt new file mode 100644 index 00000000..2069c3ec --- /dev/null +++ b/testdata/result/ddl/alter_proto_bundle_update.sql.txt @@ -0,0 +1,27 @@ +--- alter_proto_bundle_update.sql +ALTER PROTO BUNDLE UPDATE(`examples.shipping.Order`) +--- AST +&ast.AlterProtoBundle{ + Alter: 0, + Alteration: &ast.AlterProtoBundleUpdate{ + Update: 19, + Types: &ast.ProtoBundleTypes{ + Lparen: 25, + Rparen: 51, + Types: []*ast.NamedType{ + &ast.NamedType{ + Path: []*ast.Ident{ + &ast.Ident{ + NamePos: 26, + NameEnd: 51, + Name: "examples.shipping.Order", + }, + }, + }, + }, + }, + }, +} + +--- SQL +ALTER PROTO BUNDLE UPDATE (`examples.shipping.Order`) diff --git a/testdata/result/ddl/create_proto_bundle_backquoted.sql.txt b/testdata/result/ddl/create_proto_bundle_backquoted.sql.txt new file mode 100644 index 00000000..e61fa00d --- /dev/null +++ b/testdata/result/ddl/create_proto_bundle_backquoted.sql.txt @@ -0,0 +1,47 @@ +--- create_proto_bundle_backquoted.sql +-- If you're using a protocol buffer type and any part of the type name is a Spanner reserved keyword, +-- enclose the entire protocol buffer type name in backticks. +CREATE PROTO BUNDLE ( + `examples.shipping.Order`, + `examples.shipping.Order.Address`, + `examples.shipping.Order.Item`) +--- AST +&ast.CreateProtoBundle{ + Create: 165, + Types: &ast.ProtoBundleTypes{ + Lparen: 185, + Rparen: 300, + Types: []*ast.NamedType{ + &ast.NamedType{ + Path: []*ast.Ident{ + &ast.Ident{ + NamePos: 194, + NameEnd: 219, + Name: "examples.shipping.Order", + }, + }, + }, + &ast.NamedType{ + Path: []*ast.Ident{ + &ast.Ident{ + NamePos: 228, + NameEnd: 261, + Name: "examples.shipping.Order.Address", + }, + }, + }, + &ast.NamedType{ + Path: []*ast.Ident{ + &ast.Ident{ + NamePos: 270, + NameEnd: 300, + Name: "examples.shipping.Order.Item", + }, + }, + }, + }, + }, +} + +--- SQL +CREATE PROTO BUNDLE (`examples.shipping.Order`, `examples.shipping.Order.Address`, `examples.shipping.Order.Item`) diff --git a/testdata/result/ddl/drop_proto_bunldle.sql.txt b/testdata/result/ddl/drop_proto_bunldle.sql.txt new file mode 100644 index 00000000..b36d664b --- /dev/null +++ b/testdata/result/ddl/drop_proto_bunldle.sql.txt @@ -0,0 +1,10 @@ +--- drop_proto_bunldle.sql +DROP PROTO BUNDLE +--- AST +&ast.DropProtoBundle{ + Drop: 0, + Bundle: 11, +} + +--- SQL +DROP PROTO BUNDLE diff --git a/testdata/result/statement/alter_proto_bundle_delete.sql.txt b/testdata/result/statement/alter_proto_bundle_delete.sql.txt new file mode 100644 index 00000000..fb2dd121 --- /dev/null +++ b/testdata/result/statement/alter_proto_bundle_delete.sql.txt @@ -0,0 +1,27 @@ +--- alter_proto_bundle_delete.sql +ALTER PROTO BUNDLE DELETE(`examples.shipping.OrderHistory`) +--- AST +&ast.AlterProtoBundle{ + Alter: 0, + Alteration: &ast.AlterProtoBundleDelete{ + Delete: 19, + Types: &ast.ProtoBundleTypes{ + Lparen: 25, + Rparen: 58, + Types: []*ast.NamedType{ + &ast.NamedType{ + Path: []*ast.Ident{ + &ast.Ident{ + NamePos: 26, + NameEnd: 58, + Name: "examples.shipping.OrderHistory", + }, + }, + }, + }, + }, + }, +} + +--- SQL +ALTER PROTO BUNDLE DELETE (`examples.shipping.OrderHistory`) diff --git a/testdata/result/statement/alter_proto_bundle_insert.sql.txt b/testdata/result/statement/alter_proto_bundle_insert.sql.txt new file mode 100644 index 00000000..ebe692b9 --- /dev/null +++ b/testdata/result/statement/alter_proto_bundle_insert.sql.txt @@ -0,0 +1,39 @@ +--- alter_proto_bundle_insert.sql +ALTER PROTO BUNDLE INSERT ( + examples.shipping.OrderHistory +) +--- AST +&ast.AlterProtoBundle{ + Alter: 0, + Alteration: &ast.AlterProtoBundleInsert{ + Insert: 19, + Types: &ast.ProtoBundleTypes{ + Lparen: 26, + Rparen: 61, + Types: []*ast.NamedType{ + &ast.NamedType{ + Path: []*ast.Ident{ + &ast.Ident{ + NamePos: 30, + NameEnd: 38, + Name: "examples", + }, + &ast.Ident{ + NamePos: 39, + NameEnd: 47, + Name: "shipping", + }, + &ast.Ident{ + NamePos: 48, + NameEnd: 60, + Name: "OrderHistory", + }, + }, + }, + }, + }, + }, +} + +--- SQL +ALTER PROTO BUNDLE INSERT (examples.shipping.OrderHistory) diff --git a/testdata/result/statement/alter_proto_bundle_update.sql.txt b/testdata/result/statement/alter_proto_bundle_update.sql.txt new file mode 100644 index 00000000..2069c3ec --- /dev/null +++ b/testdata/result/statement/alter_proto_bundle_update.sql.txt @@ -0,0 +1,27 @@ +--- alter_proto_bundle_update.sql +ALTER PROTO BUNDLE UPDATE(`examples.shipping.Order`) +--- AST +&ast.AlterProtoBundle{ + Alter: 0, + Alteration: &ast.AlterProtoBundleUpdate{ + Update: 19, + Types: &ast.ProtoBundleTypes{ + Lparen: 25, + Rparen: 51, + Types: []*ast.NamedType{ + &ast.NamedType{ + Path: []*ast.Ident{ + &ast.Ident{ + NamePos: 26, + NameEnd: 51, + Name: "examples.shipping.Order", + }, + }, + }, + }, + }, + }, +} + +--- SQL +ALTER PROTO BUNDLE UPDATE (`examples.shipping.Order`) diff --git a/testdata/result/statement/create_proto_bundle_backquoted.sql.txt b/testdata/result/statement/create_proto_bundle_backquoted.sql.txt new file mode 100644 index 00000000..e61fa00d --- /dev/null +++ b/testdata/result/statement/create_proto_bundle_backquoted.sql.txt @@ -0,0 +1,47 @@ +--- create_proto_bundle_backquoted.sql +-- If you're using a protocol buffer type and any part of the type name is a Spanner reserved keyword, +-- enclose the entire protocol buffer type name in backticks. +CREATE PROTO BUNDLE ( + `examples.shipping.Order`, + `examples.shipping.Order.Address`, + `examples.shipping.Order.Item`) +--- AST +&ast.CreateProtoBundle{ + Create: 165, + Types: &ast.ProtoBundleTypes{ + Lparen: 185, + Rparen: 300, + Types: []*ast.NamedType{ + &ast.NamedType{ + Path: []*ast.Ident{ + &ast.Ident{ + NamePos: 194, + NameEnd: 219, + Name: "examples.shipping.Order", + }, + }, + }, + &ast.NamedType{ + Path: []*ast.Ident{ + &ast.Ident{ + NamePos: 228, + NameEnd: 261, + Name: "examples.shipping.Order.Address", + }, + }, + }, + &ast.NamedType{ + Path: []*ast.Ident{ + &ast.Ident{ + NamePos: 270, + NameEnd: 300, + Name: "examples.shipping.Order.Item", + }, + }, + }, + }, + }, +} + +--- SQL +CREATE PROTO BUNDLE (`examples.shipping.Order`, `examples.shipping.Order.Address`, `examples.shipping.Order.Item`) diff --git a/testdata/result/statement/drop_proto_bunldle.sql.txt b/testdata/result/statement/drop_proto_bunldle.sql.txt new file mode 100644 index 00000000..b36d664b --- /dev/null +++ b/testdata/result/statement/drop_proto_bunldle.sql.txt @@ -0,0 +1,10 @@ +--- drop_proto_bunldle.sql +DROP PROTO BUNDLE +--- AST +&ast.DropProtoBundle{ + Drop: 0, + Bundle: 11, +} + +--- SQL +DROP PROTO BUNDLE