Skip to content

Commit 47a197d

Browse files
kyleconroyclaude
andcommitted
Add SQL140 CREATE TABLE features: SUPPRESS_MESSAGES, ENCRYPTED, HIDDEN, TIME type
- Add SUPPRESS_MESSAGES sub-option for IGNORE_DUP_KEY index options - Add ENCRYPTED WITH column encryption parsing - Add MaskingFunction and Encryption fields to ColumnDefinition AST - Accept TIME token as valid data type in parseDataTypeReference - Use parseSystemVersioningTableOption for nested SYSTEM_VERSIONING options - Treat hierarchyid, geometry, geography as UserDataTypeReference (CLR types) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 45c18f1 commit 47a197d

5 files changed

Lines changed: 61 additions & 29 deletions

File tree

ast/create_table_statement.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ type ColumnDefinition struct {
6969
IsRowGuidCol bool
7070
IsHidden bool
7171
IsMasked bool
72+
MaskingFunction ScalarExpression
73+
Encryption *ColumnEncryptionDefinition
7274
Nullable *NullableConstraintDefinition
7375
StorageOptions *ColumnStorageOptions
7476
}

parser/marshal.go

Lines changed: 54 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5614,20 +5614,11 @@ func (p *Parser) parseCreateTableStatement() (*ast.CreateTableStatement, error)
56145614
}
56155615
stmt.Options = append(stmt.Options, opt)
56165616
} else if optionName == "SYSTEM_VERSIONING" {
5617-
if p.curTok.Type == TokenEquals {
5618-
p.nextToken() // consume =
5619-
}
5620-
stateUpper := strings.ToUpper(p.curTok.Literal)
5621-
state := "On"
5622-
if stateUpper == "OFF" {
5623-
state = "Off"
5617+
opt, err := p.parseSystemVersioningTableOption()
5618+
if err != nil {
5619+
return nil, err
56245620
}
5625-
p.nextToken() // consume ON/OFF
5626-
stmt.Options = append(stmt.Options, &ast.SystemVersioningTableOption{
5627-
OptionKind: "LockEscalation",
5628-
OptionState: state,
5629-
ConsistencyCheckEnabled: "NotSet",
5630-
})
5621+
stmt.Options = append(stmt.Options, opt)
56315622
} else if optionName == "CLUSTERED" {
56325623
// Could be CLUSTERED INDEX or CLUSTERED COLUMNSTORE INDEX
56335624
if strings.ToUpper(p.curTok.Literal) == "COLUMNSTORE" {
@@ -7664,22 +7655,40 @@ func (p *Parser) parseColumnDefinition() (*ast.ColumnDefinition, error) {
76647655
} else if upperLit == "MASKED" {
76657656
p.nextToken() // consume MASKED
76667657
col.IsMasked = true
7667-
// Skip optional WITH clause
7658+
// Parse optional WITH clause for masking function
76687659
if strings.ToUpper(p.curTok.Literal) == "WITH" {
7669-
p.nextToken()
7660+
p.nextToken() // consume WITH
76707661
if p.curTok.Type == TokenLParen {
7671-
depth := 1
7672-
p.nextToken()
7673-
for depth > 0 && p.curTok.Type != TokenEOF {
7674-
if p.curTok.Type == TokenLParen {
7675-
depth++
7676-
} else if p.curTok.Type == TokenRParen {
7677-
depth--
7662+
p.nextToken() // consume (
7663+
if strings.ToUpper(p.curTok.Literal) == "FUNCTION" {
7664+
p.nextToken() // consume FUNCTION
7665+
if p.curTok.Type == TokenEquals {
7666+
p.nextToken() // consume =
7667+
}
7668+
if p.curTok.Type == TokenString {
7669+
maskFunc, err := p.parseStringLiteral()
7670+
if err == nil {
7671+
col.MaskingFunction = maskFunc
7672+
}
76787673
}
7679-
p.nextToken()
7674+
}
7675+
if p.curTok.Type == TokenRParen {
7676+
p.nextToken() // consume )
76807677
}
76817678
}
76827679
}
7680+
} else if upperLit == "ENCRYPTED" {
7681+
p.nextToken() // consume ENCRYPTED
7682+
if strings.ToUpper(p.curTok.Literal) == "WITH" {
7683+
p.nextToken() // consume WITH
7684+
}
7685+
// Parse encryption specification: (COLUMN_ENCRYPTION_KEY = key1, ENCRYPTION_TYPE = ..., ALGORITHM = ...)
7686+
if p.curTok.Type == TokenLParen {
7687+
encSpec, err := p.parseColumnEncryptionSpecification()
7688+
if err == nil {
7689+
col.Encryption = encSpec
7690+
}
7691+
}
76837692
} else if upperLit == "IDENTITY" && col.IdentityOptions == nil {
76847693
// IDENTITY can appear after DEFAULT or other constraints
76857694
p.nextToken() // consume IDENTITY
@@ -7975,6 +7984,22 @@ func (p *Parser) parseConstraintIndexOptions() []ast.IndexOption {
79757984
OptionKind: "IgnoreDupKey",
79767985
OptionState: p.capitalizeFirst(strings.ToLower(valueStr)),
79777986
}
7987+
// Check for optional (SUPPRESS_MESSAGES = ON/OFF)
7988+
if valueStr == "ON" && p.curTok.Type == TokenLParen {
7989+
p.nextToken() // consume (
7990+
if strings.ToUpper(p.curTok.Literal) == "SUPPRESS_MESSAGES" {
7991+
p.nextToken() // consume SUPPRESS_MESSAGES
7992+
if p.curTok.Type == TokenEquals {
7993+
p.nextToken() // consume =
7994+
}
7995+
suppressVal := strings.ToUpper(p.curTok.Literal) == "ON"
7996+
opt.SuppressMessagesOption = &suppressVal
7997+
p.nextToken() // consume ON/OFF
7998+
}
7999+
if p.curTok.Type == TokenRParen {
8000+
p.nextToken() // consume )
8001+
}
8002+
}
79788003
options = append(options, opt)
79798004
} else if valueStr == "ON" || valueStr == "OFF" {
79808005
opt := &ast.IndexStateOption{
@@ -9609,6 +9634,12 @@ func columnDefinitionToJSON(c *ast.ColumnDefinition) jsonNode {
96099634
if c.Index != nil {
96109635
node["Index"] = indexDefinitionToJSON(c.Index)
96119636
}
9637+
if c.Encryption != nil {
9638+
node["Encryption"] = columnEncryptionDefinitionToJSON(c.Encryption)
9639+
}
9640+
if c.MaskingFunction != nil {
9641+
node["MaskingFunction"] = scalarExpressionToJSON(c.MaskingFunction)
9642+
}
96129643
return node
96139644
}
96149645

parser/parse_statements.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -911,7 +911,9 @@ func (p *Parser) parseDataTypeReference() (ast.DataTypeReference, error) {
911911
p.nextToken() // consume NATIONAL
912912
}
913913

914-
if p.curTok.Type != TokenIdent {
914+
// Accept both identifiers and keyword tokens that are also valid data types
915+
// TIME is both a keyword (WAITFOR TIME, AT TIME ZONE) and a data type
916+
if p.curTok.Type != TokenIdent && p.curTok.Type != TokenTime {
915917
return nil, fmt.Errorf("expected data type, got %s", p.curTok.Literal)
916918
}
917919

@@ -1253,9 +1255,6 @@ func getSqlDataTypeOption(typeName string) (string, bool) {
12531255
"UNIQUEIDENTIFIER": "UniqueIdentifier",
12541256
"XML": "Xml",
12551257
"JSON": "Json",
1256-
"GEOGRAPHY": "Geography",
1257-
"GEOMETRY": "Geometry",
1258-
"HIERARCHYID": "HierarchyId",
12591258
"ROWVERSION": "Rowversion",
12601259
"TIMESTAMP": "Timestamp",
12611260
"CONNECTION": "Connection",
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)