Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion ast/create_simple_statements.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,9 +215,36 @@ type CreateXmlIndexStatement struct {
func (s *CreateXmlIndexStatement) node() {}
func (s *CreateXmlIndexStatement) statement() {}

// EventNotificationObjectScope represents the scope of an event notification (SERVER, DATABASE, or QUEUE).
type EventNotificationObjectScope struct {
Target string `json:"Target,omitempty"` // "Server", "Database", or "Queue"
QueueName *SchemaObjectName `json:"QueueName,omitempty"`
}

func (s *EventNotificationObjectScope) node() {}

// EventTypeGroupContainer is an interface for event type/group containers.
type EventTypeGroupContainer interface {
node()
eventTypeGroupContainer()
}

// EventGroupContainer represents a group of events.
type EventGroupContainer struct {
EventGroup string `json:"EventGroup,omitempty"`
}

func (c *EventGroupContainer) node() {}
func (c *EventGroupContainer) eventTypeGroupContainer() {}

// CreateEventNotificationStatement represents a CREATE EVENT NOTIFICATION statement.
type CreateEventNotificationStatement struct {
Name *Identifier `json:"Name,omitempty"`
Name *Identifier `json:"Name,omitempty"`
Scope *EventNotificationObjectScope `json:"Scope,omitempty"`
WithFanIn bool `json:"WithFanIn,omitempty"`
EventTypeGroups []EventTypeGroupContainer `json:"EventTypeGroups,omitempty"`
BrokerService *StringLiteral `json:"BrokerService,omitempty"`
BrokerInstanceSpecifier *StringLiteral `json:"BrokerInstanceSpecifier,omitempty"`
}

func (s *CreateEventNotificationStatement) node() {}
Expand Down
5 changes: 4 additions & 1 deletion ast/create_trigger_statement.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,8 @@ func (s *CreateTriggerStatement) node() {}

// EventTypeContainer represents an event type container
type EventTypeContainer struct {
EventType string
EventType string `json:"EventType,omitempty"`
}

func (c *EventTypeContainer) node() {}
func (c *EventTypeContainer) eventTypeGroupContainer() {}
46 changes: 46 additions & 0 deletions parser/marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -7005,9 +7005,55 @@ func createEventNotificationStatementToJSON(s *ast.CreateEventNotificationStatem
if s.Name != nil {
node["Name"] = identifierToJSON(s.Name)
}
if s.Scope != nil {
node["Scope"] = eventNotificationObjectScopeToJSON(s.Scope)
// Include WithFanIn when Scope is present
node["WithFanIn"] = s.WithFanIn
}
if len(s.EventTypeGroups) > 0 {
groups := make([]jsonNode, len(s.EventTypeGroups))
for i, g := range s.EventTypeGroups {
groups[i] = eventTypeGroupContainerToJSON(g)
}
node["EventTypeGroups"] = groups
}
if s.BrokerService != nil {
node["BrokerService"] = stringLiteralToJSON(s.BrokerService)
}
if s.BrokerInstanceSpecifier != nil {
node["BrokerInstanceSpecifier"] = stringLiteralToJSON(s.BrokerInstanceSpecifier)
}
return node
}

func eventNotificationObjectScopeToJSON(s *ast.EventNotificationObjectScope) jsonNode {
node := jsonNode{
"$type": "EventNotificationObjectScope",
"Target": s.Target,
}
if s.QueueName != nil {
node["QueueName"] = schemaObjectNameToJSON(s.QueueName)
}
return node
}

func eventTypeGroupContainerToJSON(c ast.EventTypeGroupContainer) jsonNode {
switch v := c.(type) {
case *ast.EventTypeContainer:
return jsonNode{
"$type": "EventTypeContainer",
"EventType": v.EventType,
}
case *ast.EventGroupContainer:
return jsonNode{
"$type": "EventGroupContainer",
"EventGroup": v.EventGroup,
}
default:
return jsonNode{"$type": "Unknown"}
}
}

func alterDatabaseAddFileStatementToJSON(s *ast.AlterDatabaseAddFileStatement) jsonNode {
node := jsonNode{
"$type": "AlterDatabaseAddFileStatement",
Expand Down
122 changes: 121 additions & 1 deletion parser/parse_statements.go
Original file line number Diff line number Diff line change
Expand Up @@ -4525,11 +4525,131 @@ func (p *Parser) parseCreateEventNotificationFromEvent() (*ast.CreateEventNotifi
Name: p.parseIdentifier(),
}

// Skip rest of statement
// Parse ON <scope>
if p.curTok.Type == TokenOn {
p.nextToken() // consume ON
stmt.Scope = &ast.EventNotificationObjectScope{}

scopeUpper := strings.ToUpper(p.curTok.Literal)
switch scopeUpper {
case "SERVER":
stmt.Scope.Target = "Server"
p.nextToken()
case "DATABASE":
stmt.Scope.Target = "Database"
p.nextToken()
case "QUEUE":
stmt.Scope.Target = "Queue"
p.nextToken()
// Parse queue name
stmt.Scope.QueueName, _ = p.parseSchemaObjectName()
}
}

// Parse optional WITH FAN_IN
if strings.ToUpper(p.curTok.Literal) == "WITH" {
p.nextToken() // consume WITH
if strings.ToUpper(p.curTok.Literal) == "FAN_IN" {
stmt.WithFanIn = true
p.nextToken() // consume FAN_IN
}
}

// Parse FOR <event_type_or_group_list>
if strings.ToUpper(p.curTok.Literal) == "FOR" {
p.nextToken() // consume FOR

// Parse comma-separated list of event types/groups
for {
eventName := p.curTok.Literal
p.nextToken()

// Convert event name to PascalCase and determine if it's a group or type
pascalName := eventNameToPascalCase(eventName)

// If name ends with "Events" (after conversion), it's a group
if strings.HasSuffix(strings.ToUpper(eventName), "_EVENTS") || strings.HasSuffix(strings.ToUpper(eventName), "EVENTS") {
stmt.EventTypeGroups = append(stmt.EventTypeGroups, &ast.EventGroupContainer{
EventGroup: pascalName,
})
} else {
stmt.EventTypeGroups = append(stmt.EventTypeGroups, &ast.EventTypeContainer{
EventType: pascalName,
})
}

if p.curTok.Type != TokenComma {
break
}
p.nextToken() // consume comma
}
}

// Parse TO SERVICE 'service_name', 'broker_instance'
if strings.ToUpper(p.curTok.Literal) == "TO" {
p.nextToken() // consume TO
if strings.ToUpper(p.curTok.Literal) == "SERVICE" {
p.nextToken() // consume SERVICE

// Parse broker service name (string literal)
if p.curTok.Type == TokenString {
litVal := p.curTok.Literal
// Strip surrounding quotes
if len(litVal) >= 2 && litVal[0] == '\'' && litVal[len(litVal)-1] == '\'' {
litVal = litVal[1 : len(litVal)-1]
}
stmt.BrokerService = &ast.StringLiteral{
LiteralType: "String",
IsNational: false,
IsLargeObject: false,
Value: litVal,
}
p.nextToken()
}

// Parse comma and broker instance specifier
if p.curTok.Type == TokenComma {
p.nextToken() // consume comma

if p.curTok.Type == TokenString {
litVal := p.curTok.Literal
// Strip surrounding quotes
if len(litVal) >= 2 && litVal[0] == '\'' && litVal[len(litVal)-1] == '\'' {
litVal = litVal[1 : len(litVal)-1]
}
stmt.BrokerInstanceSpecifier = &ast.StringLiteral{
LiteralType: "String",
IsNational: false,
IsLargeObject: false,
Value: litVal,
}
p.nextToken()
}
}
}
}

// Skip any remaining tokens
p.skipToEndOfStatement()
return stmt, nil
}

// eventNameToPascalCase converts an event name like "Object_Created" or "DDL_CREDENTIAL_EVENTS" to PascalCase.
func eventNameToPascalCase(name string) string {
// Split by underscore
parts := strings.Split(name, "_")
var result strings.Builder
for _, part := range parts {
if len(part) == 0 {
continue
}
// Capitalize first letter, lowercase rest
result.WriteString(strings.ToUpper(part[:1]))
result.WriteString(strings.ToLower(part[1:]))
}
return result.String()
}

func (p *Parser) parseCreatePartitionFunctionFromPartition() (*ast.CreatePartitionFunctionStatement, error) {
// PARTITION has already been consumed, curTok is FUNCTION
if strings.ToUpper(p.curTok.Literal) == "FUNCTION" {
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo": true}
{}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo": true}
{}
Loading