Skip to content

Commit 8c359e2

Browse files
kyleconroyclaude
andcommitted
Add ALTER AUTHORIZATION statement and AS clause support
- Add AlterAuthorizationStatement AST type and parser - Add AS clause support to DenyStatement - Add FULLTEXT STOPLIST object kind support - Enable SecurityStatement90Tests and Baselines90_SecurityStatement90Tests Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 6635435 commit 8c359e2

File tree

6 files changed

+243
-6
lines changed

6 files changed

+243
-6
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package ast
2+
3+
// AlterAuthorizationStatement represents an ALTER AUTHORIZATION statement
4+
type AlterAuthorizationStatement struct {
5+
SecurityTargetObject *SecurityTargetObject
6+
ToSchemaOwner bool
7+
PrincipalName *Identifier
8+
}
9+
10+
func (s *AlterAuthorizationStatement) node() {}
11+
func (s *AlterAuthorizationStatement) statement() {}

ast/deny_statement.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ type DenyStatement struct {
66
Principals []*SecurityPrincipal
77
CascadeOption bool
88
SecurityTargetObject *SecurityTargetObject
9+
AsClause *Identifier
910
}
1011

1112
func (s *DenyStatement) node() {}

parser/marshal.go

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,8 @@ func statementToJSON(stmt ast.Statement) jsonNode {
605605
return alterEndpointStatementToJSON(s)
606606
case *ast.AlterEventSessionStatement:
607607
return alterEventSessionStatementToJSON(s)
608+
case *ast.AlterAuthorizationStatement:
609+
return alterAuthorizationStatementToJSON(s)
608610
case *ast.AlterServiceStatement:
609611
return alterServiceStatementToJSON(s)
610612
case *ast.AlterCertificateStatement:
@@ -7879,8 +7881,13 @@ func (p *Parser) parseGrantStatement() (*ast.GrantStatement, error) {
78797881
p.nextToken() // consume FULLTEXT
78807882
if strings.ToUpper(p.curTok.Literal) == "CATALOG" {
78817883
p.nextToken() // consume CATALOG
7884+
stmt.SecurityTargetObject.ObjectKind = "FullTextCatalog"
7885+
} else if strings.ToUpper(p.curTok.Literal) == "STOPLIST" {
7886+
p.nextToken() // consume STOPLIST
7887+
stmt.SecurityTargetObject.ObjectKind = "FullTextStopList"
7888+
} else {
7889+
stmt.SecurityTargetObject.ObjectKind = "FullTextCatalog"
78827890
}
7883-
stmt.SecurityTargetObject.ObjectKind = "FullTextCatalog"
78847891
case "MESSAGE":
78857892
p.nextToken() // consume MESSAGE
78867893
if strings.ToUpper(p.curTok.Literal) == "TYPE" {
@@ -8170,8 +8177,13 @@ func (p *Parser) parseRevokeStatement() (*ast.RevokeStatement, error) {
81708177
p.nextToken()
81718178
if strings.ToUpper(p.curTok.Literal) == "CATALOG" {
81728179
p.nextToken()
8180+
stmt.SecurityTargetObject.ObjectKind = "FullTextCatalog"
8181+
} else if strings.ToUpper(p.curTok.Literal) == "STOPLIST" {
8182+
p.nextToken()
8183+
stmt.SecurityTargetObject.ObjectKind = "FullTextStopList"
8184+
} else {
8185+
stmt.SecurityTargetObject.ObjectKind = "FullTextCatalog"
81738186
}
8174-
stmt.SecurityTargetObject.ObjectKind = "FullTextCatalog"
81758187
case "MESSAGE":
81768188
p.nextToken()
81778189
if strings.ToUpper(p.curTok.Literal) == "TYPE" {
@@ -8442,8 +8454,13 @@ func (p *Parser) parseDenyStatement() (*ast.DenyStatement, error) {
84428454
p.nextToken()
84438455
if strings.ToUpper(p.curTok.Literal) == "CATALOG" {
84448456
p.nextToken()
8457+
stmt.SecurityTargetObject.ObjectKind = "FullTextCatalog"
8458+
} else if strings.ToUpper(p.curTok.Literal) == "STOPLIST" {
8459+
p.nextToken()
8460+
stmt.SecurityTargetObject.ObjectKind = "FullTextStopList"
8461+
} else {
8462+
stmt.SecurityTargetObject.ObjectKind = "FullTextCatalog"
84458463
}
8446-
stmt.SecurityTargetObject.ObjectKind = "FullTextCatalog"
84478464
case "MESSAGE":
84488465
p.nextToken()
84498466
if strings.ToUpper(p.curTok.Literal) == "TYPE" {
@@ -8570,7 +8587,7 @@ func (p *Parser) parseDenyStatement() (*ast.DenyStatement, error) {
85708587
}
85718588

85728589
// Parse principal(s)
8573-
for p.curTok.Type != TokenEOF && p.curTok.Type != TokenSemicolon && strings.ToUpper(p.curTok.Literal) != "CASCADE" {
8590+
for p.curTok.Type != TokenEOF && p.curTok.Type != TokenSemicolon && strings.ToUpper(p.curTok.Literal) != "CASCADE" && strings.ToUpper(p.curTok.Literal) != "AS" {
85748591
principal := &ast.SecurityPrincipal{}
85758592
if p.curTok.Type == TokenPublic {
85768593
principal.PrincipalType = "Public"
@@ -8599,6 +8616,12 @@ func (p *Parser) parseDenyStatement() (*ast.DenyStatement, error) {
85998616
p.nextToken()
86008617
}
86018618

8619+
// Check for AS clause
8620+
if strings.ToUpper(p.curTok.Literal) == "AS" {
8621+
p.nextToken() // consume AS
8622+
stmt.AsClause = p.parseIdentifier()
8623+
}
8624+
86028625
// Skip optional semicolon
86038626
if p.curTok.Type == TokenSemicolon {
86048627
p.nextToken()
@@ -9302,6 +9325,9 @@ func denyStatementToJSON(s *ast.DenyStatement) jsonNode {
93029325
}
93039326
node["Principals"] = principals
93049327
}
9328+
if s.AsClause != nil {
9329+
node["AsClause"] = identifierToJSON(s.AsClause)
9330+
}
93059331
return node
93069332
}
93079333

@@ -16973,6 +16999,20 @@ func alterEventSessionStatementToJSON(s *ast.AlterEventSessionStatement) jsonNod
1697316999
return node
1697417000
}
1697517001

17002+
func alterAuthorizationStatementToJSON(s *ast.AlterAuthorizationStatement) jsonNode {
17003+
node := jsonNode{
17004+
"$type": "AlterAuthorizationStatement",
17005+
"ToSchemaOwner": s.ToSchemaOwner,
17006+
}
17007+
if s.SecurityTargetObject != nil {
17008+
node["SecurityTargetObject"] = securityTargetObjectToJSON(s.SecurityTargetObject)
17009+
}
17010+
if s.PrincipalName != nil {
17011+
node["PrincipalName"] = identifierToJSON(s.PrincipalName)
17012+
}
17013+
return node
17014+
}
17015+
1697617016
func eventDeclarationToJSON(e *ast.EventDeclaration) jsonNode {
1697717017
node := jsonNode{
1697817018
"$type": "EventDeclaration",

parser/parse_ddl.go

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2189,6 +2189,8 @@ func (p *Parser) parseAlterStatement() (ast.Statement, error) {
21892189
return p.parseAlterExternalStatement()
21902190
case TokenView:
21912191
return p.parseAlterViewStatement()
2192+
case TokenAuthorization:
2193+
return p.parseAlterAuthorizationStatement()
21922194
case TokenIdent:
21932195
// Handle keywords that are not reserved tokens
21942196
switch strings.ToUpper(p.curTok.Literal) {
@@ -11744,3 +11746,186 @@ done:
1174411746
}
1174511747
return stmt, nil
1174611748
}
11749+
11750+
func (p *Parser) parseAlterAuthorizationStatement() (*ast.AlterAuthorizationStatement, error) {
11751+
// Consume AUTHORIZATION
11752+
p.nextToken()
11753+
11754+
stmt := &ast.AlterAuthorizationStatement{}
11755+
11756+
// Expect ON
11757+
if p.curTok.Type == TokenOn {
11758+
p.nextToken() // consume ON
11759+
}
11760+
11761+
// Parse security target object
11762+
stmt.SecurityTargetObject = &ast.SecurityTargetObject{}
11763+
stmt.SecurityTargetObject.ObjectKind = "NotSpecified"
11764+
11765+
// Parse object kind and ::
11766+
objectKind := strings.ToUpper(p.curTok.Literal)
11767+
switch objectKind {
11768+
case "SERVER":
11769+
p.nextToken()
11770+
if strings.ToUpper(p.curTok.Literal) == "ROLE" {
11771+
p.nextToken()
11772+
stmt.SecurityTargetObject.ObjectKind = "ServerRole"
11773+
} else {
11774+
stmt.SecurityTargetObject.ObjectKind = "Server"
11775+
}
11776+
case "APPLICATION":
11777+
p.nextToken()
11778+
if strings.ToUpper(p.curTok.Literal) == "ROLE" {
11779+
p.nextToken()
11780+
}
11781+
stmt.SecurityTargetObject.ObjectKind = "ApplicationRole"
11782+
case "ASYMMETRIC":
11783+
p.nextToken()
11784+
if strings.ToUpper(p.curTok.Literal) == "KEY" {
11785+
p.nextToken()
11786+
}
11787+
stmt.SecurityTargetObject.ObjectKind = "AsymmetricKey"
11788+
case "SYMMETRIC":
11789+
p.nextToken()
11790+
if strings.ToUpper(p.curTok.Literal) == "KEY" {
11791+
p.nextToken()
11792+
}
11793+
stmt.SecurityTargetObject.ObjectKind = "SymmetricKey"
11794+
case "REMOTE":
11795+
p.nextToken()
11796+
if strings.ToUpper(p.curTok.Literal) == "SERVICE" {
11797+
p.nextToken()
11798+
if strings.ToUpper(p.curTok.Literal) == "BINDING" {
11799+
p.nextToken()
11800+
}
11801+
}
11802+
stmt.SecurityTargetObject.ObjectKind = "RemoteServiceBinding"
11803+
case "FULLTEXT":
11804+
p.nextToken()
11805+
if strings.ToUpper(p.curTok.Literal) == "CATALOG" {
11806+
p.nextToken()
11807+
}
11808+
stmt.SecurityTargetObject.ObjectKind = "FullTextCatalog"
11809+
case "MESSAGE":
11810+
p.nextToken()
11811+
if strings.ToUpper(p.curTok.Literal) == "TYPE" {
11812+
p.nextToken()
11813+
}
11814+
stmt.SecurityTargetObject.ObjectKind = "MessageType"
11815+
case "XML":
11816+
p.nextToken()
11817+
if strings.ToUpper(p.curTok.Literal) == "SCHEMA" {
11818+
p.nextToken()
11819+
if strings.ToUpper(p.curTok.Literal) == "COLLECTION" {
11820+
p.nextToken()
11821+
}
11822+
}
11823+
stmt.SecurityTargetObject.ObjectKind = "XmlSchemaCollection"
11824+
case "SEARCH":
11825+
p.nextToken()
11826+
if strings.ToUpper(p.curTok.Literal) == "PROPERTY" {
11827+
p.nextToken()
11828+
if strings.ToUpper(p.curTok.Literal) == "LIST" {
11829+
p.nextToken()
11830+
}
11831+
}
11832+
stmt.SecurityTargetObject.ObjectKind = "SearchPropertyList"
11833+
case "AVAILABILITY":
11834+
p.nextToken()
11835+
if strings.ToUpper(p.curTok.Literal) == "GROUP" {
11836+
p.nextToken()
11837+
}
11838+
stmt.SecurityTargetObject.ObjectKind = "AvailabilityGroup"
11839+
case "TYPE":
11840+
p.nextToken()
11841+
stmt.SecurityTargetObject.ObjectKind = "Type"
11842+
case "OBJECT":
11843+
p.nextToken()
11844+
stmt.SecurityTargetObject.ObjectKind = "Object"
11845+
case "ASSEMBLY":
11846+
p.nextToken()
11847+
stmt.SecurityTargetObject.ObjectKind = "Assembly"
11848+
case "CERTIFICATE":
11849+
p.nextToken()
11850+
stmt.SecurityTargetObject.ObjectKind = "Certificate"
11851+
case "CONTRACT":
11852+
p.nextToken()
11853+
stmt.SecurityTargetObject.ObjectKind = "Contract"
11854+
case "DATABASE":
11855+
p.nextToken()
11856+
stmt.SecurityTargetObject.ObjectKind = "Database"
11857+
case "ENDPOINT":
11858+
p.nextToken()
11859+
stmt.SecurityTargetObject.ObjectKind = "Endpoint"
11860+
case "LOGIN":
11861+
p.nextToken()
11862+
stmt.SecurityTargetObject.ObjectKind = "Login"
11863+
case "ROLE":
11864+
p.nextToken()
11865+
stmt.SecurityTargetObject.ObjectKind = "Role"
11866+
case "ROUTE":
11867+
p.nextToken()
11868+
stmt.SecurityTargetObject.ObjectKind = "Route"
11869+
case "SCHEMA":
11870+
p.nextToken()
11871+
stmt.SecurityTargetObject.ObjectKind = "Schema"
11872+
case "SERVICE":
11873+
p.nextToken()
11874+
stmt.SecurityTargetObject.ObjectKind = "Service"
11875+
case "USER":
11876+
p.nextToken()
11877+
stmt.SecurityTargetObject.ObjectKind = "User"
11878+
}
11879+
11880+
// Parse :: if present
11881+
if p.curTok.Type == TokenColonColon {
11882+
p.nextToken()
11883+
}
11884+
11885+
// Parse object name as multi-part identifier
11886+
if p.curTok.Type == TokenDot || p.curTok.Type == TokenIdent || p.curTok.Type == TokenLBracket {
11887+
stmt.SecurityTargetObject.ObjectName = &ast.SecurityTargetObjectName{}
11888+
multiPart := &ast.MultiPartIdentifier{}
11889+
for {
11890+
if p.curTok.Type == TokenDot {
11891+
multiPart.Identifiers = append(multiPart.Identifiers, &ast.Identifier{
11892+
Value: "",
11893+
QuoteType: "NotQuoted",
11894+
})
11895+
} else {
11896+
id := p.parseIdentifier()
11897+
multiPart.Identifiers = append(multiPart.Identifiers, id)
11898+
}
11899+
if p.curTok.Type == TokenDot {
11900+
p.nextToken()
11901+
} else {
11902+
break
11903+
}
11904+
}
11905+
multiPart.Count = len(multiPart.Identifiers)
11906+
stmt.SecurityTargetObject.ObjectName.MultiPartIdentifier = multiPart
11907+
}
11908+
11909+
// Expect TO
11910+
if p.curTok.Type == TokenTo {
11911+
p.nextToken()
11912+
}
11913+
11914+
// Check for SCHEMA OWNER or principal name
11915+
if strings.ToUpper(p.curTok.Literal) == "SCHEMA" {
11916+
p.nextToken() // consume SCHEMA
11917+
if strings.ToUpper(p.curTok.Literal) == "OWNER" {
11918+
p.nextToken() // consume OWNER
11919+
}
11920+
stmt.ToSchemaOwner = true
11921+
} else {
11922+
// Parse principal name
11923+
stmt.PrincipalName = p.parseIdentifier()
11924+
}
11925+
11926+
if p.curTok.Type == TokenSemicolon {
11927+
p.nextToken()
11928+
}
11929+
11930+
return stmt, nil
11931+
}
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)