Skip to content

Commit b22298b

Browse files
committed
Add ALTER TABLE DROP WITH clause options parsing support
Implement parsing for ALTER TABLE DROP CONSTRAINT/COLUMN statements with WITH clause options including ONLINE, MOVE TO, and MAXDOP. - Add DropClusteredConstraintOption interface and implementations: - DropClusteredConstraintStateOption (ONLINE = ON/OFF) - DropClusteredConstraintMoveOption (MOVE TO filegroup) - DropClusteredConstraintValueOption (MAXDOP = value) - Add FileGroupOrPartitionScheme type for MOVE TO targets - Update parser to handle WITH (options) after element names - Add JSON marshaling for new types - Enable AlterTableDropTableElementStatementTests90 tests
1 parent 261e53c commit b22298b

File tree

5 files changed

+234
-7
lines changed

5 files changed

+234
-7
lines changed

ast/alter_table_drop_table_element_statement.go

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package ast
22

33
// AlterTableDropTableElementStatement represents an ALTER TABLE ... DROP statement.
44
type AlterTableDropTableElementStatement struct {
5-
SchemaObjectName *SchemaObjectName
5+
SchemaObjectName *SchemaObjectName
66
AlterTableDropTableElements []*AlterTableDropTableElement
77
}
88

@@ -11,9 +11,51 @@ func (*AlterTableDropTableElementStatement) statement() {}
1111

1212
// AlterTableDropTableElement represents an element being dropped from a table.
1313
type AlterTableDropTableElement struct {
14-
TableElementType string
15-
Name *Identifier
16-
IsIfExists bool
14+
TableElementType string
15+
Name *Identifier
16+
IsIfExists bool
17+
DropClusteredConstraintOptions []DropClusteredConstraintOption
1718
}
1819

1920
func (*AlterTableDropTableElement) node() {}
21+
22+
// DropClusteredConstraintOption is an interface for options when dropping clustered constraints.
23+
type DropClusteredConstraintOption interface {
24+
node()
25+
dropClusteredConstraintOption()
26+
}
27+
28+
// DropClusteredConstraintStateOption represents an ON/OFF option like ONLINE = ON.
29+
type DropClusteredConstraintStateOption struct {
30+
OptionKind string
31+
OptionState string
32+
}
33+
34+
func (*DropClusteredConstraintStateOption) node() {}
35+
func (*DropClusteredConstraintStateOption) dropClusteredConstraintOption() {}
36+
37+
// DropClusteredConstraintMoveOption represents a MOVE TO option.
38+
type DropClusteredConstraintMoveOption struct {
39+
OptionKind string
40+
OptionValue *FileGroupOrPartitionScheme
41+
}
42+
43+
func (*DropClusteredConstraintMoveOption) node() {}
44+
func (*DropClusteredConstraintMoveOption) dropClusteredConstraintOption() {}
45+
46+
// DropClusteredConstraintValueOption represents a value option like MAXDOP = 21.
47+
type DropClusteredConstraintValueOption struct {
48+
OptionKind string
49+
OptionValue ScalarExpression
50+
}
51+
52+
func (*DropClusteredConstraintValueOption) node() {}
53+
func (*DropClusteredConstraintValueOption) dropClusteredConstraintOption() {}
54+
55+
// FileGroupOrPartitionScheme represents a filegroup or partition scheme reference.
56+
type FileGroupOrPartitionScheme struct {
57+
Name *IdentifierOrValueExpression
58+
PartitionSchemeColumns []*Identifier
59+
}
60+
61+
func (*FileGroupOrPartitionScheme) node() {}

parser/marshal.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,10 +601,65 @@ func alterTableDropTableElementToJSON(e *ast.AlterTableDropTableElement) jsonNod
601601
if e.Name != nil {
602602
node["Name"] = identifierToJSON(e.Name)
603603
}
604+
if len(e.DropClusteredConstraintOptions) > 0 {
605+
options := make([]jsonNode, len(e.DropClusteredConstraintOptions))
606+
for i, o := range e.DropClusteredConstraintOptions {
607+
options[i] = dropClusteredConstraintOptionToJSON(o)
608+
}
609+
node["DropClusteredConstraintOptions"] = options
610+
}
604611
node["IsIfExists"] = e.IsIfExists
605612
return node
606613
}
607614

615+
func dropClusteredConstraintOptionToJSON(o ast.DropClusteredConstraintOption) jsonNode {
616+
switch opt := o.(type) {
617+
case *ast.DropClusteredConstraintStateOption:
618+
return jsonNode{
619+
"$type": "DropClusteredConstraintStateOption",
620+
"OptionState": opt.OptionState,
621+
"OptionKind": opt.OptionKind,
622+
}
623+
case *ast.DropClusteredConstraintMoveOption:
624+
node := jsonNode{
625+
"$type": "DropClusteredConstraintMoveOption",
626+
"OptionKind": opt.OptionKind,
627+
}
628+
if opt.OptionValue != nil {
629+
node["OptionValue"] = fileGroupOrPartitionSchemeToJSON(opt.OptionValue)
630+
}
631+
return node
632+
case *ast.DropClusteredConstraintValueOption:
633+
node := jsonNode{
634+
"$type": "DropClusteredConstraintValueOption",
635+
"OptionKind": opt.OptionKind,
636+
}
637+
if opt.OptionValue != nil {
638+
node["OptionValue"] = scalarExpressionToJSON(opt.OptionValue)
639+
}
640+
return node
641+
default:
642+
return jsonNode{"$type": "UnknownDropClusteredConstraintOption"}
643+
}
644+
}
645+
646+
func fileGroupOrPartitionSchemeToJSON(fg *ast.FileGroupOrPartitionScheme) jsonNode {
647+
node := jsonNode{
648+
"$type": "FileGroupOrPartitionScheme",
649+
}
650+
if fg.Name != nil {
651+
node["Name"] = identifierOrValueExpressionToJSON(fg.Name)
652+
}
653+
if len(fg.PartitionSchemeColumns) > 0 {
654+
cols := make([]jsonNode, len(fg.PartitionSchemeColumns))
655+
for i, c := range fg.PartitionSchemeColumns {
656+
cols[i] = identifierToJSON(c)
657+
}
658+
node["PartitionSchemeColumns"] = cols
659+
}
660+
return node
661+
}
662+
608663
func alterTableAlterIndexStatementToJSON(s *ast.AlterTableAlterIndexStatement) jsonNode {
609664
node := jsonNode{
610665
"$type": "AlterTableAlterIndexStatement",

parser/parse_ddl.go

Lines changed: 131 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2149,7 +2149,7 @@ func (p *Parser) parseAlterTableDropStatement(tableName *ast.SchemaObjectName) (
21492149
}
21502150

21512151
// Parse multiple elements separated by commas
2152-
// Format: DROP [COLUMN] name, [CONSTRAINT] name, [INDEX] name, ...
2152+
// Format: DROP [COLUMN] name [WITH (options)], [CONSTRAINT] name [WITH (options)], ...
21532153
var currentElementType string = "NotSpecified"
21542154

21552155
for {
@@ -2179,6 +2179,16 @@ func (p *Parser) parseAlterTableDropStatement(tableName *ast.SchemaObjectName) (
21792179
Name: p.parseIdentifier(),
21802180
IsIfExists: false,
21812181
}
2182+
2183+
// Check for WITH clause
2184+
if p.curTok.Type == TokenWith {
2185+
options, err := p.parseDropClusteredConstraintOptions()
2186+
if err != nil {
2187+
return nil, err
2188+
}
2189+
element.DropClusteredConstraintOptions = options
2190+
}
2191+
21822192
stmt.AlterTableDropTableElements = append(stmt.AlterTableDropTableElements, element)
21832193

21842194
// After adding an element, reset type to NotSpecified for next element
@@ -2201,6 +2211,126 @@ func (p *Parser) parseAlterTableDropStatement(tableName *ast.SchemaObjectName) (
22012211
return stmt, nil
22022212
}
22032213

2214+
func (p *Parser) parseDropClusteredConstraintOptions() ([]ast.DropClusteredConstraintOption, error) {
2215+
// Consume WITH
2216+
p.nextToken()
2217+
2218+
if p.curTok.Type != TokenLParen {
2219+
return nil, fmt.Errorf("expected ( after WITH, got %s", p.curTok.Literal)
2220+
}
2221+
p.nextToken() // consume (
2222+
2223+
var options []ast.DropClusteredConstraintOption
2224+
2225+
for p.curTok.Type != TokenRParen && p.curTok.Type != TokenEOF {
2226+
optionName := strings.ToUpper(p.curTok.Literal)
2227+
2228+
switch optionName {
2229+
case "ONLINE":
2230+
p.nextToken() // consume ONLINE
2231+
if p.curTok.Type != TokenEquals {
2232+
return nil, fmt.Errorf("expected = after ONLINE, got %s", p.curTok.Literal)
2233+
}
2234+
p.nextToken() // consume =
2235+
state := strings.ToUpper(p.curTok.Literal)
2236+
var optionState string
2237+
if state == "ON" {
2238+
optionState = "On"
2239+
} else if state == "OFF" {
2240+
optionState = "Off"
2241+
} else {
2242+
return nil, fmt.Errorf("expected ON or OFF after ONLINE =, got %s", p.curTok.Literal)
2243+
}
2244+
p.nextToken() // consume ON/OFF
2245+
options = append(options, &ast.DropClusteredConstraintStateOption{
2246+
OptionKind: "Online",
2247+
OptionState: optionState,
2248+
})
2249+
2250+
case "MOVE":
2251+
p.nextToken() // consume MOVE
2252+
if strings.ToUpper(p.curTok.Literal) != "TO" {
2253+
return nil, fmt.Errorf("expected TO after MOVE, got %s", p.curTok.Literal)
2254+
}
2255+
p.nextToken() // consume TO
2256+
2257+
fg, err := p.parseFileGroupOrPartitionScheme()
2258+
if err != nil {
2259+
return nil, err
2260+
}
2261+
options = append(options, &ast.DropClusteredConstraintMoveOption{
2262+
OptionKind: "MoveTo",
2263+
OptionValue: fg,
2264+
})
2265+
2266+
case "MAXDOP":
2267+
p.nextToken() // consume MAXDOP
2268+
if p.curTok.Type != TokenEquals {
2269+
return nil, fmt.Errorf("expected = after MAXDOP, got %s", p.curTok.Literal)
2270+
}
2271+
p.nextToken() // consume =
2272+
if p.curTok.Type != TokenNumber {
2273+
return nil, fmt.Errorf("expected number after MAXDOP =, got %s", p.curTok.Literal)
2274+
}
2275+
options = append(options, &ast.DropClusteredConstraintValueOption{
2276+
OptionKind: "MaxDop",
2277+
OptionValue: &ast.IntegerLiteral{
2278+
LiteralType: "Integer",
2279+
Value: p.curTok.Literal,
2280+
},
2281+
})
2282+
p.nextToken() // consume number
2283+
2284+
default:
2285+
return nil, fmt.Errorf("unexpected option in DROP WITH clause: %s", p.curTok.Literal)
2286+
}
2287+
2288+
// Check for comma or end of options
2289+
if p.curTok.Type == TokenComma {
2290+
p.nextToken() // consume comma
2291+
}
2292+
}
2293+
2294+
if p.curTok.Type != TokenRParen {
2295+
return nil, fmt.Errorf("expected ) to close WITH options, got %s", p.curTok.Literal)
2296+
}
2297+
p.nextToken() // consume )
2298+
2299+
return options, nil
2300+
}
2301+
2302+
func (p *Parser) parseFileGroupOrPartitionScheme() (*ast.FileGroupOrPartitionScheme, error) {
2303+
fg := &ast.FileGroupOrPartitionScheme{}
2304+
2305+
// Parse filegroup/partition scheme name (can be identifier or string literal)
2306+
iove, err := p.parseIdentifierOrValueExpression()
2307+
if err != nil {
2308+
return nil, err
2309+
}
2310+
fg.Name = iove
2311+
2312+
// Check for partition scheme columns (column1, column2, ...)
2313+
if p.curTok.Type == TokenLParen {
2314+
p.nextToken() // consume (
2315+
for p.curTok.Type != TokenRParen && p.curTok.Type != TokenEOF {
2316+
if p.curTok.Type != TokenIdent && p.curTok.Type != TokenLBracket {
2317+
return nil, fmt.Errorf("expected column identifier in partition scheme, got %s", p.curTok.Literal)
2318+
}
2319+
fg.PartitionSchemeColumns = append(fg.PartitionSchemeColumns, p.parseIdentifier())
2320+
2321+
if p.curTok.Type == TokenComma {
2322+
p.nextToken() // consume comma
2323+
}
2324+
}
2325+
if p.curTok.Type != TokenRParen {
2326+
return nil, fmt.Errorf("expected ) to close partition scheme columns, got %s", p.curTok.Literal)
2327+
}
2328+
p.nextToken() // consume )
2329+
}
2330+
2331+
return fg, nil
2332+
}
2333+
22042334
func (p *Parser) parseAlterTableAlterIndexStatement(tableName *ast.SchemaObjectName) (*ast.AlterTableAlterIndexStatement, error) {
22052335
// Consume ALTER
22062336
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)