Skip to content

Commit 6635435

Browse files
kyleconroyclaude
andcommitted
Add Event Session statement support
- Add ALTER EVENT SESSION parsing with ADD/DROP EVENT/TARGET, WITH, STATE options - Add DROP EVENT SESSION statement parsing - Add BooleanNotExpression for NOT predicate handling - Add comparison operators (=, !=, <>, <, >, <=, >=) to event predicates - Add EventDeclarationSetParameters to EventDeclaration - Add DropEventDeclarations and DropTargetDeclarations to AlterEventSessionStatement - Add memoryPartitionValue mapping for PER_CPU -> PerCpu - Fix loop termination in ALTER EVENT SESSION to not exit on DROP/ADD tokens This enables 4 tests: - Baselines100_EventSessionStatementTests - Baselines130_EventSessionDbScopeStatementTests - EventSessionDbScopeStatementTests - EventSessionStatementTests Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 81b0b38 commit 6635435

File tree

9 files changed

+389
-8
lines changed

9 files changed

+389
-8
lines changed

ast/boolean_not_expression.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package ast
2+
3+
// BooleanNotExpression represents a NOT expression
4+
type BooleanNotExpression struct {
5+
Expression BooleanExpression
6+
}
7+
8+
func (e *BooleanNotExpression) node() {}
9+
func (e *BooleanNotExpression) booleanExpression() {}

ast/event_statements.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,35 @@ type CreateEventSessionStatement struct {
1212
func (s *CreateEventSessionStatement) node() {}
1313
func (s *CreateEventSessionStatement) statement() {}
1414

15+
// AlterEventSessionStatement represents ALTER EVENT SESSION statement
16+
type AlterEventSessionStatement struct {
17+
Name *Identifier
18+
SessionScope string // "Server" or "Database"
19+
StatementType string // "AddEventDeclarationOptionalSessionOptions", "DropEventSpecificationOptionalSessionOptions", "AddTargetDeclarationOptionalSessionOptions", "DropTargetSpecificationOptionalSessionOptions", "RequiredSessionOptions", "AlterStateIsStart", "AlterStateIsStop"
20+
EventDeclarations []*EventDeclaration
21+
DropEventDeclarations []*EventSessionObjectName
22+
TargetDeclarations []*TargetDeclaration
23+
DropTargetDeclarations []*EventSessionObjectName
24+
SessionOptions []SessionOption
25+
}
26+
27+
func (s *AlterEventSessionStatement) node() {}
28+
func (s *AlterEventSessionStatement) statement() {}
29+
30+
// DropEventSessionStatement represents DROP EVENT SESSION statement
31+
type DropEventSessionStatement struct {
32+
Name *Identifier
33+
SessionScope string // "Server" or "Database"
34+
IsIfExists bool
35+
}
36+
37+
func (s *DropEventSessionStatement) node() {}
38+
func (s *DropEventSessionStatement) statement() {}
39+
1540
// EventDeclaration represents an event in the event session
1641
type EventDeclaration struct {
1742
ObjectName *EventSessionObjectName
43+
EventDeclarationSetParameters []*EventDeclarationSetParameter
1844
EventDeclarationActionParameters []*EventSessionObjectName
1945
EventDeclarationPredicateParameter BooleanExpression
2046
}

parser/marshal.go

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,6 +573,8 @@ func statementToJSON(stmt ast.Statement) jsonNode {
573573
return dropServiceStatementToJSON(s)
574574
case *ast.DropEventNotificationStatement:
575575
return dropEventNotificationStatementToJSON(s)
576+
case *ast.DropEventSessionStatement:
577+
return dropEventSessionStatementToJSON(s)
576578
case *ast.AlterTableTriggerModificationStatement:
577579
return alterTableTriggerModificationStatementToJSON(s)
578580
case *ast.AlterTableFileTableNamespaceStatement:
@@ -601,6 +603,8 @@ func statementToJSON(stmt ast.Statement) jsonNode {
601603
return alterAssemblyStatementToJSON(s)
602604
case *ast.AlterEndpointStatement:
603605
return alterEndpointStatementToJSON(s)
606+
case *ast.AlterEventSessionStatement:
607+
return alterEventSessionStatementToJSON(s)
604608
case *ast.AlterServiceStatement:
605609
return alterServiceStatementToJSON(s)
606610
case *ast.AlterCertificateStatement:
@@ -3389,6 +3393,14 @@ func booleanExpressionToJSON(expr ast.BooleanExpression) jsonNode {
33893393
node["Expression"] = booleanExpressionToJSON(e.Expression)
33903394
}
33913395
return node
3396+
case *ast.BooleanNotExpression:
3397+
node := jsonNode{
3398+
"$type": "BooleanNotExpression",
3399+
}
3400+
if e.Expression != nil {
3401+
node["Expression"] = booleanExpressionToJSON(e.Expression)
3402+
}
3403+
return node
33923404
case *ast.BooleanIsNullExpression:
33933405
node := jsonNode{
33943406
"$type": "BooleanIsNullExpression",
@@ -15909,6 +15921,20 @@ func dropEventNotificationStatementToJSON(s *ast.DropEventNotificationStatement)
1590915921
return node
1591015922
}
1591115923

15924+
func dropEventSessionStatementToJSON(s *ast.DropEventSessionStatement) jsonNode {
15925+
node := jsonNode{
15926+
"$type": "DropEventSessionStatement",
15927+
"IsIfExists": s.IsIfExists,
15928+
}
15929+
if s.Name != nil {
15930+
node["Name"] = identifierToJSON(s.Name)
15931+
}
15932+
if s.SessionScope != "" {
15933+
node["SessionScope"] = s.SessionScope
15934+
}
15935+
return node
15936+
}
15937+
1591215938
func dropSecurityPolicyStatementToJSON(s *ast.DropSecurityPolicyStatement) jsonNode {
1591315939
node := jsonNode{
1591415940
"$type": "DropSecurityPolicyStatement",
@@ -16894,13 +16920,73 @@ func createEventSessionStatementToJSON(s *ast.CreateEventSessionStatement) jsonN
1689416920
return node
1689516921
}
1689616922

16923+
func alterEventSessionStatementToJSON(s *ast.AlterEventSessionStatement) jsonNode {
16924+
node := jsonNode{
16925+
"$type": "AlterEventSessionStatement",
16926+
}
16927+
if s.StatementType != "" {
16928+
node["StatementType"] = s.StatementType
16929+
}
16930+
// DropEventDeclarations comes before Name in JSON
16931+
if len(s.DropEventDeclarations) > 0 {
16932+
events := make([]jsonNode, len(s.DropEventDeclarations))
16933+
for i, e := range s.DropEventDeclarations {
16934+
events[i] = eventSessionObjectNameToJSON(e)
16935+
}
16936+
node["DropEventDeclarations"] = events
16937+
}
16938+
// DropTargetDeclarations comes before Name in JSON
16939+
if len(s.DropTargetDeclarations) > 0 {
16940+
targets := make([]jsonNode, len(s.DropTargetDeclarations))
16941+
for i, t := range s.DropTargetDeclarations {
16942+
targets[i] = eventSessionObjectNameToJSON(t)
16943+
}
16944+
node["DropTargetDeclarations"] = targets
16945+
}
16946+
if s.Name != nil {
16947+
node["Name"] = identifierToJSON(s.Name)
16948+
}
16949+
if s.SessionScope != "" {
16950+
node["SessionScope"] = s.SessionScope
16951+
}
16952+
if len(s.EventDeclarations) > 0 {
16953+
events := make([]jsonNode, len(s.EventDeclarations))
16954+
for i, e := range s.EventDeclarations {
16955+
events[i] = eventDeclarationToJSON(e)
16956+
}
16957+
node["EventDeclarations"] = events
16958+
}
16959+
if len(s.TargetDeclarations) > 0 {
16960+
targets := make([]jsonNode, len(s.TargetDeclarations))
16961+
for i, t := range s.TargetDeclarations {
16962+
targets[i] = targetDeclarationToJSON(t)
16963+
}
16964+
node["TargetDeclarations"] = targets
16965+
}
16966+
if len(s.SessionOptions) > 0 {
16967+
opts := make([]jsonNode, len(s.SessionOptions))
16968+
for i, o := range s.SessionOptions {
16969+
opts[i] = sessionOptionToJSON(o)
16970+
}
16971+
node["SessionOptions"] = opts
16972+
}
16973+
return node
16974+
}
16975+
1689716976
func eventDeclarationToJSON(e *ast.EventDeclaration) jsonNode {
1689816977
node := jsonNode{
1689916978
"$type": "EventDeclaration",
1690016979
}
1690116980
if e.ObjectName != nil {
1690216981
node["ObjectName"] = eventSessionObjectNameToJSON(e.ObjectName)
1690316982
}
16983+
if len(e.EventDeclarationSetParameters) > 0 {
16984+
params := make([]jsonNode, len(e.EventDeclarationSetParameters))
16985+
for i, p := range e.EventDeclarationSetParameters {
16986+
params[i] = eventDeclarationSetParameterToJSON(p)
16987+
}
16988+
node["EventDeclarationSetParameters"] = params
16989+
}
1690416990
if len(e.EventDeclarationActionParameters) > 0 {
1690516991
actions := make([]jsonNode, len(e.EventDeclarationActionParameters))
1690616992
for i, a := range e.EventDeclarationActionParameters {

parser/parse_ddl.go

Lines changed: 170 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2056,9 +2056,13 @@ func (p *Parser) parseDropServiceStatement() (*ast.DropServiceStatement, error)
20562056
return stmt, nil
20572057
}
20582058

2059-
func (p *Parser) parseDropEventNotificationStatement() (*ast.DropEventNotificationStatement, error) {
2059+
func (p *Parser) parseDropEventNotificationStatement() (ast.Statement, error) {
20602060
// Consume EVENT
20612061
p.nextToken()
2062+
// Check if this is DROP EVENT SESSION or DROP EVENT NOTIFICATION
2063+
if strings.ToUpper(p.curTok.Literal) == "SESSION" {
2064+
return p.parseDropEventSessionStatement()
2065+
}
20622066
// Consume NOTIFICATION
20632067
if strings.ToUpper(p.curTok.Literal) == "NOTIFICATION" {
20642068
p.nextToken()
@@ -2107,6 +2111,44 @@ func (p *Parser) parseDropEventNotificationStatement() (*ast.DropEventNotificati
21072111
return stmt, nil
21082112
}
21092113

2114+
func (p *Parser) parseDropEventSessionStatement() (*ast.DropEventSessionStatement, error) {
2115+
// Consume SESSION
2116+
p.nextToken()
2117+
2118+
stmt := &ast.DropEventSessionStatement{}
2119+
2120+
// Check for IF EXISTS
2121+
if strings.ToUpper(p.curTok.Literal) == "IF" {
2122+
p.nextToken()
2123+
if strings.ToUpper(p.curTok.Literal) == "EXISTS" {
2124+
stmt.IsIfExists = true
2125+
p.nextToken()
2126+
}
2127+
}
2128+
2129+
// Parse session name
2130+
stmt.Name = p.parseIdentifier()
2131+
2132+
// ON SERVER/DATABASE
2133+
if p.curTok.Type == TokenOn {
2134+
p.nextToken()
2135+
scopeUpper := strings.ToUpper(p.curTok.Literal)
2136+
if scopeUpper == "SERVER" {
2137+
stmt.SessionScope = "Server"
2138+
p.nextToken()
2139+
} else if scopeUpper == "DATABASE" {
2140+
stmt.SessionScope = "Database"
2141+
p.nextToken()
2142+
}
2143+
}
2144+
2145+
if p.curTok.Type == TokenSemicolon {
2146+
p.nextToken()
2147+
}
2148+
2149+
return stmt, nil
2150+
}
2151+
21102152
func (p *Parser) parseAlterStatement() (ast.Statement, error) {
21112153
// Consume ALTER
21122154
p.nextToken()
@@ -2204,6 +2246,8 @@ func (p *Parser) parseAlterStatement() (ast.Statement, error) {
22042246
return p.parseAlterAvailabilityGroupStatement()
22052247
case "MATERIALIZED":
22062248
return p.parseAlterMaterializedViewStatement()
2249+
case "EVENT":
2250+
return p.parseAlterEventSessionStatement()
22072251
}
22082252
return nil, fmt.Errorf("unexpected token after ALTER: %s", p.curTok.Literal)
22092253
default:
@@ -11575,3 +11619,128 @@ func (p *Parser) parseAvailabilityReplicasServerOnly() []*ast.AvailabilityReplic
1157511619
}
1157611620
return replicas
1157711621
}
11622+
11623+
func (p *Parser) parseAlterEventSessionStatement() (*ast.AlterEventSessionStatement, error) {
11624+
p.nextToken() // consume EVENT
11625+
if strings.ToUpper(p.curTok.Literal) != "SESSION" {
11626+
return nil, fmt.Errorf("expected SESSION after EVENT, got %s", p.curTok.Literal)
11627+
}
11628+
p.nextToken() // consume SESSION
11629+
11630+
stmt := &ast.AlterEventSessionStatement{
11631+
Name: p.parseIdentifier(),
11632+
}
11633+
11634+
// ON SERVER/DATABASE
11635+
if p.curTok.Type == TokenOn {
11636+
p.nextToken()
11637+
scopeUpper := strings.ToUpper(p.curTok.Literal)
11638+
if scopeUpper == "SERVER" {
11639+
stmt.SessionScope = "Server"
11640+
p.nextToken()
11641+
} else if scopeUpper == "DATABASE" {
11642+
stmt.SessionScope = "Database"
11643+
p.nextToken()
11644+
}
11645+
}
11646+
11647+
// Parse action: ADD/DROP EVENT/TARGET, WITH, STATE
11648+
// Note: Don't use isStatementTerminator here because DROP is a statement terminator
11649+
// but we need to handle DROP EVENT/TARGET inside ALTER EVENT SESSION
11650+
for p.curTok.Type != TokenSemicolon && p.curTok.Type != TokenEOF {
11651+
// Check for GO batch separator
11652+
if p.curTok.Type == TokenIdent && strings.ToUpper(p.curTok.Literal) == "GO" {
11653+
break
11654+
}
11655+
// Check for other statement starters that would indicate end of this statement
11656+
switch p.curTok.Type {
11657+
case TokenSelect, TokenInsert, TokenUpdate, TokenDelete, TokenDeclare,
11658+
TokenIf, TokenWhile, TokenBegin, TokenEnd, TokenCreate, TokenAlter,
11659+
TokenExec, TokenExecute, TokenPrint, TokenThrow:
11660+
// These tokens indicate start of a new statement
11661+
goto done
11662+
}
11663+
upperLit := strings.ToUpper(p.curTok.Literal)
11664+
11665+
if upperLit == "ADD" || p.curTok.Type == TokenAdd {
11666+
p.nextToken()
11667+
addType := strings.ToUpper(p.curTok.Literal)
11668+
p.nextToken()
11669+
11670+
if addType == "EVENT" {
11671+
if stmt.StatementType == "" {
11672+
stmt.StatementType = "AddEventDeclarationOptionalSessionOptions"
11673+
}
11674+
event := p.parseEventDeclaration()
11675+
stmt.EventDeclarations = append(stmt.EventDeclarations, event)
11676+
} else if addType == "TARGET" {
11677+
if stmt.StatementType == "" {
11678+
stmt.StatementType = "AddTargetDeclarationOptionalSessionOptions"
11679+
}
11680+
target := p.parseTargetDeclaration()
11681+
stmt.TargetDeclarations = append(stmt.TargetDeclarations, target)
11682+
}
11683+
} else if upperLit == "DROP" || p.curTok.Type == TokenDrop {
11684+
p.nextToken()
11685+
dropType := strings.ToUpper(p.curTok.Literal)
11686+
p.nextToken()
11687+
11688+
if dropType == "EVENT" {
11689+
if stmt.StatementType == "" {
11690+
stmt.StatementType = "DropEventSpecificationOptionalSessionOptions"
11691+
}
11692+
objName := p.parseEventSessionObjectName()
11693+
stmt.DropEventDeclarations = append(stmt.DropEventDeclarations, objName)
11694+
} else if dropType == "TARGET" {
11695+
if stmt.StatementType == "" {
11696+
stmt.StatementType = "DropTargetSpecificationOptionalSessionOptions"
11697+
}
11698+
objName := p.parseEventSessionObjectName()
11699+
stmt.DropTargetDeclarations = append(stmt.DropTargetDeclarations, objName)
11700+
}
11701+
} else if upperLit == "WITH" || p.curTok.Type == TokenWith {
11702+
p.nextToken()
11703+
if p.curTok.Type == TokenLParen {
11704+
p.nextToken()
11705+
for p.curTok.Type != TokenRParen && p.curTok.Type != TokenEOF {
11706+
opt := p.parseSessionOption()
11707+
if opt != nil {
11708+
stmt.SessionOptions = append(stmt.SessionOptions, opt)
11709+
}
11710+
if p.curTok.Type == TokenComma {
11711+
p.nextToken()
11712+
} else {
11713+
break
11714+
}
11715+
}
11716+
if p.curTok.Type == TokenRParen {
11717+
p.nextToken()
11718+
}
11719+
}
11720+
if stmt.StatementType == "" {
11721+
stmt.StatementType = "RequiredSessionOptions"
11722+
}
11723+
} else if upperLit == "STATE" {
11724+
p.nextToken() // consume STATE
11725+
if p.curTok.Type == TokenEquals {
11726+
p.nextToken() // consume =
11727+
}
11728+
stateVal := strings.ToUpper(p.curTok.Literal)
11729+
if stateVal == "START" {
11730+
stmt.StatementType = "AlterStateIsStart"
11731+
} else if stateVal == "STOP" {
11732+
stmt.StatementType = "AlterStateIsStop"
11733+
}
11734+
p.nextToken()
11735+
} else if p.curTok.Type == TokenComma {
11736+
p.nextToken()
11737+
} else {
11738+
p.nextToken()
11739+
}
11740+
}
11741+
done:
11742+
if p.curTok.Type == TokenSemicolon {
11743+
p.nextToken()
11744+
}
11745+
return stmt, nil
11746+
}

0 commit comments

Comments
 (0)