diff --git a/ast/ast.go b/ast/ast.go index 445eb898..295b21b4 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -58,6 +58,7 @@ func (CreateView) isStatement() {} func (CreateIndex) isStatement() {} func (CreateRole) isStatement() {} func (AlterTable) isStatement() {} +func (AlterIndex) isStatement() {} func (DropTable) isStatement() {} func (DropIndex) isStatement() {} func (DropRole) isStatement() {} @@ -225,6 +226,7 @@ func (CreateSequence) isDDL() {} func (AlterTable) isDDL() {} func (DropTable) isDDL() {} func (CreateIndex) isDDL() {} +func (AlterIndex) isDDL() {} func (DropIndex) isDDL() {} func (CreateRole) isDDL() {} func (DropRole) isDDL() {} @@ -292,6 +294,15 @@ func (ScalarSchemaType) isSchemaType() {} func (SizedSchemaType) isSchemaType() {} func (ArraySchemaType) isSchemaType() {} +// IndexAlternation represents ALTER INDEX action. +type IndexAlternation interface { + Node + isIndexAlternation() +} + +func (AddStoredColumn) isIndexAlternation() {} +func (DropStoredColumn) isIndexAlternation() {} + // DML represents data manipulation language in SQL. // // https://cloud.google.com/spanner/docs/data-definition-language @@ -1626,6 +1637,19 @@ type AlterTable struct { TableAlternation TableAlternation } +// AlterIndex is ALTER INDEX statement node. +// +// ALTER INDEX {{.Name | sql}} {{.IndexAlternation | sql}} +type AlterIndex struct { + // pos = Alter + // end = IndexAlternation.end + + Alter token.Pos // position of "ALTER" keyword + + Name *Ident + IndexAlternation IndexAlternation +} + // AlterChangeStream is ALTER CHANGE STREAM statement node. // // ALTER CHANGE STREAM {{.Name | sql}} {{.ChangeStreamAlternation | sql}} @@ -1939,6 +1963,30 @@ type InterleaveIn struct { TableName *Ident } +// AddStoredColumn is ADD STORED COLUMN clause in ALTER INDEX. +// +// ADD STORED COLUMN {{.Name | sql}} +type AddStoredColumn struct { + // pos = Add + // end = Name.end + + Add token.Pos // position of "ADD" keyword + + Name *Ident +} + +// DropStoredColumn is DROP STORED COLUMN clause in ALTER INDEX. +// +// DROP STORED COLUMN {{.Name | sql}} +type DropStoredColumn struct { + // pos = Drop + // end = Name.end + + Drop token.Pos // position of "DROP" keyword + + Name *Ident +} + // DropIndex is DROP INDEX statement node. // // DROP INDEX {{if .IfExists}}IF EXISTS{{end}} {{.Name | sql}} diff --git a/ast/ast_test.go b/ast/ast_test.go index 8b70fbb2..c9c1f999 100644 --- a/ast/ast_test.go +++ b/ast/ast_test.go @@ -13,6 +13,7 @@ func TestStatement(t *testing.T) { Statement(&CreateSequence{}).isStatement() Statement(&CreateRole{}).isStatement() Statement(&AlterTable{}).isStatement() + Statement(&AlterIndex{}).isStatement() Statement(&DropTable{}).isStatement() Statement(&DropIndex{}).isStatement() Statement(&DropRole{}).isStatement() @@ -128,6 +129,7 @@ func TestDDL(t *testing.T) { DDL(&AlterTable{}).isDDL() DDL(&DropTable{}).isDDL() DDL(&CreateIndex{}).isDDL() + DDL(&AlterIndex{}).isDDL() DDL(&DropIndex{}).isDDL() DDL(&CreateRole{}).isDDL() DDL(&DropRole{}).isDDL() @@ -170,6 +172,11 @@ func TestSchemaType(t *testing.T) { SchemaType(&ArraySchemaType{}).isSchemaType() } +func TestIndexAlternation(t *testing.T) { + IndexAlternation(&AddStoredColumn{}).isIndexAlternation() + IndexAlternation(&DropStoredColumn{}).isIndexAlternation() +} + func TestDML(t *testing.T) { DML(&Insert{}).isDML() DML(&Delete{}).isDML() diff --git a/ast/pos.go b/ast/pos.go index c0d14c89..a6aaa402 100644 --- a/ast/pos.go +++ b/ast/pos.go @@ -692,6 +692,15 @@ func (s *Storing) End() token.Pos { return s.Rparen + 1 } func (i *InterleaveIn) Pos() token.Pos { return i.Comma } func (i *InterleaveIn) End() token.Pos { return i.TableName.End() } +func (a *AlterIndex) Pos() token.Pos { return a.Alter } +func (a *AlterIndex) End() token.Pos { return a.IndexAlternation.End() } + +func (a *AddStoredColumn) Pos() token.Pos { return a.Add } +func (a *AddStoredColumn) End() token.Pos { return a.Name.End() } + +func (a *DropStoredColumn) Pos() token.Pos { return a.Drop } +func (a *DropStoredColumn) End() token.Pos { return a.Name.End() } + func (d *DropIndex) Pos() token.Pos { return d.Drop } func (d *DropIndex) End() token.Pos { return d.Name.End() } diff --git a/ast/sql.go b/ast/sql.go index 02b7df27..c31d71bc 100644 --- a/ast/sql.go +++ b/ast/sql.go @@ -1057,6 +1057,18 @@ func (i *InterleaveIn) SQL() string { return ", INTERLEAVE IN " + i.TableName.SQL() } +func (a *AlterIndex) SQL() string { + return "ALTER INDEX " + a.Name.SQL() + " " + a.IndexAlternation.SQL() +} + +func (a *AddStoredColumn) SQL() string { + return "ADD STORED COLUMN " + a.Name.SQL() +} + +func (a *DropStoredColumn) SQL() string { + return "DROP STORED COLUMN " + a.Name.SQL() +} + func (d *DropIndex) SQL() string { sql := "DROP INDEX " if d.IfExists { diff --git a/parser.go b/parser.go index 42757cce..7648d90c 100644 --- a/parser.go +++ b/parser.go @@ -1997,6 +1997,8 @@ func (p *Parser) parseDDL() ast.DDL { switch { case p.Token.IsKeywordLike("TABLE"): return p.parseAlterTable(pos) + case p.Token.IsKeywordLike("INDEX"): + return p.parseAlterIndex(pos) case p.Token.IsKeywordLike("CHANGE"): return p.parseAlterChangeStream(pos) } @@ -2851,6 +2853,53 @@ func (p *Parser) parseAlterColumn() ast.TableAlternation { } } +func (p *Parser) parseAlterIndex(pos token.Pos) *ast.AlterIndex { + p.expectKeywordLike("INDEX") + + name := p.parseIdent() + + var alternation ast.IndexAlternation + switch { + case p.Token.IsKeywordLike("ADD"): + alternation = p.parseAddStoredColumn() + case p.Token.IsKeywordLike("DROP"): + alternation = p.parseDropStoredColumn() + default: + p.panicfAtToken(&p.Token, "expected pseudo keyword: ADD, DROP, but: %s", p.Token.AsString) + } + + return &ast.AlterIndex{ + Alter: pos, + Name: name, + IndexAlternation: alternation, + } +} + +func (p *Parser) parseAddStoredColumn() ast.IndexAlternation { + pos := p.expectKeywordLike("ADD").Pos + p.expectKeywordLike("STORED") + p.expectKeywordLike("COLUMN") + + name := p.parseIdent() + + return &ast.AddStoredColumn{ + Add: pos, + Name: name, + } +} + +func (p *Parser) parseDropStoredColumn() ast.IndexAlternation { + pos := p.expectKeywordLike("DROP").Pos + p.expectKeywordLike("STORED") + p.expectKeywordLike("COLUMN") + + name := p.parseIdent() + + return &ast.DropStoredColumn{ + Drop: pos, + Name: name, + } +} func (p *Parser) parseDropTable(pos token.Pos) *ast.DropTable { p.expectKeywordLike("TABLE") ifExists := p.parseIfExists() diff --git a/testdata/input/ddl/alter_index_add_stored_column.sql b/testdata/input/ddl/alter_index_add_stored_column.sql new file mode 100644 index 00000000..0e8bdc3e --- /dev/null +++ b/testdata/input/ddl/alter_index_add_stored_column.sql @@ -0,0 +1 @@ +alter index foo add stored column bar diff --git a/testdata/input/ddl/alter_index_drop_stored_column.sql b/testdata/input/ddl/alter_index_drop_stored_column.sql new file mode 100644 index 00000000..306aae0f --- /dev/null +++ b/testdata/input/ddl/alter_index_drop_stored_column.sql @@ -0,0 +1 @@ +alter index foo drop stored column bar diff --git a/testdata/result/ddl/alter_index_add_stored_column.sql.txt b/testdata/result/ddl/alter_index_add_stored_column.sql.txt new file mode 100644 index 00000000..558262b9 --- /dev/null +++ b/testdata/result/ddl/alter_index_add_stored_column.sql.txt @@ -0,0 +1,23 @@ +--- alter_index_add_stored_column.sql +alter index foo add stored column bar + +--- AST +&ast.AlterIndex{ + Alter: 0, + Name: &ast.Ident{ + NamePos: 12, + NameEnd: 15, + Name: "foo", + }, + IndexAlternation: &ast.AddStoredColumn{ + Add: 16, + Name: &ast.Ident{ + NamePos: 34, + NameEnd: 37, + Name: "bar", + }, + }, +} + +--- SQL +ALTER INDEX foo ADD STORED COLUMN bar diff --git a/testdata/result/ddl/alter_index_drop_stored_column.sql.txt b/testdata/result/ddl/alter_index_drop_stored_column.sql.txt new file mode 100644 index 00000000..cbad680e --- /dev/null +++ b/testdata/result/ddl/alter_index_drop_stored_column.sql.txt @@ -0,0 +1,23 @@ +--- alter_index_drop_stored_column.sql +alter index foo drop stored column bar + +--- AST +&ast.AlterIndex{ + Alter: 0, + Name: &ast.Ident{ + NamePos: 12, + NameEnd: 15, + Name: "foo", + }, + IndexAlternation: &ast.DropStoredColumn{ + Drop: 16, + Name: &ast.Ident{ + NamePos: 35, + NameEnd: 38, + Name: "bar", + }, + }, +} + +--- SQL +ALTER INDEX foo DROP STORED COLUMN bar diff --git a/testdata/result/statement/alter_index_add_stored_column.sql.txt b/testdata/result/statement/alter_index_add_stored_column.sql.txt new file mode 100644 index 00000000..558262b9 --- /dev/null +++ b/testdata/result/statement/alter_index_add_stored_column.sql.txt @@ -0,0 +1,23 @@ +--- alter_index_add_stored_column.sql +alter index foo add stored column bar + +--- AST +&ast.AlterIndex{ + Alter: 0, + Name: &ast.Ident{ + NamePos: 12, + NameEnd: 15, + Name: "foo", + }, + IndexAlternation: &ast.AddStoredColumn{ + Add: 16, + Name: &ast.Ident{ + NamePos: 34, + NameEnd: 37, + Name: "bar", + }, + }, +} + +--- SQL +ALTER INDEX foo ADD STORED COLUMN bar diff --git a/testdata/result/statement/alter_index_drop_stored_column.sql.txt b/testdata/result/statement/alter_index_drop_stored_column.sql.txt new file mode 100644 index 00000000..cbad680e --- /dev/null +++ b/testdata/result/statement/alter_index_drop_stored_column.sql.txt @@ -0,0 +1,23 @@ +--- alter_index_drop_stored_column.sql +alter index foo drop stored column bar + +--- AST +&ast.AlterIndex{ + Alter: 0, + Name: &ast.Ident{ + NamePos: 12, + NameEnd: 15, + Name: "foo", + }, + IndexAlternation: &ast.DropStoredColumn{ + Drop: 16, + Name: &ast.Ident{ + NamePos: 35, + NameEnd: 38, + Name: "bar", + }, + }, +} + +--- SQL +ALTER INDEX foo DROP STORED COLUMN bar