Skip to content

Commit 34564e1

Browse files
committed
Add full ALTER PROCEDURE and BEGIN ATOMIC block support
- Add Options field to AlterProcedureStatement for WITH clause - Add NATIVE_COMPILATION and SCHEMABINDING procedure option parsing - Fix BEGIN ATOMIC block to handle multi-word isolation levels - Add OnOffAtomicBlockOption for DELAYED_DURABILITY - Fix DATEFIRST (IntegerLiteral) and DATEFORMAT (StringLiteral) types
1 parent b159310 commit 34564e1

9 files changed

Lines changed: 142 additions & 14 deletions

File tree

ast/alter_procedure_statement.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package ast
44
type AlterProcedureStatement struct {
55
ProcedureReference *ProcedureReference
66
Parameters []*ProcedureParameter
7+
Options []ProcedureOptionBase
78
StatementList *StatementList
89
IsForReplication bool
910
}

ast/begin_end_block_statement.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@ type LiteralAtomicBlockOption struct {
3838

3939
func (o *LiteralAtomicBlockOption) atomicBlockOption() {}
4040

41+
// OnOffAtomicBlockOption represents an atomic block option with an ON/OFF value.
42+
type OnOffAtomicBlockOption struct {
43+
OptionKind string
44+
OptionState string // "On" or "Off"
45+
}
46+
47+
func (o *OnOffAtomicBlockOption) atomicBlockOption() {}
48+
4149
// StatementList is a list of statements.
4250
type StatementList struct {
4351
Statements []Statement `json:"Statements,omitempty"`

parser/marshal.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3683,6 +3683,12 @@ func atomicBlockOptionToJSON(o ast.AtomicBlockOption) jsonNode {
36833683
node["Value"] = scalarExpressionToJSON(opt.Value)
36843684
}
36853685
return node
3686+
case *ast.OnOffAtomicBlockOption:
3687+
return jsonNode{
3688+
"$type": "OnOffAtomicBlockOption",
3689+
"OptionState": opt.OptionState,
3690+
"OptionKind": opt.OptionKind,
3691+
}
36863692
default:
36873693
return jsonNode{"$type": "UnknownAtomicBlockOption"}
36883694
}
@@ -16547,6 +16553,13 @@ func alterProcedureStatementToJSON(s *ast.AlterProcedureStatement) jsonNode {
1654716553
node["ProcedureReference"] = procedureReferenceToJSON(s.ProcedureReference)
1654816554
}
1654916555
node["IsForReplication"] = s.IsForReplication
16556+
if len(s.Options) > 0 {
16557+
opts := make([]jsonNode, len(s.Options))
16558+
for i, o := range s.Options {
16559+
opts[i] = procedureOptionToJSON(o)
16560+
}
16561+
node["Options"] = opts
16562+
}
1655016563
if len(s.Parameters) > 0 {
1655116564
params := make([]jsonNode, len(s.Parameters))
1655216565
for i, p := range s.Parameters {

parser/parse_ddl.go

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7310,20 +7310,70 @@ func (p *Parser) parseAlterProcedureStatement() (*ast.AlterProcedureStatement, e
73107310
stmt.Parameters = params
73117311
}
73127312

7313-
// Skip WITH options (like RECOMPILE, ENCRYPTION, etc.)
7313+
// Parse WITH options (like RECOMPILE, ENCRYPTION, NATIVE_COMPILATION, etc.)
73147314
if p.curTok.Type == TokenWith {
73157315
p.nextToken()
73167316
for {
73177317
if strings.ToUpper(p.curTok.Literal) == "FOR" || p.curTok.Type == TokenAs || p.curTok.Type == TokenEOF {
73187318
break
73197319
}
7320-
if strings.ToUpper(p.curTok.Literal) == "REPLICATION" {
7320+
upperLit := strings.ToUpper(p.curTok.Literal)
7321+
if upperLit == "RECOMPILE" {
7322+
stmt.Options = append(stmt.Options, &ast.ProcedureOption{OptionKind: "Recompile"})
7323+
p.nextToken()
7324+
} else if upperLit == "ENCRYPTION" {
7325+
stmt.Options = append(stmt.Options, &ast.ProcedureOption{OptionKind: "Encryption"})
7326+
p.nextToken()
7327+
} else if upperLit == "NATIVE_COMPILATION" {
7328+
stmt.Options = append(stmt.Options, &ast.ProcedureOption{OptionKind: "NativeCompilation"})
7329+
p.nextToken()
7330+
} else if upperLit == "SCHEMABINDING" {
7331+
stmt.Options = append(stmt.Options, &ast.ProcedureOption{OptionKind: "SchemaBinding"})
7332+
p.nextToken()
7333+
} else if upperLit == "EXECUTE" {
7334+
p.nextToken() // consume EXECUTE
7335+
if p.curTok.Type == TokenAs {
7336+
p.nextToken() // consume AS
7337+
}
7338+
executeAsOpt := &ast.ExecuteAsProcedureOption{
7339+
OptionKind: "ExecuteAs",
7340+
ExecuteAs: &ast.ExecuteAsClause{},
7341+
}
7342+
upperOption := strings.ToUpper(p.curTok.Literal)
7343+
if upperOption == "CALLER" {
7344+
executeAsOpt.ExecuteAs.ExecuteAsOption = "Caller"
7345+
p.nextToken()
7346+
} else if upperOption == "SELF" {
7347+
executeAsOpt.ExecuteAs.ExecuteAsOption = "Self"
7348+
p.nextToken()
7349+
} else if upperOption == "OWNER" {
7350+
executeAsOpt.ExecuteAs.ExecuteAsOption = "Owner"
7351+
p.nextToken()
7352+
} else if p.curTok.Type == TokenString {
7353+
executeAsOpt.ExecuteAs.ExecuteAsOption = "String"
7354+
value := p.curTok.Literal
7355+
// Strip quotes
7356+
if len(value) >= 2 && value[0] == '\'' && value[len(value)-1] == '\'' {
7357+
value = value[1 : len(value)-1]
7358+
}
7359+
executeAsOpt.ExecuteAs.Literal = &ast.StringLiteral{
7360+
LiteralType: "String",
7361+
IsNational: false,
7362+
IsLargeObject: false,
7363+
Value: value,
7364+
}
7365+
p.nextToken()
7366+
}
7367+
stmt.Options = append(stmt.Options, executeAsOpt)
7368+
} else if upperLit == "REPLICATION" {
73217369
stmt.IsForReplication = true
7370+
p.nextToken()
7371+
} else {
7372+
p.nextToken()
73227373
}
7323-
p.nextToken()
73247374
if p.curTok.Type == TokenComma {
73257375
p.nextToken()
7326-
} else {
7376+
} else if p.curTok.Type == TokenAs || strings.ToUpper(p.curTok.Literal) == "FOR" || p.curTok.Type == TokenEOF {
73277377
break
73287378
}
73297379
}

parser/parse_statements.go

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1933,10 +1933,22 @@ func (p *Parser) parseBeginAtomicBlockStatement() (*ast.BeginEndAtomicBlockState
19331933
p.nextToken() // consume =
19341934
}
19351935
}
1936-
// Parse the isolation level identifier
1936+
// Parse the isolation level identifier - may be multi-word like "READ COMMITTED"
1937+
levelValue := strings.ToUpper(p.curTok.Literal)
1938+
p.nextToken()
1939+
// Check for two-word isolation levels
1940+
nextWord := strings.ToUpper(p.curTok.Literal)
1941+
if (levelValue == "READ" && (nextWord == "COMMITTED" || nextWord == "UNCOMMITTED")) ||
1942+
(levelValue == "REPEATABLE" && nextWord == "READ") {
1943+
levelValue = levelValue + " " + nextWord
1944+
p.nextToken()
1945+
}
19371946
opt := &ast.IdentifierAtomicBlockOption{
19381947
OptionKind: "IsolationLevel",
1939-
Value: p.parseIdentifier(),
1948+
Value: &ast.Identifier{
1949+
Value: levelValue,
1950+
QuoteType: "NotQuoted",
1951+
},
19401952
}
19411953
stmt.Options = append(stmt.Options, opt)
19421954
case "LANGUAGE":
@@ -1970,10 +1982,48 @@ func (p *Parser) parseBeginAtomicBlockStatement() (*ast.BeginEndAtomicBlockState
19701982
}
19711983
stmt.Options = append(stmt.Options, opt)
19721984
}
1973-
case "DATEFIRST", "DATEFORMAT":
1974-
opt := &ast.IdentifierAtomicBlockOption{
1975-
OptionKind: optName,
1976-
Value: p.parseIdentifier(),
1985+
case "DATEFIRST":
1986+
// Parse as integer literal
1987+
intLit := &ast.IntegerLiteral{
1988+
LiteralType: "Integer",
1989+
Value: p.curTok.Literal,
1990+
}
1991+
p.nextToken()
1992+
opt := &ast.LiteralAtomicBlockOption{
1993+
OptionKind: "DateFirst",
1994+
Value: intLit,
1995+
}
1996+
stmt.Options = append(stmt.Options, opt)
1997+
case "DATEFORMAT":
1998+
// Parse as string literal
1999+
value := p.curTok.Literal
2000+
// Strip quotes if present
2001+
if len(value) >= 2 && value[0] == '\'' && value[len(value)-1] == '\'' {
2002+
value = value[1 : len(value)-1]
2003+
}
2004+
strLit := &ast.StringLiteral{
2005+
LiteralType: "String",
2006+
Value: value,
2007+
IsNational: false,
2008+
IsLargeObject: false,
2009+
}
2010+
p.nextToken()
2011+
opt := &ast.LiteralAtomicBlockOption{
2012+
OptionKind: "DateFormat",
2013+
Value: strLit,
2014+
}
2015+
stmt.Options = append(stmt.Options, opt)
2016+
case "DELAYED_DURABILITY":
2017+
// Parse ON/OFF as OnOffAtomicBlockOption
2018+
stateUpper := strings.ToUpper(p.curTok.Literal)
2019+
optState := "Off"
2020+
if stateUpper == "ON" {
2021+
optState = "On"
2022+
}
2023+
p.nextToken()
2024+
opt := &ast.OnOffAtomicBlockOption{
2025+
OptionKind: "DelayedDurability",
2026+
OptionState: optState,
19772027
}
19782028
stmt.Options = append(stmt.Options, opt)
19792029
default:
@@ -4190,6 +4240,12 @@ func (p *Parser) parseCreateProcedureStatement() (*ast.CreateProcedureStatement,
41904240
} else if upperLit == "ENCRYPTION" {
41914241
stmt.Options = append(stmt.Options, &ast.ProcedureOption{OptionKind: "Encryption"})
41924242
p.nextToken()
4243+
} else if upperLit == "NATIVE_COMPILATION" {
4244+
stmt.Options = append(stmt.Options, &ast.ProcedureOption{OptionKind: "NativeCompilation"})
4245+
p.nextToken()
4246+
} else if upperLit == "SCHEMABINDING" {
4247+
stmt.Options = append(stmt.Options, &ast.ProcedureOption{OptionKind: "SchemaBinding"})
4248+
p.nextToken()
41934249
} else if upperLit == "EXECUTE" {
41944250
p.nextToken() // consume EXECUTE
41954251
if p.curTok.Type == TokenAs {
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+
{}
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)