Skip to content

Commit c69043b

Browse files
committed
Add CREATE/ALTER QUEUE parsing with ACTIVATION options
Support ON filegroup clause, STATUS, RETENTION, PROCEDURE_NAME, MAX_QUEUE_READERS, and EXECUTE AS options for queue statements.
1 parent 7074428 commit c69043b

5 files changed

Lines changed: 192 additions & 15 deletions

File tree

ast/create_simple_statements.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,38 @@ type QueueOptionSimple struct {
7474
func (o *QueueOptionSimple) node() {}
7575
func (o *QueueOptionSimple) queueOption() {}
7676

77+
// QueueProcedureOption represents a PROCEDURE_NAME option.
78+
type QueueProcedureOption struct {
79+
OptionValue *SchemaObjectName `json:"OptionValue,omitempty"`
80+
OptionKind string `json:"OptionKind,omitempty"` // "ActivationProcedureName"
81+
}
82+
83+
func (o *QueueProcedureOption) node() {}
84+
func (o *QueueProcedureOption) queueOption() {}
85+
86+
// QueueValueOption represents an option with an integer value.
87+
type QueueValueOption struct {
88+
OptionValue ScalarExpression `json:"OptionValue,omitempty"`
89+
OptionKind string `json:"OptionKind,omitempty"` // "ActivationMaxQueueReaders"
90+
}
91+
92+
func (o *QueueValueOption) node() {}
93+
func (o *QueueValueOption) queueOption() {}
94+
95+
// QueueExecuteAsOption represents an EXECUTE AS option.
96+
type QueueExecuteAsOption struct {
97+
OptionValue *ExecuteAsClause `json:"OptionValue,omitempty"`
98+
OptionKind string `json:"OptionKind,omitempty"` // "ActivationExecuteAs"
99+
}
100+
101+
func (o *QueueExecuteAsOption) node() {}
102+
func (o *QueueExecuteAsOption) queueOption() {}
103+
77104
// CreateQueueStatement represents a CREATE QUEUE statement.
78105
type CreateQueueStatement struct {
79-
Name *SchemaObjectName `json:"Name,omitempty"`
80-
QueueOptions []QueueOption `json:"QueueOptions,omitempty"`
106+
Name *SchemaObjectName `json:"Name,omitempty"`
107+
OnFileGroup *IdentifierOrValueExpression `json:"OnFileGroup,omitempty"`
108+
QueueOptions []QueueOption `json:"QueueOptions,omitempty"`
81109
}
82110

83111
func (s *CreateQueueStatement) node() {}

parser/marshal.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13227,6 +13227,9 @@ func createQueueStatementToJSON(s *ast.CreateQueueStatement) jsonNode {
1322713227
node := jsonNode{
1322813228
"$type": "CreateQueueStatement",
1322913229
}
13230+
if s.OnFileGroup != nil {
13231+
node["OnFileGroup"] = identifierOrValueExpressionToJSON(s.OnFileGroup)
13232+
}
1323013233
if s.Name != nil {
1323113234
node["Name"] = schemaObjectNameToJSON(s.Name)
1323213235
}
@@ -13255,6 +13258,33 @@ func queueOptionToJSON(opt ast.QueueOption) jsonNode {
1325513258
"OptionKind": o.OptionKind,
1325613259
}
1325713260
return node
13261+
case *ast.QueueProcedureOption:
13262+
node := jsonNode{
13263+
"$type": "QueueProcedureOption",
13264+
"OptionKind": o.OptionKind,
13265+
}
13266+
if o.OptionValue != nil {
13267+
node["OptionValue"] = schemaObjectNameToJSON(o.OptionValue)
13268+
}
13269+
return node
13270+
case *ast.QueueValueOption:
13271+
node := jsonNode{
13272+
"$type": "QueueValueOption",
13273+
"OptionKind": o.OptionKind,
13274+
}
13275+
if o.OptionValue != nil {
13276+
node["OptionValue"] = scalarExpressionToJSON(o.OptionValue)
13277+
}
13278+
return node
13279+
case *ast.QueueExecuteAsOption:
13280+
node := jsonNode{
13281+
"$type": "QueueExecuteAsOption",
13282+
"OptionKind": o.OptionKind,
13283+
}
13284+
if o.OptionValue != nil {
13285+
node["OptionValue"] = executeAsClauseToJSON(o.OptionValue)
13286+
}
13287+
return node
1325813288
default:
1325913289
return jsonNode{"$type": "QueueOption"}
1326013290
}

parser/parse_statements.go

Lines changed: 130 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9346,6 +9346,16 @@ func (p *Parser) parseCreateQueueStatement() (*ast.CreateQueueStatement, error)
93469346
Name: name,
93479347
}
93489348

9349+
// Check for ON clause (filegroup)
9350+
if p.curTok.Type == TokenOn {
9351+
p.nextToken() // consume ON
9352+
fg, err := p.parseIdentifierOrValueExpression()
9353+
if err != nil {
9354+
return nil, err
9355+
}
9356+
stmt.OnFileGroup = fg
9357+
}
9358+
93499359
// Check for WITH clause
93509360
if strings.ToUpper(p.curTok.Literal) == "WITH" {
93519361
p.nextToken() // consume WITH
@@ -9356,8 +9366,21 @@ func (p *Parser) parseCreateQueueStatement() (*ast.CreateQueueStatement, error)
93569366
stmt.QueueOptions = opts
93579367
}
93589368

9359-
// Skip rest of statement
9360-
p.skipToEndOfStatement()
9369+
// Check for ON clause after WITH (alternative syntax)
9370+
if p.curTok.Type == TokenOn {
9371+
p.nextToken() // consume ON
9372+
fg, err := p.parseIdentifierOrValueExpression()
9373+
if err != nil {
9374+
return nil, err
9375+
}
9376+
stmt.OnFileGroup = fg
9377+
}
9378+
9379+
// Skip optional semicolon
9380+
if p.curTok.Type == TokenSemicolon {
9381+
p.nextToken()
9382+
}
9383+
93619384
return stmt, nil
93629385
}
93639386

@@ -9440,16 +9463,16 @@ func (p *Parser) parseQueueOptions() ([]ast.QueueOption, error) {
94409463
}
94419464
options = append(options, opt)
94429465
} else {
9443-
// Skip to end of activation clause
9444-
depth := 1
9445-
for depth > 0 && p.curTok.Type != TokenEOF {
9446-
if p.curTok.Type == TokenLParen {
9447-
depth++
9448-
} else if p.curTok.Type == TokenRParen {
9449-
depth--
9450-
}
9451-
p.nextToken()
9466+
// Parse activation options
9467+
activationOpts, err := p.parseActivationOptions()
9468+
if err != nil {
9469+
return nil, err
94529470
}
9471+
options = append(options, activationOpts...)
9472+
if p.curTok.Type != TokenRParen {
9473+
return nil, fmt.Errorf("expected ) after ACTIVATION options, got %s", p.curTok.Literal)
9474+
}
9475+
p.nextToken() // consume )
94539476
}
94549477

94559478
default:
@@ -9468,6 +9491,102 @@ func (p *Parser) parseQueueOptions() ([]ast.QueueOption, error) {
94689491
return options, nil
94699492
}
94709493

9494+
func (p *Parser) parseActivationOptions() ([]ast.QueueOption, error) {
9495+
var options []ast.QueueOption
9496+
9497+
for p.curTok.Type != TokenRParen && p.curTok.Type != TokenEOF {
9498+
optName := strings.ToUpper(p.curTok.Literal)
9499+
switch optName {
9500+
case "STATUS":
9501+
p.nextToken() // consume STATUS
9502+
if p.curTok.Type == TokenEquals {
9503+
p.nextToken() // consume =
9504+
}
9505+
state := strings.ToUpper(p.curTok.Literal)
9506+
p.nextToken() // consume ON/OFF
9507+
opt := &ast.QueueStateOption{
9508+
OptionState: capitalizeFirst(state),
9509+
OptionKind: "ActivationStatus",
9510+
}
9511+
options = append(options, opt)
9512+
9513+
case "PROCEDURE_NAME":
9514+
p.nextToken() // consume PROCEDURE_NAME
9515+
if p.curTok.Type == TokenEquals {
9516+
p.nextToken() // consume =
9517+
}
9518+
procName, _ := p.parseSchemaObjectName()
9519+
opt := &ast.QueueProcedureOption{
9520+
OptionValue: procName,
9521+
OptionKind: "ActivationProcedureName",
9522+
}
9523+
options = append(options, opt)
9524+
9525+
case "MAX_QUEUE_READERS":
9526+
p.nextToken() // consume MAX_QUEUE_READERS
9527+
if p.curTok.Type == TokenEquals {
9528+
p.nextToken() // consume =
9529+
}
9530+
value, _ := p.parseScalarExpression()
9531+
opt := &ast.QueueValueOption{
9532+
OptionValue: value,
9533+
OptionKind: "ActivationMaxQueueReaders",
9534+
}
9535+
options = append(options, opt)
9536+
9537+
case "EXECUTE":
9538+
p.nextToken() // consume EXECUTE
9539+
// Expect AS
9540+
if strings.ToUpper(p.curTok.Literal) == "AS" {
9541+
p.nextToken() // consume AS
9542+
}
9543+
execAs := &ast.ExecuteAsClause{}
9544+
// Check for SELF, OWNER, or string
9545+
execVal := strings.ToUpper(p.curTok.Literal)
9546+
switch execVal {
9547+
case "SELF":
9548+
execAs.ExecuteAsOption = "Self"
9549+
p.nextToken()
9550+
case "OWNER":
9551+
execAs.ExecuteAsOption = "Owner"
9552+
p.nextToken()
9553+
default:
9554+
// String literal - 'username'
9555+
if p.curTok.Type == TokenString {
9556+
value := p.curTok.Literal
9557+
// Remove quotes
9558+
if len(value) >= 2 && value[0] == '\'' && value[len(value)-1] == '\'' {
9559+
value = value[1 : len(value)-1]
9560+
}
9561+
execAs.ExecuteAsOption = "String"
9562+
execAs.Literal = &ast.StringLiteral{
9563+
LiteralType: "String",
9564+
IsNational: false,
9565+
IsLargeObject: false,
9566+
Value: value,
9567+
}
9568+
p.nextToken()
9569+
}
9570+
}
9571+
opt := &ast.QueueExecuteAsOption{
9572+
OptionValue: execAs,
9573+
OptionKind: "ActivationExecuteAs",
9574+
}
9575+
options = append(options, opt)
9576+
9577+
default:
9578+
return options, nil
9579+
}
9580+
9581+
// Check for comma separator
9582+
if p.curTok.Type == TokenComma {
9583+
p.nextToken()
9584+
}
9585+
}
9586+
9587+
return options, nil
9588+
}
9589+
94719590
func (p *Parser) parseCreateRouteStatement() (*ast.CreateRouteStatement, error) {
94729591
p.nextToken() // consume ROUTE
94739592

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)