Skip to content

Commit f8fb162

Browse files
committed
Add CREATE/DROP SYMMETRIC KEY full parsing support
Add support for: - CREATE SYMMETRIC KEY with FROM PROVIDER clause - WITH options: PROVIDER_KEY_NAME, ALGORITHM, CREATION_DISPOSITION - ENCRYPTION BY clause with CryptoMechanism (CERTIFICATE, SYMMETRIC KEY, etc.) - DROP SYMMETRIC KEY with REMOVE PROVIDER KEY option Enabled tests: - Baselines100_SymmetricKeyStatementTests100 - SymmetricKeyStatementTests100
1 parent e08d5e9 commit f8fb162

6 files changed

Lines changed: 254 additions & 3 deletions

File tree

ast/create_simple_statements.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,14 +174,36 @@ type CreationDispositionKeyOption struct {
174174
func (c *CreationDispositionKeyOption) node() {}
175175
func (c *CreationDispositionKeyOption) keyOption() {}
176176

177+
// CryptoMechanism represents an encryption mechanism (CERTIFICATE, KEY, PASSWORD, etc.)
178+
type CryptoMechanism struct {
179+
CryptoMechanismType string `json:"CryptoMechanismType,omitempty"` // "Certificate", "SymmetricKey", "AsymmetricKey", "Password"
180+
Identifier *Identifier `json:"Identifier,omitempty"`
181+
PasswordOrSignature ScalarExpression `json:"PasswordOrSignature,omitempty"`
182+
}
183+
184+
func (c *CryptoMechanism) node() {}
185+
177186
// CreateSymmetricKeyStatement represents a CREATE SYMMETRIC KEY statement.
178187
type CreateSymmetricKeyStatement struct {
179-
Name *Identifier `json:"Name,omitempty"`
188+
KeyOptions []KeyOption `json:"KeyOptions,omitempty"`
189+
Provider *Identifier `json:"Provider,omitempty"`
190+
Name *Identifier `json:"Name,omitempty"`
191+
EncryptingMechanisms []*CryptoMechanism `json:"EncryptingMechanisms,omitempty"`
180192
}
181193

182194
func (s *CreateSymmetricKeyStatement) node() {}
183195
func (s *CreateSymmetricKeyStatement) statement() {}
184196

197+
// DropSymmetricKeyStatement represents a DROP SYMMETRIC KEY statement.
198+
type DropSymmetricKeyStatement struct {
199+
RemoveProviderKey bool `json:"RemoveProviderKey,omitempty"`
200+
Name *Identifier `json:"Name,omitempty"`
201+
IsIfExists bool `json:"IsIfExists"`
202+
}
203+
204+
func (s *DropSymmetricKeyStatement) node() {}
205+
func (s *DropSymmetricKeyStatement) statement() {}
206+
185207
// CreateMessageTypeStatement represents a CREATE MESSAGE TYPE statement.
186208
type CreateMessageTypeStatement struct {
187209
Name *Identifier `json:"Name,omitempty"`

parser/marshal.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,8 @@ func statementToJSON(stmt ast.Statement) jsonNode {
214214
return dropAssemblyStatementToJSON(s)
215215
case *ast.DropAsymmetricKeyStatement:
216216
return dropAsymmetricKeyStatementToJSON(s)
217+
case *ast.DropSymmetricKeyStatement:
218+
return dropSymmetricKeyStatementToJSON(s)
217219
case *ast.CreateTableStatement:
218220
return createTableStatementToJSON(s)
219221
case *ast.GrantStatement:
@@ -8329,6 +8331,18 @@ func dropAsymmetricKeyStatementToJSON(s *ast.DropAsymmetricKeyStatement) jsonNod
83298331
return node
83308332
}
83318333

8334+
func dropSymmetricKeyStatementToJSON(s *ast.DropSymmetricKeyStatement) jsonNode {
8335+
node := jsonNode{
8336+
"$type": "DropSymmetricKeyStatement",
8337+
"RemoveProviderKey": s.RemoveProviderKey,
8338+
}
8339+
if s.Name != nil {
8340+
node["Name"] = identifierToJSON(s.Name)
8341+
}
8342+
node["IsIfExists"] = s.IsIfExists
8343+
return node
8344+
}
8345+
83328346
func alterTableTriggerModificationStatementToJSON(s *ast.AlterTableTriggerModificationStatement) jsonNode {
83338347
node := jsonNode{
83348348
"$type": "AlterTableTriggerModificationStatement",
@@ -9302,9 +9316,40 @@ func createSymmetricKeyStatementToJSON(s *ast.CreateSymmetricKeyStatement) jsonN
93029316
node := jsonNode{
93039317
"$type": "CreateSymmetricKeyStatement",
93049318
}
9319+
if len(s.KeyOptions) > 0 {
9320+
opts := make([]interface{}, len(s.KeyOptions))
9321+
for i, opt := range s.KeyOptions {
9322+
opts[i] = keyOptionToJSON(opt)
9323+
}
9324+
node["KeyOptions"] = opts
9325+
}
9326+
if s.Provider != nil {
9327+
node["Provider"] = identifierToJSON(s.Provider)
9328+
}
93059329
if s.Name != nil {
93069330
node["Name"] = identifierToJSON(s.Name)
93079331
}
9332+
if len(s.EncryptingMechanisms) > 0 {
9333+
mechs := make([]jsonNode, len(s.EncryptingMechanisms))
9334+
for i, mech := range s.EncryptingMechanisms {
9335+
mechs[i] = cryptoMechanismToJSON(mech)
9336+
}
9337+
node["EncryptingMechanisms"] = mechs
9338+
}
9339+
return node
9340+
}
9341+
9342+
func cryptoMechanismToJSON(mech *ast.CryptoMechanism) jsonNode {
9343+
node := jsonNode{
9344+
"$type": "CryptoMechanism",
9345+
"CryptoMechanismType": mech.CryptoMechanismType,
9346+
}
9347+
if mech.Identifier != nil {
9348+
node["Identifier"] = identifierToJSON(mech.Identifier)
9349+
}
9350+
if mech.PasswordOrSignature != nil {
9351+
node["PasswordOrSignature"] = scalarExpressionToJSON(mech.PasswordOrSignature)
9352+
}
93089353
return node
93099354
}
93109355

parser/parse_ddl.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ func (p *Parser) parseDropStatement() (ast.Statement, error) {
112112
return p.parseDropCryptographicProviderStatement()
113113
case "ASYMMETRIC":
114114
return p.parseDropAsymmetricKeyStatement()
115+
case "SYMMETRIC":
116+
return p.parseDropSymmetricKeyStatement()
115117
}
116118

117119
return nil, fmt.Errorf("unexpected token after DROP: %s", p.curTok.Literal)
@@ -698,6 +700,50 @@ func (p *Parser) parseDropAsymmetricKeyStatement() (*ast.DropAsymmetricKeyStatem
698700
return stmt, nil
699701
}
700702

703+
func (p *Parser) parseDropSymmetricKeyStatement() (*ast.DropSymmetricKeyStatement, error) {
704+
// Consume SYMMETRIC
705+
p.nextToken()
706+
707+
// Expect KEY
708+
if strings.ToUpper(p.curTok.Literal) == "KEY" {
709+
p.nextToken()
710+
}
711+
712+
stmt := &ast.DropSymmetricKeyStatement{}
713+
714+
// Check for IF EXISTS
715+
if p.curTok.Type == TokenIf {
716+
p.nextToken()
717+
if strings.ToUpper(p.curTok.Literal) != "EXISTS" {
718+
return nil, fmt.Errorf("expected EXISTS after IF, got %s", p.curTok.Literal)
719+
}
720+
p.nextToken()
721+
stmt.IsIfExists = true
722+
}
723+
724+
// Parse key name
725+
stmt.Name = p.parseIdentifier()
726+
727+
// Check for REMOVE PROVIDER KEY
728+
if strings.ToUpper(p.curTok.Literal) == "REMOVE" {
729+
p.nextToken() // consume REMOVE
730+
if strings.ToUpper(p.curTok.Literal) == "PROVIDER" {
731+
p.nextToken() // consume PROVIDER
732+
if strings.ToUpper(p.curTok.Literal) == "KEY" {
733+
p.nextToken() // consume KEY
734+
}
735+
stmt.RemoveProviderKey = true
736+
}
737+
}
738+
739+
// Skip optional semicolon
740+
if p.curTok.Type == TokenSemicolon {
741+
p.nextToken()
742+
}
743+
744+
return stmt, nil
745+
}
746+
701747
func (p *Parser) parseDropDatabaseStatement() (ast.Statement, error) {
702748
// Consume DATABASE
703749
p.nextToken()

parser/parse_statements.go

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6772,11 +6772,149 @@ func (p *Parser) parseCreateSymmetricKeyStatement() (*ast.CreateSymmetricKeyStat
67726772
Name: p.parseIdentifier(),
67736773
}
67746774

6775+
// Check for FROM PROVIDER clause
6776+
if p.curTok.Type == TokenFrom && strings.ToUpper(p.peekTok.Literal) == "PROVIDER" {
6777+
p.nextToken() // consume FROM
6778+
p.nextToken() // consume PROVIDER
6779+
stmt.Provider = p.parseIdentifier()
6780+
}
6781+
6782+
// Check for WITH clause (key options)
6783+
if p.curTok.Type == TokenWith {
6784+
p.nextToken() // consume WITH
6785+
keyOpts, err := p.parseSymmetricKeyOptions()
6786+
if err != nil {
6787+
return nil, err
6788+
}
6789+
stmt.KeyOptions = keyOpts
6790+
}
6791+
6792+
// Check for ENCRYPTION BY clause
6793+
if strings.ToUpper(p.curTok.Literal) == "ENCRYPTION" {
6794+
p.nextToken() // consume ENCRYPTION
6795+
if strings.ToUpper(p.curTok.Literal) == "BY" {
6796+
p.nextToken() // consume BY
6797+
}
6798+
mechanisms, err := p.parseCryptoMechanisms()
6799+
if err != nil {
6800+
return nil, err
6801+
}
6802+
stmt.EncryptingMechanisms = mechanisms
6803+
}
6804+
67756805
// Skip rest of statement
67766806
p.skipToEndOfStatement()
67776807
return stmt, nil
67786808
}
67796809

6810+
func (p *Parser) parseSymmetricKeyOptions() ([]ast.KeyOption, error) {
6811+
var options []ast.KeyOption
6812+
6813+
for {
6814+
optName := strings.ToUpper(p.curTok.Literal)
6815+
switch optName {
6816+
case "PROVIDER_KEY_NAME":
6817+
p.nextToken() // consume PROVIDER_KEY_NAME
6818+
if p.curTok.Type == TokenEquals {
6819+
p.nextToken() // consume =
6820+
}
6821+
keyName, _ := p.parseScalarExpression()
6822+
opt := &ast.ProviderKeyNameKeyOption{
6823+
KeyName: keyName,
6824+
OptionKind: "ProviderKeyName",
6825+
}
6826+
options = append(options, opt)
6827+
6828+
case "ALGORITHM":
6829+
p.nextToken() // consume ALGORITHM
6830+
if p.curTok.Type == TokenEquals {
6831+
p.nextToken() // consume =
6832+
}
6833+
algo := strings.ToUpper(p.curTok.Literal)
6834+
p.nextToken() // consume algorithm name
6835+
opt := &ast.AlgorithmKeyOption{
6836+
Algorithm: algo,
6837+
OptionKind: "Algorithm",
6838+
}
6839+
options = append(options, opt)
6840+
6841+
case "CREATION_DISPOSITION":
6842+
p.nextToken() // consume CREATION_DISPOSITION
6843+
if p.curTok.Type == TokenEquals {
6844+
p.nextToken() // consume =
6845+
}
6846+
disposition := strings.ToUpper(p.curTok.Literal)
6847+
p.nextToken() // consume CREATE_NEW or OPEN_EXISTING
6848+
opt := &ast.CreationDispositionKeyOption{
6849+
IsCreateNew: disposition == "CREATE_NEW",
6850+
OptionKind: "CreationDisposition",
6851+
}
6852+
options = append(options, opt)
6853+
6854+
default:
6855+
return options, nil
6856+
}
6857+
6858+
if p.curTok.Type == TokenComma {
6859+
p.nextToken() // consume ,
6860+
} else {
6861+
break
6862+
}
6863+
}
6864+
6865+
return options, nil
6866+
}
6867+
6868+
func (p *Parser) parseCryptoMechanisms() ([]*ast.CryptoMechanism, error) {
6869+
var mechanisms []*ast.CryptoMechanism
6870+
6871+
for {
6872+
mechanism := &ast.CryptoMechanism{}
6873+
upperLit := strings.ToUpper(p.curTok.Literal)
6874+
6875+
switch upperLit {
6876+
case "CERTIFICATE":
6877+
p.nextToken() // consume CERTIFICATE
6878+
mechanism.CryptoMechanismType = "Certificate"
6879+
mechanism.Identifier = p.parseIdentifier()
6880+
case "SYMMETRIC":
6881+
p.nextToken() // consume SYMMETRIC
6882+
if strings.ToUpper(p.curTok.Literal) == "KEY" {
6883+
p.nextToken() // consume KEY
6884+
}
6885+
mechanism.CryptoMechanismType = "SymmetricKey"
6886+
mechanism.Identifier = p.parseIdentifier()
6887+
case "ASYMMETRIC":
6888+
p.nextToken() // consume ASYMMETRIC
6889+
if strings.ToUpper(p.curTok.Literal) == "KEY" {
6890+
p.nextToken() // consume KEY
6891+
}
6892+
mechanism.CryptoMechanismType = "AsymmetricKey"
6893+
mechanism.Identifier = p.parseIdentifier()
6894+
case "PASSWORD":
6895+
p.nextToken() // consume PASSWORD
6896+
if p.curTok.Type == TokenEquals {
6897+
p.nextToken() // consume =
6898+
}
6899+
mechanism.CryptoMechanismType = "Password"
6900+
// Password should be a string literal
6901+
mechanism.PasswordOrSignature, _ = p.parseScalarExpression()
6902+
default:
6903+
return mechanisms, nil
6904+
}
6905+
6906+
mechanisms = append(mechanisms, mechanism)
6907+
6908+
if p.curTok.Type == TokenComma {
6909+
p.nextToken() // consume ,
6910+
} else {
6911+
break
6912+
}
6913+
}
6914+
6915+
return mechanisms, nil
6916+
}
6917+
67806918
func (p *Parser) parseCreateCertificateStatement() (*ast.CreateCertificateStatement, error) {
67816919
p.nextToken() // consume CERTIFICATE
67826920

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)