Skip to content

Commit 7a69bac

Browse files
committed
Add TABLE DISTRIBUTION, CLUSTERED INDEX, and HEAP options for CREATE TABLE
- Add TableDistributionOption and TableHashDistributionPolicy AST types - Add TableIndexOption, TableClusteredIndexType, and TableNonClusteredIndexType AST types - Parse DISTRIBUTION = HASH(col1, col2, ...) in CREATE TABLE WITH clause - Parse CLUSTERED INDEX(col), CLUSTERED COLUMNSTORE INDEX, and HEAP options - Add JSON marshaling for all new table option types - Enables Baselines160_CreateTableTests160 and CreateTableTests160 tests
1 parent 269492f commit 7a69bac

5 files changed

Lines changed: 204 additions & 2 deletions

File tree

ast/table_distribution_option.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package ast
2+
3+
// TableDistributionOption represents DISTRIBUTION option for tables
4+
type TableDistributionOption struct {
5+
Value *TableHashDistributionPolicy
6+
OptionKind string // "Distribution"
7+
}
8+
9+
func (t *TableDistributionOption) node() {}
10+
func (t *TableDistributionOption) tableOption() {}
11+
12+
// TableHashDistributionPolicy represents HASH distribution for tables
13+
type TableHashDistributionPolicy struct {
14+
DistributionColumn *Identifier
15+
DistributionColumns []*Identifier
16+
}
17+
18+
func (t *TableHashDistributionPolicy) node() {}

ast/table_index_option.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package ast
2+
3+
// TableIndexOption represents a table index option in CREATE TABLE WITH
4+
type TableIndexOption struct {
5+
Value TableIndexType
6+
OptionKind string // "LockEscalation" (incorrect but matches expected output)
7+
}
8+
9+
func (t *TableIndexOption) node() {}
10+
func (t *TableIndexOption) tableOption() {}
11+
12+
// TableIndexType is an interface for different table index types
13+
type TableIndexType interface {
14+
Node
15+
tableIndexType()
16+
}
17+
18+
// TableClusteredIndexType represents a clustered index type
19+
type TableClusteredIndexType struct {
20+
Columns []*ColumnWithSortOrder
21+
ColumnStore bool
22+
}
23+
24+
func (t *TableClusteredIndexType) node() {}
25+
func (t *TableClusteredIndexType) tableIndexType() {}
26+
27+
// TableNonClusteredIndexType represents HEAP (non-clustered)
28+
type TableNonClusteredIndexType struct{}
29+
30+
func (t *TableNonClusteredIndexType) node() {}
31+
func (t *TableNonClusteredIndexType) tableIndexType() {}

parser/marshal.go

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4555,6 +4555,95 @@ func (p *Parser) parseCreateTableStatement() (*ast.CreateTableStatement, error)
45554555
return nil, err
45564556
}
45574557
stmt.Options = append(stmt.Options, opt)
4558+
} else if optionName == "CLUSTERED" {
4559+
// Could be CLUSTERED INDEX or CLUSTERED COLUMNSTORE INDEX
4560+
if strings.ToUpper(p.curTok.Literal) == "COLUMNSTORE" {
4561+
p.nextToken() // consume COLUMNSTORE
4562+
if p.curTok.Type == TokenIndex {
4563+
p.nextToken() // consume INDEX
4564+
}
4565+
stmt.Options = append(stmt.Options, &ast.TableIndexOption{
4566+
Value: &ast.TableClusteredIndexType{
4567+
ColumnStore: true,
4568+
},
4569+
OptionKind: "LockEscalation",
4570+
})
4571+
} else if p.curTok.Type == TokenIndex {
4572+
p.nextToken() // consume INDEX
4573+
// Parse column list
4574+
indexType := &ast.TableClusteredIndexType{
4575+
ColumnStore: false,
4576+
}
4577+
if p.curTok.Type == TokenLParen {
4578+
p.nextToken() // consume (
4579+
for p.curTok.Type != TokenRParen && p.curTok.Type != TokenEOF {
4580+
col := &ast.ColumnWithSortOrder{
4581+
SortOrder: ast.SortOrderNotSpecified,
4582+
Column: &ast.ColumnReferenceExpression{
4583+
ColumnType: "Regular",
4584+
MultiPartIdentifier: &ast.MultiPartIdentifier{
4585+
Identifiers: []*ast.Identifier{p.parseIdentifier()},
4586+
Count: 1,
4587+
},
4588+
},
4589+
}
4590+
indexType.Columns = append(indexType.Columns, col)
4591+
if p.curTok.Type == TokenComma {
4592+
p.nextToken()
4593+
} else {
4594+
break
4595+
}
4596+
}
4597+
if p.curTok.Type == TokenRParen {
4598+
p.nextToken()
4599+
}
4600+
}
4601+
stmt.Options = append(stmt.Options, &ast.TableIndexOption{
4602+
Value: indexType,
4603+
OptionKind: "LockEscalation",
4604+
})
4605+
}
4606+
} else if optionName == "HEAP" {
4607+
stmt.Options = append(stmt.Options, &ast.TableIndexOption{
4608+
Value: &ast.TableNonClusteredIndexType{},
4609+
OptionKind: "LockEscalation",
4610+
})
4611+
} else if optionName == "DISTRIBUTION" {
4612+
// Parse DISTRIBUTION = HASH(col1, col2, ...) or ROUND_ROBIN or REPLICATE
4613+
if p.curTok.Type == TokenEquals {
4614+
p.nextToken() // consume =
4615+
}
4616+
distTypeUpper := strings.ToUpper(p.curTok.Literal)
4617+
if distTypeUpper == "HASH" {
4618+
p.nextToken() // consume HASH
4619+
if p.curTok.Type == TokenLParen {
4620+
p.nextToken() // consume (
4621+
distOpt := &ast.TableDistributionOption{
4622+
OptionKind: "Distribution",
4623+
Value: &ast.TableHashDistributionPolicy{},
4624+
}
4625+
// Parse column list
4626+
for p.curTok.Type != TokenRParen && p.curTok.Type != TokenEOF {
4627+
col := p.parseIdentifier()
4628+
if distOpt.Value.DistributionColumn == nil {
4629+
distOpt.Value.DistributionColumn = col
4630+
}
4631+
distOpt.Value.DistributionColumns = append(distOpt.Value.DistributionColumns, col)
4632+
if p.curTok.Type == TokenComma {
4633+
p.nextToken()
4634+
} else {
4635+
break
4636+
}
4637+
}
4638+
if p.curTok.Type == TokenRParen {
4639+
p.nextToken()
4640+
}
4641+
stmt.Options = append(stmt.Options, distOpt)
4642+
}
4643+
} else {
4644+
// ROUND_ROBIN or REPLICATE - skip for now
4645+
p.nextToken()
4646+
}
45584647
} else {
45594648
// Skip unknown option value
45604649
if p.curTok.Type == TokenEquals {
@@ -7423,6 +7512,24 @@ func tableOptionToJSON(opt ast.TableOption) jsonNode {
74237512
node["XmlCompressionOption"] = xmlCompressionOptionToJSON(o.XmlCompressionOption)
74247513
}
74257514
return node
7515+
case *ast.TableIndexOption:
7516+
node := jsonNode{
7517+
"$type": "TableIndexOption",
7518+
"OptionKind": o.OptionKind,
7519+
}
7520+
if o.Value != nil {
7521+
node["Value"] = tableIndexTypeToJSON(o.Value)
7522+
}
7523+
return node
7524+
case *ast.TableDistributionOption:
7525+
node := jsonNode{
7526+
"$type": "TableDistributionOption",
7527+
"OptionKind": o.OptionKind,
7528+
}
7529+
if o.Value != nil {
7530+
node["Value"] = tableHashDistributionPolicyToJSON(o.Value)
7531+
}
7532+
return node
74267533
case *ast.SystemVersioningTableOption:
74277534
return systemVersioningTableOptionToJSON(o)
74287535
case *ast.MemoryOptimizedTableOption:
@@ -7540,6 +7647,52 @@ func xmlCompressionOptionToJSON(opt *ast.XmlCompressionOption) jsonNode {
75407647
return node
75417648
}
75427649

7650+
func tableHashDistributionPolicyToJSON(policy *ast.TableHashDistributionPolicy) jsonNode {
7651+
node := jsonNode{
7652+
"$type": "TableHashDistributionPolicy",
7653+
}
7654+
if policy.DistributionColumn != nil {
7655+
node["DistributionColumn"] = identifierToJSON(policy.DistributionColumn)
7656+
}
7657+
if len(policy.DistributionColumns) > 0 {
7658+
cols := make([]jsonNode, len(policy.DistributionColumns))
7659+
for i, c := range policy.DistributionColumns {
7660+
// First column is same as DistributionColumn, use $ref
7661+
if i == 0 && policy.DistributionColumn != nil {
7662+
cols[i] = jsonNode{"$ref": "Identifier"}
7663+
} else {
7664+
cols[i] = identifierToJSON(c)
7665+
}
7666+
}
7667+
node["DistributionColumns"] = cols
7668+
}
7669+
return node
7670+
}
7671+
7672+
func tableIndexTypeToJSON(t ast.TableIndexType) jsonNode {
7673+
switch v := t.(type) {
7674+
case *ast.TableClusteredIndexType:
7675+
node := jsonNode{
7676+
"$type": "TableClusteredIndexType",
7677+
"ColumnStore": v.ColumnStore,
7678+
}
7679+
if len(v.Columns) > 0 {
7680+
cols := make([]jsonNode, len(v.Columns))
7681+
for i, c := range v.Columns {
7682+
cols[i] = columnWithSortOrderToJSON(c)
7683+
}
7684+
node["Columns"] = cols
7685+
}
7686+
return node
7687+
case *ast.TableNonClusteredIndexType:
7688+
return jsonNode{
7689+
"$type": "TableNonClusteredIndexType",
7690+
}
7691+
default:
7692+
return jsonNode{"$type": "UnknownTableIndexType"}
7693+
}
7694+
}
7695+
75437696
func compressionPartitionRangeToJSON(pr *ast.CompressionPartitionRange) jsonNode {
75447697
node := jsonNode{
75457698
"$type": "CompressionPartitionRange",
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)