diff --git a/ast/ast.go b/ast/ast.go index 834070a5..a58895cf 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -60,9 +60,11 @@ func (CreateVectorIndex) isStatement() {} func (CreateRole) isStatement() {} func (AlterTable) isStatement() {} func (AlterIndex) isStatement() {} +func (AlterSequence) isStatement() {} func (DropTable) isStatement() {} func (DropIndex) isStatement() {} func (DropVectorIndex) isStatement() {} +func (DropSequence) isStatement() {} func (DropRole) isStatement() {} func (Insert) isStatement() {} func (Delete) isStatement() {} @@ -230,8 +232,10 @@ func (DropTable) isDDL() {} func (CreateIndex) isDDL() {} func (CreateVectorIndex) isDDL() {} func (AlterIndex) isDDL() {} +func (AlterSequence) isDDL() {} func (DropIndex) isDDL() {} func (DropVectorIndex) isDDL() {} +func (DropSequence) isDDL() {} func (CreateRole) isDDL() {} func (DropRole) isDDL() {} func (Grant) isDDL() {} @@ -1448,14 +1452,13 @@ type CreateTable struct { // CREATE SEQUENCE {{if .IfNotExists}}IF NOT EXISTS{{end}} {{.Name | sql}} }} OPTIONS ({{.Options | sqlJoin ","}}) type CreateSequence struct { // pos = Create - // end = Rparen + 1 + // end = Options.end Create token.Pos // position of "CREATE" keyword - Rparen token.Pos // position of ")" of OPTIONS clause Name *Ident IfNotExists bool - Options []*SequenceOption // len(Options) > 0 + Options *SequenceOptions } // ColumnDef is column definition in CREATE TABLE. @@ -1654,6 +1657,17 @@ type AlterIndex struct { IndexAlteration IndexAlteration } +// AlterSequence is ALTER SEQUENCE statement node. +type AlterSequence struct { + // pos = Alter + // end = Options.end + + Alter token.Pos // position of "ALTER" keyword + + Name *Ident + Options *SequenceOptions +} + // AlterChangeStream is ALTER CHANGE STREAM statement node. // // ALTER CHANGE STREAM {{.Name | sql}} {{.ChangeStreamAlteration | sql}} @@ -2067,6 +2081,17 @@ type DropVectorIndex struct { Name *Ident } +// DropSequence is DROP SEQUENCE statement node. +// +// DROP SEQUENCE {{if .IfExists}}IF EXISTS{{end}} {{.Name | sql}} +type DropSequence struct { + // pos = Drop + // end = Name.end + Drop token.Pos + IfExists bool + Name *Ident +} + // CreateRole is CREATE ROLE statement node. // // CREATE ROLE {{.Name | sql}} @@ -2392,3 +2417,16 @@ type SequenceOption struct { Name *Ident Value Expr } + +// SequenceOptions is OPTIONS clause node in CREATE|ALTER SEQUENCE . +// +// OPTIONS ({{.Records | sqlJoin ","}}) +type SequenceOptions struct { + // pos = Options + // end = Rparen + 1 + + Options token.Pos // position of "OPTIONS" keyword + Rparen token.Pos // position of ")" + + Records []*SequenceOption // len(Records) > 0 +} diff --git a/ast/ast_test.go b/ast/ast_test.go index 51e90eaf..e3ab0b70 100644 --- a/ast/ast_test.go +++ b/ast/ast_test.go @@ -127,6 +127,8 @@ func TestDDL(t *testing.T) { DDL(&CreateIndex{}).isDDL() DDL(&CreateVectorIndex{}).isDDL() DDL(&CreateSequence{}).isDDL() + DDL(&AlterSequence{}).isDDL() + DDL(&DropSequence{}).isDDL() DDL(&CreateView{}).isDDL() DDL(&AlterTable{}).isDDL() DDL(&DropTable{}).isDDL() diff --git a/ast/pos.go b/ast/pos.go index aec05369..ad28dcae 100644 --- a/ast/pos.go +++ b/ast/pos.go @@ -500,7 +500,7 @@ func (c *CreateSequence) Pos() token.Pos { } func (c *CreateSequence) End() token.Pos { - return c.Rparen + 1 + return c.Options.End() } func (c *CreateView) Pos() token.Pos { @@ -703,6 +703,9 @@ 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.IndexAlteration.End() } +func (a *AlterSequence) Pos() token.Pos { return a.Alter } +func (a *AlterSequence) End() token.Pos { return a.Options.End() } + func (a *AddStoredColumn) Pos() token.Pos { return a.Add } func (a *AddStoredColumn) End() token.Pos { return a.Name.End() } @@ -715,6 +718,9 @@ func (d *DropIndex) End() token.Pos { return d.Name.End() } func (d *DropVectorIndex) Pos() token.Pos { return d.Drop } func (d *DropVectorIndex) End() token.Pos { return d.Name.End() } +func (d *DropSequence) Pos() token.Pos { return d.Drop } +func (d *DropSequence) End() token.Pos { return d.Name.End() } + func (c *CreateRole) Pos() token.Pos { return c.Create } func (c *CreateRole) End() token.Pos { return c.Name.End() } @@ -855,3 +861,6 @@ func (u *UpdateItem) End() token.Pos { return u.Expr.End() } func (o *SequenceOption) Pos() token.Pos { return o.Name.Pos() } func (o *SequenceOption) End() token.Pos { return o.Value.End() } + +func (o *SequenceOptions) Pos() token.Pos { return o.Options } +func (o *SequenceOptions) End() token.Pos { return o.Rparen + 1 } diff --git a/ast/sql.go b/ast/sql.go index 25bdd7b7..8c3fdad5 100644 --- a/ast/sql.go +++ b/ast/sql.go @@ -482,6 +482,18 @@ func (o *SequenceOption) SQL() string { return o.Name.SQL() + " = " + o.Value.SQL() } +func (o *SequenceOptions) SQL() string { + sql := "OPTIONS (" + for i, o := range o.Records { + if i > 0 { + sql += ", " + } + sql += o.SQL() + } + sql += ")" + return sql +} + func (s *ExprArg) SQL() string { return s.Expr.SQL() } @@ -755,17 +767,14 @@ func (c *CreateSequence) SQL() string { if c.IfNotExists { sql += "IF NOT EXISTS " } - sql += c.Name.SQL() + " OPTIONS (" - for i, o := range c.Options { - if i > 0 { - sql += ", " - } - sql += o.SQL() - } - sql += ")" + sql += c.Name.SQL() + " " + c.Options.SQL() return sql } +func (c *AlterSequence) SQL() string { + return "ALTER SEQUENCE " + c.Name.SQL() + " SET " + c.Options.SQL() +} + func (c *CreateView) SQL() string { sql := "CREATE" if c.OrReplace { @@ -1115,6 +1124,14 @@ func (d *DropVectorIndex) SQL() string { return sql + d.Name.SQL() } +func (d *DropSequence) SQL() string { + sql := "DROP SEQUENCE " + if d.IfExists { + sql += "IF EXISTS " + } + return sql + d.Name.SQL() +} + func (c *CreateRole) SQL() string { return "CREATE ROLE " + c.Name.SQL() } diff --git a/parser.go b/parser.go index ead5e3cb..fb5473c3 100644 --- a/parser.go +++ b/parser.go @@ -2002,6 +2002,8 @@ func (p *Parser) parseDDL() ast.DDL { return p.parseAlterTable(pos) case p.Token.IsKeywordLike("INDEX"): return p.parseAlterIndex(pos) + case p.Token.IsKeywordLike("SEQUENCE"): + return p.parseAlterSequence(pos) case p.Token.IsKeywordLike("CHANGE"): return p.parseAlterChangeStream(pos) } @@ -2015,6 +2017,8 @@ func (p *Parser) parseDDL() ast.DDL { return p.parseDropIndex(pos) case p.Token.IsKeywordLike("VECTOR"): return p.parseDropVectorIndex(pos) + case p.Token.IsKeywordLike("SEQUENCE"): + return p.parseDropSequence(pos) case p.Token.IsKeywordLike("ROLE"): return p.parseDropRole(pos) case p.Token.IsKeywordLike("CHANGE"): @@ -2111,14 +2115,10 @@ func (p *Parser) parseCreateTable(pos token.Pos) *ast.CreateTable { } } -func (p *Parser) parseCreateSequence(pos token.Pos) *ast.CreateSequence { - p.expectKeywordLike("SEQUENCE") - ifNotExists := p.parseIfNotExists() - name := p.parseIdent() - +func (p *Parser) parseSequenceOptions(pos token.Pos) *ast.SequenceOptions { p.expectKeywordLike("OPTIONS") p.expect("(") - var options []*ast.SequenceOption + var records []*ast.SequenceOption for p.Token.Kind != token.TokenEOF { if p.Token.Kind == ")" { break @@ -2134,7 +2134,7 @@ func (p *Parser) parseCreateSequence(pos token.Pos) *ast.CreateSequence { default: p.panicfAtToken(&p.Token, "expected token: , , but: %s", p.Token.Kind) } - options = append(options, &ast.SequenceOption{ + records = append(records, &ast.SequenceOption{ Name: optionName, Value: value, }) @@ -2144,14 +2144,25 @@ func (p *Parser) parseCreateSequence(pos token.Pos) *ast.CreateSequence { p.nextToken() } - if len(options) == 0 { + rparen := p.expect(")").Pos + if len(records) == 0 { p.panicfAtToken(&p.Token, "required at least one option") } + return &ast.SequenceOptions{ + Options: pos, + Rparen: rparen, + Records: records, + } +} + +func (p *Parser) parseCreateSequence(pos token.Pos) *ast.CreateSequence { + p.expectKeywordLike("SEQUENCE") + ifNotExists := p.parseIfNotExists() + name := p.parseIdent() + options := p.parseSequenceOptions(p.Token.Pos) - rparent := p.expect(")").Pos return &ast.CreateSequence{ Create: pos, - Rparen: rparent, Name: name, IfNotExists: ifNotExists, Options: options, @@ -2929,6 +2940,19 @@ func (p *Parser) parseAlterIndex(pos token.Pos) *ast.AlterIndex { } } +func (p *Parser) parseAlterSequence(pos token.Pos) *ast.AlterSequence { + p.expectKeywordLike("SEQUENCE") + name := p.parseIdent() + p.expect("SET") + options := p.parseSequenceOptions(p.Token.Pos) + + return &ast.AlterSequence{ + Alter: pos, + Name: name, + Options: options, + } +} + func (p *Parser) parseAddStoredColumn() ast.IndexAlteration { pos := p.expectKeywordLike("ADD").Pos p.expectKeywordLike("STORED") @@ -2988,6 +3012,17 @@ func (p *Parser) parseDropVectorIndex(pos token.Pos) *ast.DropVectorIndex { } } +func (p *Parser) parseDropSequence(pos token.Pos) *ast.DropSequence { + p.expectKeywordLike("SEQUENCE") + ifExists := p.parseIfExists() + name := p.parseIdent() + return &ast.DropSequence{ + Drop: pos, + IfExists: ifExists, + Name: name, + } +} + func (p *Parser) parseCreateRole(pos token.Pos) *ast.CreateRole { p.expectKeywordLike("ROLE") name := p.parseIdent() diff --git a/testdata/input/ddl/alter_sequence.sql b/testdata/input/ddl/alter_sequence.sql new file mode 100644 index 00000000..bdaab1ff --- /dev/null +++ b/testdata/input/ddl/alter_sequence.sql @@ -0,0 +1 @@ +ALTER SEQUENCE my_sequence SET OPTIONS (skip_range_min=1, skip_range_max=1234567) diff --git a/testdata/input/ddl/drop_sequence.sql b/testdata/input/ddl/drop_sequence.sql new file mode 100644 index 00000000..52332165 --- /dev/null +++ b/testdata/input/ddl/drop_sequence.sql @@ -0,0 +1 @@ +DROP SEQUENCE my_sequence diff --git a/testdata/input/ddl/drop_sequence_if_exits.sql b/testdata/input/ddl/drop_sequence_if_exits.sql new file mode 100644 index 00000000..fc6962b0 --- /dev/null +++ b/testdata/input/ddl/drop_sequence_if_exits.sql @@ -0,0 +1 @@ +DROP SEQUENCE IF EXISTS my_sequence diff --git a/testdata/result/ddl/alter_sequence.sql.txt b/testdata/result/ddl/alter_sequence.sql.txt new file mode 100644 index 00000000..c8e8305d --- /dev/null +++ b/testdata/result/ddl/alter_sequence.sql.txt @@ -0,0 +1,47 @@ +--- alter_sequence.sql +ALTER SEQUENCE my_sequence SET OPTIONS (skip_range_min=1, skip_range_max=1234567) + +--- AST +&ast.AlterSequence{ + Alter: 0, + Name: &ast.Ident{ + NamePos: 15, + NameEnd: 26, + Name: "my_sequence", + }, + Options: &ast.SequenceOptions{ + Options: 31, + Rparen: 80, + Records: []*ast.SequenceOption{ + &ast.SequenceOption{ + Name: &ast.Ident{ + NamePos: 40, + NameEnd: 54, + Name: "skip_range_min", + }, + Value: &ast.IntLiteral{ + ValuePos: 55, + ValueEnd: 56, + Base: 10, + Value: "1", + }, + }, + &ast.SequenceOption{ + Name: &ast.Ident{ + NamePos: 58, + NameEnd: 72, + Name: "skip_range_max", + }, + Value: &ast.IntLiteral{ + ValuePos: 73, + ValueEnd: 80, + Base: 10, + Value: "1234567", + }, + }, + }, + }, +} + +--- SQL +ALTER SEQUENCE my_sequence SET OPTIONS (skip_range_min = 1, skip_range_max = 1234567) diff --git a/testdata/result/ddl/create_sequance.sql.txt b/testdata/result/ddl/create_sequance.sql.txt index 37c68aa4..9d6a5575 100644 --- a/testdata/result/ddl/create_sequance.sql.txt +++ b/testdata/result/ddl/create_sequance.sql.txt @@ -8,63 +8,66 @@ CREATE SEQUENCE IF NOT EXISTS MySequence OPTIONS ( --- AST &ast.CreateSequence{ Create: 0, - Rparen: 172, Name: &ast.Ident{ NamePos: 30, NameEnd: 40, Name: "MySequence", }, IfNotExists: true, - Options: []*ast.SequenceOption{ - &ast.SequenceOption{ - Name: &ast.Ident{ - NamePos: 55, - NameEnd: 68, - Name: "sequence_kind", + Options: &ast.SequenceOptions{ + Options: 41, + Rparen: 172, + Records: []*ast.SequenceOption{ + &ast.SequenceOption{ + Name: &ast.Ident{ + NamePos: 55, + NameEnd: 68, + Name: "sequence_kind", + }, + Value: &ast.StringLiteral{ + ValuePos: 69, + ValueEnd: 92, + Value: "bit_reversed_positive", + }, }, - Value: &ast.StringLiteral{ - ValuePos: 69, - ValueEnd: 92, - Value: "bit_reversed_positive", + &ast.SequenceOption{ + Name: &ast.Ident{ + NamePos: 98, + NameEnd: 112, + Name: "skip_range_min", + }, + Value: &ast.IntLiteral{ + ValuePos: 115, + ValueEnd: 116, + Base: 10, + Value: "1", + }, }, - }, - &ast.SequenceOption{ - Name: &ast.Ident{ - NamePos: 98, - NameEnd: 112, - Name: "skip_range_min", - }, - Value: &ast.IntLiteral{ - ValuePos: 115, - ValueEnd: 116, - Base: 10, - Value: "1", - }, - }, - &ast.SequenceOption{ - Name: &ast.Ident{ - NamePos: 122, - NameEnd: 136, - Name: "skip_range_max", - }, - Value: &ast.IntLiteral{ - ValuePos: 139, - ValueEnd: 143, - Base: 10, - Value: "1000", - }, - }, - &ast.SequenceOption{ - Name: &ast.Ident{ - NamePos: 149, - NameEnd: 167, - Name: "start_with_counter", + &ast.SequenceOption{ + Name: &ast.Ident{ + NamePos: 122, + NameEnd: 136, + Name: "skip_range_max", + }, + Value: &ast.IntLiteral{ + ValuePos: 139, + ValueEnd: 143, + Base: 10, + Value: "1000", + }, }, - Value: &ast.IntLiteral{ - ValuePos: 170, - ValueEnd: 172, - Base: 10, - Value: "50", + &ast.SequenceOption{ + Name: &ast.Ident{ + NamePos: 149, + NameEnd: 167, + Name: "start_with_counter", + }, + Value: &ast.IntLiteral{ + ValuePos: 170, + ValueEnd: 172, + Base: 10, + Value: "50", + }, }, }, }, diff --git a/testdata/result/ddl/drop_sequence.sql.txt b/testdata/result/ddl/drop_sequence.sql.txt new file mode 100644 index 00000000..5cf3fb92 --- /dev/null +++ b/testdata/result/ddl/drop_sequence.sql.txt @@ -0,0 +1,16 @@ +--- drop_sequence.sql +DROP SEQUENCE my_sequence + +--- AST +&ast.DropSequence{ + Drop: 0, + IfExists: false, + Name: &ast.Ident{ + NamePos: 14, + NameEnd: 25, + Name: "my_sequence", + }, +} + +--- SQL +DROP SEQUENCE my_sequence diff --git a/testdata/result/ddl/drop_sequence_if_exits.sql.txt b/testdata/result/ddl/drop_sequence_if_exits.sql.txt new file mode 100644 index 00000000..8d420cbf --- /dev/null +++ b/testdata/result/ddl/drop_sequence_if_exits.sql.txt @@ -0,0 +1,16 @@ +--- drop_sequence_if_exits.sql +DROP SEQUENCE IF EXISTS my_sequence + +--- AST +&ast.DropSequence{ + Drop: 0, + IfExists: true, + Name: &ast.Ident{ + NamePos: 24, + NameEnd: 35, + Name: "my_sequence", + }, +} + +--- SQL +DROP SEQUENCE IF EXISTS my_sequence diff --git a/testdata/result/statement/alter_sequence.sql.txt b/testdata/result/statement/alter_sequence.sql.txt new file mode 100644 index 00000000..c8e8305d --- /dev/null +++ b/testdata/result/statement/alter_sequence.sql.txt @@ -0,0 +1,47 @@ +--- alter_sequence.sql +ALTER SEQUENCE my_sequence SET OPTIONS (skip_range_min=1, skip_range_max=1234567) + +--- AST +&ast.AlterSequence{ + Alter: 0, + Name: &ast.Ident{ + NamePos: 15, + NameEnd: 26, + Name: "my_sequence", + }, + Options: &ast.SequenceOptions{ + Options: 31, + Rparen: 80, + Records: []*ast.SequenceOption{ + &ast.SequenceOption{ + Name: &ast.Ident{ + NamePos: 40, + NameEnd: 54, + Name: "skip_range_min", + }, + Value: &ast.IntLiteral{ + ValuePos: 55, + ValueEnd: 56, + Base: 10, + Value: "1", + }, + }, + &ast.SequenceOption{ + Name: &ast.Ident{ + NamePos: 58, + NameEnd: 72, + Name: "skip_range_max", + }, + Value: &ast.IntLiteral{ + ValuePos: 73, + ValueEnd: 80, + Base: 10, + Value: "1234567", + }, + }, + }, + }, +} + +--- SQL +ALTER SEQUENCE my_sequence SET OPTIONS (skip_range_min = 1, skip_range_max = 1234567) diff --git a/testdata/result/statement/create_sequance.sql.txt b/testdata/result/statement/create_sequance.sql.txt index 37c68aa4..9d6a5575 100644 --- a/testdata/result/statement/create_sequance.sql.txt +++ b/testdata/result/statement/create_sequance.sql.txt @@ -8,63 +8,66 @@ CREATE SEQUENCE IF NOT EXISTS MySequence OPTIONS ( --- AST &ast.CreateSequence{ Create: 0, - Rparen: 172, Name: &ast.Ident{ NamePos: 30, NameEnd: 40, Name: "MySequence", }, IfNotExists: true, - Options: []*ast.SequenceOption{ - &ast.SequenceOption{ - Name: &ast.Ident{ - NamePos: 55, - NameEnd: 68, - Name: "sequence_kind", + Options: &ast.SequenceOptions{ + Options: 41, + Rparen: 172, + Records: []*ast.SequenceOption{ + &ast.SequenceOption{ + Name: &ast.Ident{ + NamePos: 55, + NameEnd: 68, + Name: "sequence_kind", + }, + Value: &ast.StringLiteral{ + ValuePos: 69, + ValueEnd: 92, + Value: "bit_reversed_positive", + }, }, - Value: &ast.StringLiteral{ - ValuePos: 69, - ValueEnd: 92, - Value: "bit_reversed_positive", + &ast.SequenceOption{ + Name: &ast.Ident{ + NamePos: 98, + NameEnd: 112, + Name: "skip_range_min", + }, + Value: &ast.IntLiteral{ + ValuePos: 115, + ValueEnd: 116, + Base: 10, + Value: "1", + }, }, - }, - &ast.SequenceOption{ - Name: &ast.Ident{ - NamePos: 98, - NameEnd: 112, - Name: "skip_range_min", - }, - Value: &ast.IntLiteral{ - ValuePos: 115, - ValueEnd: 116, - Base: 10, - Value: "1", - }, - }, - &ast.SequenceOption{ - Name: &ast.Ident{ - NamePos: 122, - NameEnd: 136, - Name: "skip_range_max", - }, - Value: &ast.IntLiteral{ - ValuePos: 139, - ValueEnd: 143, - Base: 10, - Value: "1000", - }, - }, - &ast.SequenceOption{ - Name: &ast.Ident{ - NamePos: 149, - NameEnd: 167, - Name: "start_with_counter", + &ast.SequenceOption{ + Name: &ast.Ident{ + NamePos: 122, + NameEnd: 136, + Name: "skip_range_max", + }, + Value: &ast.IntLiteral{ + ValuePos: 139, + ValueEnd: 143, + Base: 10, + Value: "1000", + }, }, - Value: &ast.IntLiteral{ - ValuePos: 170, - ValueEnd: 172, - Base: 10, - Value: "50", + &ast.SequenceOption{ + Name: &ast.Ident{ + NamePos: 149, + NameEnd: 167, + Name: "start_with_counter", + }, + Value: &ast.IntLiteral{ + ValuePos: 170, + ValueEnd: 172, + Base: 10, + Value: "50", + }, }, }, }, diff --git a/testdata/result/statement/drop_sequence.sql.txt b/testdata/result/statement/drop_sequence.sql.txt new file mode 100644 index 00000000..5cf3fb92 --- /dev/null +++ b/testdata/result/statement/drop_sequence.sql.txt @@ -0,0 +1,16 @@ +--- drop_sequence.sql +DROP SEQUENCE my_sequence + +--- AST +&ast.DropSequence{ + Drop: 0, + IfExists: false, + Name: &ast.Ident{ + NamePos: 14, + NameEnd: 25, + Name: "my_sequence", + }, +} + +--- SQL +DROP SEQUENCE my_sequence diff --git a/testdata/result/statement/drop_sequence_if_exits.sql.txt b/testdata/result/statement/drop_sequence_if_exits.sql.txt new file mode 100644 index 00000000..8d420cbf --- /dev/null +++ b/testdata/result/statement/drop_sequence_if_exits.sql.txt @@ -0,0 +1,16 @@ +--- drop_sequence_if_exits.sql +DROP SEQUENCE IF EXISTS my_sequence + +--- AST +&ast.DropSequence{ + Drop: 0, + IfExists: true, + Name: &ast.Ident{ + NamePos: 24, + NameEnd: 35, + Name: "my_sequence", + }, +} + +--- SQL +DROP SEQUENCE IF EXISTS my_sequence