From fbe1bd4d0806302a48ff4a5822867757893a5f2d Mon Sep 17 00:00:00 2001 From: TOGASHI Tomoki Date: Mon, 23 Jan 2023 13:35:11 +0900 Subject: [PATCH] feat(spanner/spansql): add support for ALTER INDEX statement (#7287) --- spanner/spansql/parser.go | 49 ++++++++++++++++++++++++++++++++++ spanner/spansql/parser_test.go | 15 ++++++++++- spanner/spansql/sql.go | 12 +++++++++ spanner/spansql/sql_test.go | 22 +++++++++++++++ spanner/spansql/types.go | 25 +++++++++++++++++ 5 files changed, 122 insertions(+), 1 deletion(-) diff --git a/spanner/spansql/parser.go b/spanner/spansql/parser.go index 335d4e56c1d6..10dd9b646069 100644 --- a/spanner/spansql/parser.go +++ b/spanner/spansql/parser.go @@ -1053,6 +1053,9 @@ func (p *parser) parseDDLStmt() (DDLStmt, *parseError) { } else if p.sniff("ALTER", "STATISTICS") { as, err := p.parseAlterStatistics() return as, err + } else if p.sniff("ALTER", "INDEX") { + ai, err := p.parseAlterIndex() + return ai, err } return nil, p.errorf("unknown DDL statement") @@ -2321,6 +2324,52 @@ func (p *parser) parseStatisticsOptions() (StatisticsOptions, *parseError) { return opts, nil } +func (p *parser) parseAlterIndex() (*AlterIndex, *parseError) { + debugf("parseAlterIndex: %v", p) + + if err := p.expect("ALTER"); err != nil { + return nil, err + } + pos := p.Pos() + if err := p.expect("INDEX"); err != nil { + return nil, err + } + iname, err := p.parseTableOrIndexOrColumnName() + if err != nil { + return nil, err + } + + a := &AlterIndex{Name: iname, Position: pos} + tok := p.next() + if tok.err != nil { + return nil, tok.err + } + switch { + case tok.caseEqual("ADD"): + if err := p.expect("STORED", "COLUMN"); err != nil { + return nil, err + } + cname, err := p.parseTableOrIndexOrColumnName() + if err != nil { + return nil, err + } + a.Alteration = AddStoredColumn{Name: cname} + return a, nil + case tok.caseEqual("DROP"): + if err := p.expect("STORED", "COLUMN"); err != nil { + return nil, err + } + cname, err := p.parseTableOrIndexOrColumnName() + if err != nil { + return nil, err + } + a.Alteration = DropStoredColumn{Name: cname} + return a, nil + } + + return nil, p.errorf("got %q, expected ADD or DROP", tok.value) +} + var baseTypes = map[string]TypeBase{ "BOOL": Bool, "INT64": Int64, diff --git a/spanner/spansql/parser_test.go b/spanner/spansql/parser_test.go index 2ffe46b5c6d5..c2d90c81db3d 100644 --- a/spanner/spansql/parser_test.go +++ b/spanner/spansql/parser_test.go @@ -668,6 +668,9 @@ func TestParseDDL(t *testing.T) { ALTER TABLE DefaultCol ALTER COLUMN Age SET DEFAULT (0); ALTER TABLE DefaultCol ALTER COLUMN Age STRING(MAX) DEFAULT ("0"); + ALTER INDEX MyFirstIndex ADD STORED COLUMN UpdatedAt; + ALTER INDEX MyFirstIndex DROP STORED COLUMN UpdatedAt; + -- Trailing comment at end of file. `, &DDL{Filename: "filename", List: []DDLStmt{ &CreateTable{ @@ -954,6 +957,16 @@ func TestParseDDL(t *testing.T) { }, Position: line(83), }, + &AlterIndex{ + Name: "MyFirstIndex", + Alteration: AddStoredColumn{Name: "UpdatedAt"}, + Position: line(85), + }, + &AlterIndex{ + Name: "MyFirstIndex", + Alteration: DropStoredColumn{Name: "UpdatedAt"}, + Position: line(86), + }, }, Comments: []*Comment{ { Marker: "#", Start: line(2), End: line(2), @@ -989,7 +1002,7 @@ func TestParseDDL(t *testing.T) { {Marker: "--", Isolated: true, Start: line(75), End: line(75), Text: []string{"Table has a column with a default value."}}, // Comment after everything else. - {Marker: "--", Isolated: true, Start: line(85), End: line(85), Text: []string{"Trailing comment at end of file."}}, + {Marker: "--", Isolated: true, Start: line(88), End: line(88), Text: []string{"Trailing comment at end of file."}}, }}}, // No trailing comma: {`ALTER TABLE T ADD COLUMN C2 INT64`, &DDL{Filename: "filename", List: []DDLStmt{ diff --git a/spanner/spansql/sql.go b/spanner/spansql/sql.go index 92bf0f804ec4..7cb84abd53af 100644 --- a/spanner/spansql/sql.go +++ b/spanner/spansql/sql.go @@ -339,6 +339,18 @@ func (sa StatisticsOptions) SQL() string { return str } +func (ai AlterIndex) SQL() string { + return "ALTER INDEX " + ai.Name.SQL() + " " + ai.Alteration.SQL() +} + +func (asc AddStoredColumn) SQL() string { + return "ADD STORED COLUMN " + asc.Name.SQL() +} + +func (dsc DropStoredColumn) SQL() string { + return "DROP STORED COLUMN " + dsc.Name.SQL() +} + func (d *Delete) SQL() string { return "DELETE FROM " + d.Table.SQL() + " WHERE " + d.Where.SQL() } diff --git a/spanner/spansql/sql_test.go b/spanner/spansql/sql_test.go index da2ba900d555..0c36405d29a6 100644 --- a/spanner/spansql/sql_test.go +++ b/spanner/spansql/sql_test.go @@ -466,6 +466,28 @@ func TestSQL(t *testing.T) { "ALTER STATISTICS auto_20191128_14_47_22UTC SET OPTIONS (allow_gc=false)", reparseDDL, }, + { + &AlterIndex{ + Name: "iname", + Alteration: AddStoredColumn{ + Name: "cname", + }, + Position: line(1), + }, + "ALTER INDEX iname ADD STORED COLUMN cname", + reparseDDL, + }, + { + &AlterIndex{ + Name: "iname", + Alteration: DropStoredColumn{ + Name: "cname", + }, + Position: line(1), + }, + "ALTER INDEX iname DROP STORED COLUMN cname", + reparseDDL, + }, { &Insert{ Table: "Singers", diff --git a/spanner/spansql/types.go b/spanner/spansql/types.go index eba8312c601f..8db0541d045a 100644 --- a/spanner/spansql/types.go +++ b/spanner/spansql/types.go @@ -1146,3 +1146,28 @@ func (SetStatisticsOptions) isStatisticsAlteration() {} type StatisticsOptions struct { AllowGC *bool } + +type AlterIndex struct { + Name ID + Alteration IndexAlteration + + Position Position // position of the "ALTER" token +} + +func (as *AlterIndex) String() string { return fmt.Sprintf("%#v", as) } +func (*AlterIndex) isDDLStmt() {} +func (as *AlterIndex) Pos() Position { return as.Position } +func (as *AlterIndex) clearOffset() { as.Position.Offset = 0 } + +type IndexAlteration interface { + isIndexAlteration() + SQL() string +} + +func (AddStoredColumn) isIndexAlteration() {} +func (DropStoredColumn) isIndexAlteration() {} + +type ( + AddStoredColumn struct{ Name ID } + DropStoredColumn struct{ Name ID } +)