Skip to content

Commit e1a195c

Browse files
committed
Add ALTER TABLE ALTER COLUMN HIDDEN and encryption support
- Support ADD HIDDEN and DROP HIDDEN column options - Parse HIDDEN modifier in column definitions - Add ColumnEncryptionDefinition and related parameter types - Parse ENCRYPTED WITH (...) specifications - Parse MASKED WITH (FUNCTION = ...) specifications - Add MaskingFunction and Encryption fields to AlterTableAlterColumnStatement - Add marshaling for encryption parameter types - Enable 2 passing tests
1 parent f5e6081 commit e1a195c

6 files changed

Lines changed: 200 additions & 3 deletions

File tree

ast/alter_table_alter_column_statement.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ type AlterTableAlterColumnStatement struct {
1010
IsHidden bool
1111
Collation *Identifier
1212
IsMasked bool
13+
Encryption *ColumnEncryptionDefinition
14+
MaskingFunction ScalarExpression
1315
}
1416

1517
func (a *AlterTableAlterColumnStatement) node() {}

ast/column_encryption.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package ast
2+
3+
// ColumnEncryptionDefinition represents the ENCRYPTED WITH specification
4+
type ColumnEncryptionDefinition struct {
5+
Parameters []ColumnEncryptionParameter
6+
}
7+
8+
func (c *ColumnEncryptionDefinition) node() {}
9+
10+
// ColumnEncryptionParameter is an interface for encryption parameters
11+
type ColumnEncryptionParameter interface {
12+
columnEncryptionParameter()
13+
}
14+
15+
// ColumnEncryptionKeyNameParameter represents COLUMN_ENCRYPTION_KEY = key_name
16+
type ColumnEncryptionKeyNameParameter struct {
17+
Name *Identifier
18+
ParameterKind string // "ColumnEncryptionKey"
19+
}
20+
21+
func (c *ColumnEncryptionKeyNameParameter) columnEncryptionParameter() {}
22+
23+
// ColumnEncryptionTypeParameter represents ENCRYPTION_TYPE = DETERMINISTIC|RANDOMIZED
24+
type ColumnEncryptionTypeParameter struct {
25+
EncryptionType string // "Deterministic", "Randomized"
26+
ParameterKind string // "EncryptionType"
27+
}
28+
29+
func (c *ColumnEncryptionTypeParameter) columnEncryptionParameter() {}
30+
31+
// ColumnEncryptionAlgorithmParameter represents ALGORITHM = 'algorithm_name'
32+
type ColumnEncryptionAlgorithmParameter struct {
33+
EncryptionAlgorithm ScalarExpression // StringLiteral
34+
ParameterKind string // "Algorithm"
35+
}
36+
37+
func (c *ColumnEncryptionAlgorithmParameter) columnEncryptionParameter() {}

parser/marshal.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -887,16 +887,67 @@ func alterTableAlterColumnStatementToJSON(s *ast.AlterTableAlterColumnStatement)
887887
node["StorageOptions"] = columnStorageOptionsToJSON(s.StorageOptions)
888888
}
889889
node["IsHidden"] = s.IsHidden
890+
if s.Encryption != nil {
891+
node["Encryption"] = columnEncryptionDefinitionToJSON(s.Encryption)
892+
}
890893
if s.Collation != nil {
891894
node["Collation"] = identifierToJSON(s.Collation)
892895
}
893896
node["IsMasked"] = s.IsMasked
897+
if s.MaskingFunction != nil {
898+
node["MaskingFunction"] = scalarExpressionToJSON(s.MaskingFunction)
899+
}
894900
if s.SchemaObjectName != nil {
895901
node["SchemaObjectName"] = schemaObjectNameToJSON(s.SchemaObjectName)
896902
}
897903
return node
898904
}
899905

906+
func columnEncryptionDefinitionToJSON(e *ast.ColumnEncryptionDefinition) jsonNode {
907+
node := jsonNode{
908+
"$type": "ColumnEncryptionDefinition",
909+
}
910+
if len(e.Parameters) > 0 {
911+
params := make([]jsonNode, len(e.Parameters))
912+
for i, p := range e.Parameters {
913+
params[i] = columnEncryptionParameterToJSON(p)
914+
}
915+
node["Parameters"] = params
916+
}
917+
return node
918+
}
919+
920+
func columnEncryptionParameterToJSON(p ast.ColumnEncryptionParameter) jsonNode {
921+
switch param := p.(type) {
922+
case *ast.ColumnEncryptionKeyNameParameter:
923+
node := jsonNode{
924+
"$type": "ColumnEncryptionKeyNameParameter",
925+
"ParameterKind": param.ParameterKind,
926+
}
927+
if param.Name != nil {
928+
node["Name"] = identifierToJSON(param.Name)
929+
}
930+
return node
931+
case *ast.ColumnEncryptionTypeParameter:
932+
return jsonNode{
933+
"$type": "ColumnEncryptionTypeParameter",
934+
"EncryptionType": param.EncryptionType,
935+
"ParameterKind": param.ParameterKind,
936+
}
937+
case *ast.ColumnEncryptionAlgorithmParameter:
938+
node := jsonNode{
939+
"$type": "ColumnEncryptionAlgorithmParameter",
940+
"ParameterKind": param.ParameterKind,
941+
}
942+
if param.EncryptionAlgorithm != nil {
943+
node["EncryptionAlgorithm"] = scalarExpressionToJSON(param.EncryptionAlgorithm)
944+
}
945+
return node
946+
default:
947+
return jsonNode{"$type": "Unknown"}
948+
}
949+
}
950+
900951
func alterMessageTypeStatementToJSON(s *ast.AlterMessageTypeStatement) jsonNode {
901952
node := jsonNode{
902953
"$type": "AlterMessageTypeStatement",

parser/parse_ddl.go

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3487,6 +3487,9 @@ func (p *Parser) parseAlterTableAlterColumnStatement(tableName *ast.SchemaObject
34873487
} else if nextLit == "SPARSE" {
34883488
stmt.AlterTableAlterColumnOption = "AddSparse"
34893489
p.nextToken()
3490+
} else if nextLit == "HIDDEN" {
3491+
stmt.AlterTableAlterColumnOption = "AddHidden"
3492+
p.nextToken()
34903493
} else if nextLit == "NOT" {
34913494
p.nextToken() // consume NOT
34923495
if strings.ToUpper(p.curTok.Literal) == "FOR" {
@@ -3514,6 +3517,9 @@ func (p *Parser) parseAlterTableAlterColumnStatement(tableName *ast.SchemaObject
35143517
} else if nextLit == "SPARSE" {
35153518
stmt.AlterTableAlterColumnOption = "DropSparse"
35163519
p.nextToken()
3520+
} else if nextLit == "HIDDEN" {
3521+
stmt.AlterTableAlterColumnOption = "DropHidden"
3522+
p.nextToken()
35173523
} else if nextLit == "NOT" {
35183524
p.nextToken() // consume NOT
35193525
if strings.ToUpper(p.curTok.Literal) == "FOR" {
@@ -3547,7 +3553,7 @@ func (p *Parser) parseAlterTableAlterColumnStatement(tableName *ast.SchemaObject
35473553
stmt.Collation = p.parseIdentifier()
35483554
}
35493555

3550-
// Parse optional SPARSE, FILESTREAM, COLUMN_SET FOR ALL_SPARSE_COLUMNS
3556+
// Parse optional SPARSE, FILESTREAM, COLUMN_SET FOR ALL_SPARSE_COLUMNS, HIDDEN, ENCRYPTED WITH, MASKED WITH
35513557
for {
35523558
upperLit := strings.ToUpper(p.curTok.Literal)
35533559
if upperLit == "SPARSE" {
@@ -3574,6 +3580,48 @@ func (p *Parser) parseAlterTableAlterColumnStatement(tableName *ast.SchemaObject
35743580
stmt.StorageOptions = &ast.ColumnStorageOptions{}
35753581
}
35763582
stmt.StorageOptions.SparseOption = "ColumnSetForAllSparseColumns"
3583+
} else if upperLit == "HIDDEN" {
3584+
stmt.IsHidden = true
3585+
p.nextToken()
3586+
} else if upperLit == "ENCRYPTED" {
3587+
p.nextToken() // consume ENCRYPTED
3588+
if strings.ToUpper(p.curTok.Literal) == "WITH" {
3589+
p.nextToken() // consume WITH
3590+
}
3591+
// Parse encryption specification: (COLUMN_ENCRYPTION_KEY = key1, ENCRYPTION_TYPE = ..., ALGORITHM = ...)
3592+
if p.curTok.Type == TokenLParen {
3593+
encSpec, err := p.parseColumnEncryptionSpecification()
3594+
if err != nil {
3595+
return nil, err
3596+
}
3597+
stmt.Encryption = encSpec
3598+
}
3599+
} else if upperLit == "MASKED" {
3600+
stmt.IsMasked = true
3601+
p.nextToken() // consume MASKED
3602+
if strings.ToUpper(p.curTok.Literal) == "WITH" {
3603+
p.nextToken() // consume WITH
3604+
}
3605+
// Parse masking function: (function = 'default()')
3606+
if p.curTok.Type == TokenLParen {
3607+
p.nextToken() // consume (
3608+
if strings.ToUpper(p.curTok.Literal) == "FUNCTION" {
3609+
p.nextToken() // consume FUNCTION
3610+
if p.curTok.Type == TokenEquals {
3611+
p.nextToken() // consume =
3612+
}
3613+
if p.curTok.Type == TokenString {
3614+
maskFunc, err := p.parseStringLiteral()
3615+
if err != nil {
3616+
return nil, err
3617+
}
3618+
stmt.MaskingFunction = maskFunc
3619+
}
3620+
}
3621+
if p.curTok.Type == TokenRParen {
3622+
p.nextToken() // consume )
3623+
}
3624+
}
35773625
} else {
35783626
break
35793627
}
@@ -3599,6 +3647,65 @@ func (p *Parser) parseAlterTableAlterColumnStatement(tableName *ast.SchemaObject
35993647
return stmt, nil
36003648
}
36013649

3650+
func (p *Parser) parseColumnEncryptionSpecification() (*ast.ColumnEncryptionDefinition, error) {
3651+
// curTok should be (
3652+
p.nextToken() // consume (
3653+
3654+
encDef := &ast.ColumnEncryptionDefinition{}
3655+
3656+
for p.curTok.Type != TokenRParen && p.curTok.Type != TokenEOF {
3657+
paramName := strings.ToUpper(p.curTok.Literal)
3658+
p.nextToken()
3659+
3660+
if p.curTok.Type == TokenEquals {
3661+
p.nextToken() // consume =
3662+
}
3663+
3664+
switch paramName {
3665+
case "COLUMN_ENCRYPTION_KEY":
3666+
param := &ast.ColumnEncryptionKeyNameParameter{
3667+
ParameterKind: "ColumnEncryptionKey",
3668+
Name: p.parseIdentifier(),
3669+
}
3670+
encDef.Parameters = append(encDef.Parameters, param)
3671+
case "ENCRYPTION_TYPE":
3672+
encType := strings.ToUpper(p.curTok.Literal)
3673+
param := &ast.ColumnEncryptionTypeParameter{
3674+
ParameterKind: "EncryptionType",
3675+
}
3676+
if encType == "DETERMINISTIC" {
3677+
param.EncryptionType = "Deterministic"
3678+
} else if encType == "RANDOMIZED" {
3679+
param.EncryptionType = "Randomized"
3680+
} else {
3681+
param.EncryptionType = encType
3682+
}
3683+
p.nextToken()
3684+
encDef.Parameters = append(encDef.Parameters, param)
3685+
case "ALGORITHM":
3686+
str, err := p.parseStringLiteral()
3687+
if err != nil {
3688+
return nil, err
3689+
}
3690+
param := &ast.ColumnEncryptionAlgorithmParameter{
3691+
ParameterKind: "Algorithm",
3692+
EncryptionAlgorithm: str,
3693+
}
3694+
encDef.Parameters = append(encDef.Parameters, param)
3695+
}
3696+
3697+
if p.curTok.Type == TokenComma {
3698+
p.nextToken()
3699+
}
3700+
}
3701+
3702+
if p.curTok.Type == TokenRParen {
3703+
p.nextToken() // consume )
3704+
}
3705+
3706+
return encDef, nil
3707+
}
3708+
36023709
func (p *Parser) parseAlterTableAddStatement(tableName *ast.SchemaObjectName) (*ast.AlterTableAddTableElementStatement, error) {
36033710
// Consume ADD
36043711
p.nextToken()
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)