Skip to content

Commit 746b30e

Browse files
committed
Add CREATE PROCEDURE WITH clause and EXTERNAL NAME parsing
- Add Options field to CreateProcedureStatement for EXECUTE AS, RECOMPILE, ENCRYPTION - Add MethodSpecifier field for AS EXTERNAL NAME (CLR procedures) - Add ProcedureOption, ExecuteAsProcedureOption types - Parse WITH EXECUTE AS CALLER/SELF/OWNER/'string' - Parse AS EXTERNAL NAME assembly.class.method - Update ExecuteAsClause to use Literal field instead of Principal
1 parent 2186056 commit 746b30e

5 files changed

Lines changed: 155 additions & 7 deletions

File tree

ast/alter_trigger_statement.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,12 @@ func (o *TriggerOption) triggerOption() {}
4343

4444
// ExecuteAsClause represents an EXECUTE AS clause
4545
type ExecuteAsClause struct {
46-
ExecuteAsOption string // Caller, Self, Owner, or specific user
47-
Principal ScalarExpression
46+
ExecuteAsOption string // Caller, Self, Owner, String
47+
Literal *StringLiteral // Used when ExecuteAsOption is "String"
4848
}
4949

50+
func (e *ExecuteAsClause) node() {}
51+
5052
// ExecuteAsTriggerOption represents an EXECUTE AS trigger option
5153
type ExecuteAsTriggerOption struct {
5254
OptionKind string // "ExecuteAsClause"

ast/create_procedure_statement.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ type CreateProcedureStatement struct {
66
Parameters []*ProcedureParameter
77
StatementList *StatementList
88
IsForReplication bool
9+
Options []ProcedureOptionBase
10+
MethodSpecifier *MethodSpecifier
911
}
1012

1113
func (c *CreateProcedureStatement) node() {}
@@ -22,3 +24,26 @@ type ProcedureParameter struct {
2224
}
2325

2426
func (p *ProcedureParameter) node() {}
27+
28+
// ProcedureOptionBase is the interface for procedure options.
29+
type ProcedureOptionBase interface {
30+
Node
31+
procedureOption()
32+
}
33+
34+
// ProcedureOption represents a simple procedure option like RECOMPILE or ENCRYPTION.
35+
type ProcedureOption struct {
36+
OptionKind string // Recompile, Encryption
37+
}
38+
39+
func (p *ProcedureOption) node() {}
40+
func (p *ProcedureOption) procedureOption() {}
41+
42+
// ExecuteAsProcedureOption represents an EXECUTE AS option for a procedure.
43+
type ExecuteAsProcedureOption struct {
44+
ExecuteAs *ExecuteAsClause
45+
OptionKind string // ExecuteAs
46+
}
47+
48+
func (e *ExecuteAsProcedureOption) node() {}
49+
func (e *ExecuteAsProcedureOption) procedureOption() {}

parser/marshal.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5602,19 +5602,76 @@ func createProcedureStatementToJSON(s *ast.CreateProcedureStatement) jsonNode {
56025602
if s.ProcedureReference != nil {
56035603
node["ProcedureReference"] = procedureReferenceToJSON(s.ProcedureReference)
56045604
}
5605+
if len(s.Options) > 0 {
5606+
options := make([]jsonNode, len(s.Options))
5607+
for i, opt := range s.Options {
5608+
options[i] = procedureOptionToJSON(opt)
5609+
}
5610+
node["Options"] = options
5611+
}
56055612
if len(s.Parameters) > 0 {
56065613
params := make([]jsonNode, len(s.Parameters))
56075614
for i, p := range s.Parameters {
56085615
params[i] = procedureParameterToJSON(p)
56095616
}
56105617
node["Parameters"] = params
56115618
}
5619+
if s.MethodSpecifier != nil {
5620+
node["MethodSpecifier"] = methodSpecifierToJSON(s.MethodSpecifier)
5621+
}
56125622
if s.StatementList != nil {
56135623
node["StatementList"] = statementListToJSON(s.StatementList)
56145624
}
56155625
return node
56165626
}
56175627

5628+
func procedureOptionToJSON(opt ast.ProcedureOptionBase) jsonNode {
5629+
switch o := opt.(type) {
5630+
case *ast.ProcedureOption:
5631+
return jsonNode{
5632+
"$type": "ProcedureOption",
5633+
"OptionKind": o.OptionKind,
5634+
}
5635+
case *ast.ExecuteAsProcedureOption:
5636+
node := jsonNode{
5637+
"$type": "ExecuteAsProcedureOption",
5638+
"OptionKind": o.OptionKind,
5639+
}
5640+
if o.ExecuteAs != nil {
5641+
node["ExecuteAs"] = executeAsClauseToJSON(o.ExecuteAs)
5642+
}
5643+
return node
5644+
}
5645+
return jsonNode{}
5646+
}
5647+
5648+
func executeAsClauseToJSON(e *ast.ExecuteAsClause) jsonNode {
5649+
node := jsonNode{
5650+
"$type": "ExecuteAsClause",
5651+
"ExecuteAsOption": e.ExecuteAsOption,
5652+
}
5653+
if e.Literal != nil {
5654+
node["Literal"] = stringLiteralToJSON(e.Literal)
5655+
}
5656+
return node
5657+
}
5658+
5659+
func methodSpecifierToJSON(m *ast.MethodSpecifier) jsonNode {
5660+
node := jsonNode{
5661+
"$type": "MethodSpecifier",
5662+
}
5663+
if m.AssemblyName != nil {
5664+
node["AssemblyName"] = identifierToJSON(m.AssemblyName)
5665+
}
5666+
if m.ClassName != nil {
5667+
node["ClassName"] = identifierToJSON(m.ClassName)
5668+
}
5669+
if m.MethodName != nil {
5670+
node["MethodName"] = identifierToJSON(m.MethodName)
5671+
}
5672+
return node
5673+
}
5674+
56185675
func createRoleStatementToJSON(s *ast.CreateRoleStatement) jsonNode {
56195676
node := jsonNode{
56205677
"$type": "CreateRoleStatement",

parser/parse_statements.go

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2480,20 +2480,64 @@ func (p *Parser) parseCreateProcedureStatement() (*ast.CreateProcedureStatement,
24802480
stmt.Parameters = params
24812481
}
24822482

2483-
// Skip WITH options (like RECOMPILE, ENCRYPTION, etc.)
2483+
// Parse WITH options (like RECOMPILE, ENCRYPTION, EXECUTE AS, etc.)
24842484
if p.curTok.Type == TokenWith {
24852485
p.nextToken()
24862486
for {
24872487
if strings.ToUpper(p.curTok.Literal) == "FOR" || p.curTok.Type == TokenAs || p.curTok.Type == TokenEOF {
24882488
break
24892489
}
2490-
if strings.ToUpper(p.curTok.Literal) == "REPLICATION" {
2490+
upperLit := strings.ToUpper(p.curTok.Literal)
2491+
if upperLit == "RECOMPILE" {
2492+
stmt.Options = append(stmt.Options, &ast.ProcedureOption{OptionKind: "Recompile"})
2493+
p.nextToken()
2494+
} else if upperLit == "ENCRYPTION" {
2495+
stmt.Options = append(stmt.Options, &ast.ProcedureOption{OptionKind: "Encryption"})
2496+
p.nextToken()
2497+
} else if upperLit == "EXECUTE" {
2498+
p.nextToken() // consume EXECUTE
2499+
if p.curTok.Type == TokenAs {
2500+
p.nextToken() // consume AS
2501+
}
2502+
executeAsOpt := &ast.ExecuteAsProcedureOption{
2503+
OptionKind: "ExecuteAs",
2504+
ExecuteAs: &ast.ExecuteAsClause{},
2505+
}
2506+
upperOption := strings.ToUpper(p.curTok.Literal)
2507+
if upperOption == "CALLER" {
2508+
executeAsOpt.ExecuteAs.ExecuteAsOption = "Caller"
2509+
p.nextToken()
2510+
} else if upperOption == "SELF" {
2511+
executeAsOpt.ExecuteAs.ExecuteAsOption = "Self"
2512+
p.nextToken()
2513+
} else if upperOption == "OWNER" {
2514+
executeAsOpt.ExecuteAs.ExecuteAsOption = "Owner"
2515+
p.nextToken()
2516+
} else if p.curTok.Type == TokenString {
2517+
executeAsOpt.ExecuteAs.ExecuteAsOption = "String"
2518+
value := p.curTok.Literal
2519+
// Strip quotes
2520+
if len(value) >= 2 && value[0] == '\'' && value[len(value)-1] == '\'' {
2521+
value = value[1 : len(value)-1]
2522+
}
2523+
executeAsOpt.ExecuteAs.Literal = &ast.StringLiteral{
2524+
LiteralType: "String",
2525+
IsNational: false,
2526+
IsLargeObject: false,
2527+
Value: value,
2528+
}
2529+
p.nextToken()
2530+
}
2531+
stmt.Options = append(stmt.Options, executeAsOpt)
2532+
} else if upperLit == "REPLICATION" {
24912533
stmt.IsForReplication = true
2534+
p.nextToken()
2535+
} else {
2536+
p.nextToken()
24922537
}
2493-
p.nextToken()
24942538
if p.curTok.Type == TokenComma {
24952539
p.nextToken()
2496-
} else {
2540+
} else if p.curTok.Type == TokenAs || strings.ToUpper(p.curTok.Literal) == "FOR" || p.curTok.Type == TokenEOF {
24972541
break
24982542
}
24992543
}
@@ -2504,6 +2548,26 @@ func (p *Parser) parseCreateProcedureStatement() (*ast.CreateProcedureStatement,
25042548
p.nextToken()
25052549
}
25062550

2551+
// Check for EXTERNAL NAME (CLR procedure)
2552+
if strings.ToUpper(p.curTok.Literal) == "EXTERNAL" {
2553+
p.nextToken() // consume EXTERNAL
2554+
if strings.ToUpper(p.curTok.Literal) == "NAME" {
2555+
p.nextToken() // consume NAME
2556+
}
2557+
// Parse assembly.class.method
2558+
stmt.MethodSpecifier = &ast.MethodSpecifier{}
2559+
stmt.MethodSpecifier.AssemblyName = p.parseIdentifier()
2560+
if p.curTok.Type == TokenDot {
2561+
p.nextToken()
2562+
stmt.MethodSpecifier.ClassName = p.parseIdentifier()
2563+
}
2564+
if p.curTok.Type == TokenDot {
2565+
p.nextToken()
2566+
stmt.MethodSpecifier.MethodName = p.parseIdentifier()
2567+
}
2568+
return stmt, nil
2569+
}
2570+
25072571
// Parse statement list
25082572
stmts, err := p.parseStatementList()
25092573
if err != nil {
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)