Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 46 additions & 4 deletions ast/alter_table_drop_table_element_statement.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package ast

// AlterTableDropTableElementStatement represents an ALTER TABLE ... DROP statement.
type AlterTableDropTableElementStatement struct {
SchemaObjectName *SchemaObjectName
SchemaObjectName *SchemaObjectName
AlterTableDropTableElements []*AlterTableDropTableElement
}

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

// AlterTableDropTableElement represents an element being dropped from a table.
type AlterTableDropTableElement struct {
TableElementType string
Name *Identifier
IsIfExists bool
TableElementType string
Name *Identifier
IsIfExists bool
DropClusteredConstraintOptions []DropClusteredConstraintOption
}

func (*AlterTableDropTableElement) node() {}

// DropClusteredConstraintOption is an interface for options when dropping clustered constraints.
type DropClusteredConstraintOption interface {
node()
dropClusteredConstraintOption()
}

// DropClusteredConstraintStateOption represents an ON/OFF option like ONLINE = ON.
type DropClusteredConstraintStateOption struct {
OptionKind string
OptionState string
}

func (*DropClusteredConstraintStateOption) node() {}
func (*DropClusteredConstraintStateOption) dropClusteredConstraintOption() {}

// DropClusteredConstraintMoveOption represents a MOVE TO option.
type DropClusteredConstraintMoveOption struct {
OptionKind string
OptionValue *FileGroupOrPartitionScheme
}

func (*DropClusteredConstraintMoveOption) node() {}
func (*DropClusteredConstraintMoveOption) dropClusteredConstraintOption() {}

// DropClusteredConstraintValueOption represents a value option like MAXDOP = 21.
type DropClusteredConstraintValueOption struct {
OptionKind string
OptionValue ScalarExpression
}

func (*DropClusteredConstraintValueOption) node() {}
func (*DropClusteredConstraintValueOption) dropClusteredConstraintOption() {}

// FileGroupOrPartitionScheme represents a filegroup or partition scheme reference.
type FileGroupOrPartitionScheme struct {
Name *IdentifierOrValueExpression
PartitionSchemeColumns []*Identifier
}

func (*FileGroupOrPartitionScheme) node() {}
55 changes: 55 additions & 0 deletions parser/marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -601,10 +601,65 @@ func alterTableDropTableElementToJSON(e *ast.AlterTableDropTableElement) jsonNod
if e.Name != nil {
node["Name"] = identifierToJSON(e.Name)
}
if len(e.DropClusteredConstraintOptions) > 0 {
options := make([]jsonNode, len(e.DropClusteredConstraintOptions))
for i, o := range e.DropClusteredConstraintOptions {
options[i] = dropClusteredConstraintOptionToJSON(o)
}
node["DropClusteredConstraintOptions"] = options
}
node["IsIfExists"] = e.IsIfExists
return node
}

func dropClusteredConstraintOptionToJSON(o ast.DropClusteredConstraintOption) jsonNode {
switch opt := o.(type) {
case *ast.DropClusteredConstraintStateOption:
return jsonNode{
"$type": "DropClusteredConstraintStateOption",
"OptionState": opt.OptionState,
"OptionKind": opt.OptionKind,
}
case *ast.DropClusteredConstraintMoveOption:
node := jsonNode{
"$type": "DropClusteredConstraintMoveOption",
"OptionKind": opt.OptionKind,
}
if opt.OptionValue != nil {
node["OptionValue"] = fileGroupOrPartitionSchemeToJSON(opt.OptionValue)
}
return node
case *ast.DropClusteredConstraintValueOption:
node := jsonNode{
"$type": "DropClusteredConstraintValueOption",
"OptionKind": opt.OptionKind,
}
if opt.OptionValue != nil {
node["OptionValue"] = scalarExpressionToJSON(opt.OptionValue)
}
return node
default:
return jsonNode{"$type": "UnknownDropClusteredConstraintOption"}
}
}

func fileGroupOrPartitionSchemeToJSON(fg *ast.FileGroupOrPartitionScheme) jsonNode {
node := jsonNode{
"$type": "FileGroupOrPartitionScheme",
}
if fg.Name != nil {
node["Name"] = identifierOrValueExpressionToJSON(fg.Name)
}
if len(fg.PartitionSchemeColumns) > 0 {
cols := make([]jsonNode, len(fg.PartitionSchemeColumns))
for i, c := range fg.PartitionSchemeColumns {
cols[i] = identifierToJSON(c)
}
node["PartitionSchemeColumns"] = cols
}
return node
}

func alterTableAlterIndexStatementToJSON(s *ast.AlterTableAlterIndexStatement) jsonNode {
node := jsonNode{
"$type": "AlterTableAlterIndexStatement",
Expand Down
132 changes: 131 additions & 1 deletion parser/parse_ddl.go
Original file line number Diff line number Diff line change
Expand Up @@ -2149,7 +2149,7 @@ func (p *Parser) parseAlterTableDropStatement(tableName *ast.SchemaObjectName) (
}

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

for {
Expand Down Expand Up @@ -2179,6 +2179,16 @@ func (p *Parser) parseAlterTableDropStatement(tableName *ast.SchemaObjectName) (
Name: p.parseIdentifier(),
IsIfExists: false,
}

// Check for WITH clause
if p.curTok.Type == TokenWith {
options, err := p.parseDropClusteredConstraintOptions()
if err != nil {
return nil, err
}
element.DropClusteredConstraintOptions = options
}

stmt.AlterTableDropTableElements = append(stmt.AlterTableDropTableElements, element)

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

func (p *Parser) parseDropClusteredConstraintOptions() ([]ast.DropClusteredConstraintOption, error) {
// Consume WITH
p.nextToken()

if p.curTok.Type != TokenLParen {
return nil, fmt.Errorf("expected ( after WITH, got %s", p.curTok.Literal)
}
p.nextToken() // consume (

var options []ast.DropClusteredConstraintOption

for p.curTok.Type != TokenRParen && p.curTok.Type != TokenEOF {
optionName := strings.ToUpper(p.curTok.Literal)

switch optionName {
case "ONLINE":
p.nextToken() // consume ONLINE
if p.curTok.Type != TokenEquals {
return nil, fmt.Errorf("expected = after ONLINE, got %s", p.curTok.Literal)
}
p.nextToken() // consume =
state := strings.ToUpper(p.curTok.Literal)
var optionState string
if state == "ON" {
optionState = "On"
} else if state == "OFF" {
optionState = "Off"
} else {
return nil, fmt.Errorf("expected ON or OFF after ONLINE =, got %s", p.curTok.Literal)
}
p.nextToken() // consume ON/OFF
options = append(options, &ast.DropClusteredConstraintStateOption{
OptionKind: "Online",
OptionState: optionState,
})

case "MOVE":
p.nextToken() // consume MOVE
if strings.ToUpper(p.curTok.Literal) != "TO" {
return nil, fmt.Errorf("expected TO after MOVE, got %s", p.curTok.Literal)
}
p.nextToken() // consume TO

fg, err := p.parseFileGroupOrPartitionScheme()
if err != nil {
return nil, err
}
options = append(options, &ast.DropClusteredConstraintMoveOption{
OptionKind: "MoveTo",
OptionValue: fg,
})

case "MAXDOP":
p.nextToken() // consume MAXDOP
if p.curTok.Type != TokenEquals {
return nil, fmt.Errorf("expected = after MAXDOP, got %s", p.curTok.Literal)
}
p.nextToken() // consume =
if p.curTok.Type != TokenNumber {
return nil, fmt.Errorf("expected number after MAXDOP =, got %s", p.curTok.Literal)
}
options = append(options, &ast.DropClusteredConstraintValueOption{
OptionKind: "MaxDop",
OptionValue: &ast.IntegerLiteral{
LiteralType: "Integer",
Value: p.curTok.Literal,
},
})
p.nextToken() // consume number

default:
return nil, fmt.Errorf("unexpected option in DROP WITH clause: %s", p.curTok.Literal)
}

// Check for comma or end of options
if p.curTok.Type == TokenComma {
p.nextToken() // consume comma
}
}

if p.curTok.Type != TokenRParen {
return nil, fmt.Errorf("expected ) to close WITH options, got %s", p.curTok.Literal)
}
p.nextToken() // consume )

return options, nil
}

func (p *Parser) parseFileGroupOrPartitionScheme() (*ast.FileGroupOrPartitionScheme, error) {
fg := &ast.FileGroupOrPartitionScheme{}

// Parse filegroup/partition scheme name (can be identifier or string literal)
iove, err := p.parseIdentifierOrValueExpression()
if err != nil {
return nil, err
}
fg.Name = iove

// Check for partition scheme columns (column1, column2, ...)
if p.curTok.Type == TokenLParen {
p.nextToken() // consume (
for p.curTok.Type != TokenRParen && p.curTok.Type != TokenEOF {
if p.curTok.Type != TokenIdent && p.curTok.Type != TokenLBracket {
return nil, fmt.Errorf("expected column identifier in partition scheme, got %s", p.curTok.Literal)
}
fg.PartitionSchemeColumns = append(fg.PartitionSchemeColumns, p.parseIdentifier())

if p.curTok.Type == TokenComma {
p.nextToken() // consume comma
}
}
if p.curTok.Type != TokenRParen {
return nil, fmt.Errorf("expected ) to close partition scheme columns, got %s", p.curTok.Literal)
}
p.nextToken() // consume )
}

return fg, nil
}

func (p *Parser) parseAlterTableAlterIndexStatement(tableName *ast.SchemaObjectName) (*ast.AlterTableAlterIndexStatement, error) {
// Consume ALTER
p.nextToken()
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo": true}
{}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"todo": true}
{}
Loading