Skip to content

Commit 66b4164

Browse files
committed
Add ALTER DATABASE SET termination clause and cursor options support
Add parsing for: - Termination clause (WITH NO_WAIT, WITH ROLLBACK AFTER N, WITH ROLLBACK IMMEDIATE) - CURSOR_CLOSE_ON_COMMIT ON/OFF option - CURSOR_DEFAULT LOCAL/GLOBAL option - RecoveryDatabaseOption for RECOVERY FULL/BULK_LOGGED/SIMPLE - Fix capitalization for ArithAbort and NumericRoundAbort option kinds
1 parent 8ac6d90 commit 66b4164

5 files changed

Lines changed: 154 additions & 10 deletions

File tree

ast/alter_database_set_statement.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,18 @@ type AlterDatabaseSetStatement struct {
66
UseCurrent bool
77
WithManualCutover bool
88
Options []DatabaseOption
9+
Termination *AlterDatabaseTermination
910
}
1011

12+
// AlterDatabaseTermination represents the termination clause (WITH NO_WAIT, WITH ROLLBACK AFTER N, WITH ROLLBACK IMMEDIATE)
13+
type AlterDatabaseTermination struct {
14+
NoWait bool
15+
ImmediateRollback bool
16+
RollbackAfter ScalarExpression
17+
}
18+
19+
func (a *AlterDatabaseTermination) node() {}
20+
1121
func (a *AlterDatabaseSetStatement) node() {}
1222
func (a *AlterDatabaseSetStatement) statement() {}
1323

@@ -82,6 +92,7 @@ type SimpleDatabaseOption struct {
8292

8393
func (d *SimpleDatabaseOption) node() {}
8494
func (d *SimpleDatabaseOption) createDatabaseOption() {}
95+
func (d *SimpleDatabaseOption) databaseOption() {}
8596

8697
// MaxSizeDatabaseOption represents a MAXSIZE option.
8798
type MaxSizeDatabaseOption struct {
@@ -279,3 +290,21 @@ type ChangeRetentionChangeTrackingOptionDetail struct {
279290

280291
func (c *ChangeRetentionChangeTrackingOptionDetail) node() {}
281292
func (c *ChangeRetentionChangeTrackingOptionDetail) changeTrackingOptionDetail() {}
293+
294+
// RecoveryDatabaseOption represents RECOVERY database option
295+
type RecoveryDatabaseOption struct {
296+
OptionKind string // "Recovery"
297+
Value string // "Full", "BulkLogged", "Simple"
298+
}
299+
300+
func (r *RecoveryDatabaseOption) node() {}
301+
func (r *RecoveryDatabaseOption) databaseOption() {}
302+
303+
// CursorDefaultDatabaseOption represents CURSOR_DEFAULT database option
304+
type CursorDefaultDatabaseOption struct {
305+
OptionKind string // "CursorDefault"
306+
IsLocal bool // true for LOCAL, false for GLOBAL
307+
}
308+
309+
func (c *CursorDefaultDatabaseOption) node() {}
310+
func (c *CursorDefaultDatabaseOption) databaseOption() {}

parser/marshal.go

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,12 +1001,18 @@ func alterCredentialStatementToJSON(s *ast.AlterCredentialStatement) jsonNode {
10011001

10021002
func alterDatabaseSetStatementToJSON(s *ast.AlterDatabaseSetStatement) jsonNode {
10031003
node := jsonNode{
1004-
"$type": "AlterDatabaseSetStatement",
1005-
"WithManualCutover": s.WithManualCutover,
1006-
"UseCurrent": s.UseCurrent,
1004+
"$type": "AlterDatabaseSetStatement",
10071005
}
1008-
if s.DatabaseName != nil {
1009-
node["DatabaseName"] = identifierToJSON(s.DatabaseName)
1006+
if s.Termination != nil {
1007+
termNode := jsonNode{
1008+
"$type": "AlterDatabaseTermination",
1009+
"ImmediateRollback": s.Termination.ImmediateRollback,
1010+
"NoWait": s.Termination.NoWait,
1011+
}
1012+
if s.Termination.RollbackAfter != nil {
1013+
termNode["RollbackAfter"] = scalarExpressionToJSON(s.Termination.RollbackAfter)
1014+
}
1015+
node["Termination"] = termNode
10101016
}
10111017
if len(s.Options) > 0 {
10121018
opts := make([]jsonNode, len(s.Options))
@@ -1015,6 +1021,11 @@ func alterDatabaseSetStatementToJSON(s *ast.AlterDatabaseSetStatement) jsonNode
10151021
}
10161022
node["Options"] = opts
10171023
}
1024+
node["WithManualCutover"] = s.WithManualCutover
1025+
if s.DatabaseName != nil {
1026+
node["DatabaseName"] = identifierToJSON(s.DatabaseName)
1027+
}
1028+
node["UseCurrent"] = s.UseCurrent
10181029
return node
10191030
}
10201031

@@ -1040,11 +1051,13 @@ func databaseOptionToJSON(opt ast.DatabaseOption) jsonNode {
10401051
}
10411052
case *ast.AutoCreateStatisticsDatabaseOption:
10421053
node := jsonNode{
1043-
"$type": "AutoCreateStatisticsDatabaseOption",
1054+
"$type": "AutoCreateStatisticsDatabaseOption",
1055+
"HasIncremental": o.HasIncremental,
10441056
}
1045-
if o.HasIncremental {
1046-
node["HasIncremental"] = o.HasIncremental
1057+
if o.IncrementalState != "" {
10471058
node["IncrementalState"] = o.IncrementalState
1059+
} else {
1060+
node["IncrementalState"] = "NotSet"
10481061
}
10491062
node["OptionState"] = o.OptionState
10501063
node["OptionKind"] = o.OptionKind
@@ -1102,6 +1115,23 @@ func databaseOptionToJSON(opt ast.DatabaseOption) jsonNode {
11021115
node["Details"] = details
11031116
}
11041117
return node
1118+
case *ast.RecoveryDatabaseOption:
1119+
return jsonNode{
1120+
"$type": "RecoveryDatabaseOption",
1121+
"Value": o.Value,
1122+
"OptionKind": o.OptionKind,
1123+
}
1124+
case *ast.CursorDefaultDatabaseOption:
1125+
return jsonNode{
1126+
"$type": "CursorDefaultDatabaseOption",
1127+
"IsLocal": o.IsLocal,
1128+
"OptionKind": o.OptionKind,
1129+
}
1130+
case *ast.SimpleDatabaseOption:
1131+
return jsonNode{
1132+
"$type": "DatabaseOption",
1133+
"OptionKind": o.OptionKind,
1134+
}
11051135
default:
11061136
return jsonNode{"$type": "UnknownDatabaseOption"}
11071137
}

parser/parse_ddl.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1951,6 +1951,56 @@ func (p *Parser) parseAlterDatabaseSetStatement(dbName *ast.Identifier) (*ast.Al
19511951
p.nextToken()
19521952

19531953
switch optionName {
1954+
// Simple database options without ON/OFF
1955+
case "ONLINE", "OFFLINE":
1956+
opt := &ast.SimpleDatabaseOption{OptionKind: capitalizeFirst(strings.ToLower(optionName))}
1957+
stmt.Options = append(stmt.Options, opt)
1958+
case "SINGLE_USER":
1959+
opt := &ast.SimpleDatabaseOption{OptionKind: "SingleUser"}
1960+
stmt.Options = append(stmt.Options, opt)
1961+
case "RESTRICTED_USER":
1962+
opt := &ast.SimpleDatabaseOption{OptionKind: "RestrictedUser"}
1963+
stmt.Options = append(stmt.Options, opt)
1964+
case "MULTI_USER":
1965+
opt := &ast.SimpleDatabaseOption{OptionKind: "MultiUser"}
1966+
stmt.Options = append(stmt.Options, opt)
1967+
case "READ_ONLY":
1968+
opt := &ast.SimpleDatabaseOption{OptionKind: "ReadOnly"}
1969+
stmt.Options = append(stmt.Options, opt)
1970+
case "READ_WRITE":
1971+
opt := &ast.SimpleDatabaseOption{OptionKind: "ReadWrite"}
1972+
stmt.Options = append(stmt.Options, opt)
1973+
case "RECOVERY":
1974+
// Expect FULL, BULK_LOGGED, or SIMPLE
1975+
recoveryType := strings.ToUpper(p.curTok.Literal)
1976+
p.nextToken()
1977+
recoveryValue := "Full"
1978+
switch recoveryType {
1979+
case "BULK_LOGGED":
1980+
recoveryValue = "BulkLogged"
1981+
case "SIMPLE":
1982+
recoveryValue = "Simple"
1983+
}
1984+
opt := &ast.RecoveryDatabaseOption{OptionKind: "Recovery", Value: recoveryValue}
1985+
stmt.Options = append(stmt.Options, opt)
1986+
case "CURSOR_CLOSE_ON_COMMIT":
1987+
// Expects ON/OFF
1988+
optionValue := strings.ToUpper(p.curTok.Literal)
1989+
p.nextToken()
1990+
opt := &ast.OnOffDatabaseOption{
1991+
OptionKind: "CursorCloseOnCommit",
1992+
OptionState: capitalizeFirst(optionValue),
1993+
}
1994+
stmt.Options = append(stmt.Options, opt)
1995+
case "CURSOR_DEFAULT":
1996+
// Expects LOCAL or GLOBAL
1997+
cursorValue := strings.ToUpper(p.curTok.Literal)
1998+
p.nextToken()
1999+
opt := &ast.CursorDefaultDatabaseOption{
2000+
OptionKind: "CursorDefault",
2001+
IsLocal: cursorValue == "LOCAL",
2002+
}
2003+
stmt.Options = append(stmt.Options, opt)
19542004
case "ACCELERATED_DATABASE_RECOVERY":
19552005
// Expect = for this option
19562006
if p.curTok.Type != TokenEquals {
@@ -2079,6 +2129,37 @@ func (p *Parser) parseAlterDatabaseSetStatement(dbName *ast.Identifier) (*ast.Al
20792129
}
20802130
}
20812131

2132+
// Parse optional termination clause: WITH NO_WAIT | WITH ROLLBACK AFTER N [SECONDS] | WITH ROLLBACK IMMEDIATE
2133+
if p.curTok.Type == TokenWith || strings.ToUpper(p.curTok.Literal) == "WITH" {
2134+
p.nextToken() // consume WITH
2135+
term := &ast.AlterDatabaseTermination{}
2136+
termKeyword := strings.ToUpper(p.curTok.Literal)
2137+
if termKeyword == "NO_WAIT" {
2138+
term.NoWait = true
2139+
p.nextToken()
2140+
} else if termKeyword == "ROLLBACK" {
2141+
p.nextToken() // consume ROLLBACK
2142+
rollbackType := strings.ToUpper(p.curTok.Literal)
2143+
if rollbackType == "AFTER" {
2144+
p.nextToken() // consume AFTER
2145+
// Parse the number
2146+
val, err := p.parseScalarExpression()
2147+
if err != nil {
2148+
return nil, err
2149+
}
2150+
term.RollbackAfter = val
2151+
// Optional SECONDS keyword
2152+
if strings.ToUpper(p.curTok.Literal) == "SECONDS" {
2153+
p.nextToken()
2154+
}
2155+
} else if rollbackType == "IMMEDIATE" {
2156+
term.ImmediateRollback = true
2157+
p.nextToken()
2158+
}
2159+
}
2160+
stmt.Termination = term
2161+
}
2162+
20822163
// Skip optional semicolon
20832164
if p.curTok.Type == TokenSemicolon {
20842165
p.nextToken()
@@ -7407,6 +7488,10 @@ func convertOptionKind(optionName string) string {
74077488
switch optionName {
74087489
case "VARDECIMAL_STORAGE_FORMAT":
74097490
return "VarDecimalStorageFormat"
7491+
case "ARITHABORT":
7492+
return "ArithAbort"
7493+
case "NUMERIC_ROUNDABORT":
7494+
return "NumericRoundAbort"
74107495
}
74117496

74127497
// Split by underscores and capitalize each word
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"todo": true}
1+
{}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"todo": true}
1+
{}

0 commit comments

Comments
 (0)