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
37 changes: 37 additions & 0 deletions ast/alter_table_set_statement.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package ast

// AlterTableSetStatement represents ALTER TABLE ... SET statement
type AlterTableSetStatement struct {
SchemaObjectName *SchemaObjectName
Options []TableOption
}

func (s *AlterTableSetStatement) statement() {}
func (s *AlterTableSetStatement) node() {}

// TableOption is an interface for table options
type TableOption interface {
Node
tableOption()
}

// SystemVersioningTableOption represents SYSTEM_VERSIONING option
type SystemVersioningTableOption struct {
OptionState string // "On", "Off"
ConsistencyCheckEnabled string // "On", "Off", "NotSet"
HistoryTable *SchemaObjectName
RetentionPeriod *RetentionPeriodDefinition
OptionKind string // Always "LockEscalation"
}

func (o *SystemVersioningTableOption) tableOption() {}
func (o *SystemVersioningTableOption) node() {}

// RetentionPeriodDefinition represents the history retention period
type RetentionPeriodDefinition struct {
Duration ScalarExpression
Units string // "Day", "Week", "Month", "Months", "Year"
IsInfinity bool
}

func (r *RetentionPeriodDefinition) node() {}
56 changes: 56 additions & 0 deletions parser/marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,8 @@ func statementToJSON(stmt ast.Statement) jsonNode {
return alterTableSwitchStatementToJSON(s)
case *ast.AlterTableConstraintModificationStatement:
return alterTableConstraintModificationStatementToJSON(s)
case *ast.AlterTableSetStatement:
return alterTableSetStatementToJSON(s)
case *ast.InsertBulkStatement:
return insertBulkStatementToJSON(s)
case *ast.BulkInsertStatement:
Expand Down Expand Up @@ -6137,6 +6139,60 @@ func alterTableConstraintModificationStatementToJSON(s *ast.AlterTableConstraint
return node
}

func alterTableSetStatementToJSON(s *ast.AlterTableSetStatement) jsonNode {
node := jsonNode{
"$type": "AlterTableSetStatement",
}
if len(s.Options) > 0 {
var options []jsonNode
for _, opt := range s.Options {
options = append(options, tableOptionToJSON(opt))
}
node["Options"] = options
}
if s.SchemaObjectName != nil {
node["SchemaObjectName"] = schemaObjectNameToJSON(s.SchemaObjectName)
}
return node
}

func tableOptionToJSON(opt ast.TableOption) jsonNode {
switch o := opt.(type) {
case *ast.SystemVersioningTableOption:
return systemVersioningTableOptionToJSON(o)
default:
return jsonNode{"$type": "UnknownTableOption"}
}
}

func systemVersioningTableOptionToJSON(o *ast.SystemVersioningTableOption) jsonNode {
node := jsonNode{
"$type": "SystemVersioningTableOption",
"OptionState": o.OptionState,
"ConsistencyCheckEnabled": o.ConsistencyCheckEnabled,
}
if o.HistoryTable != nil {
node["HistoryTable"] = schemaObjectNameToJSON(o.HistoryTable)
}
if o.RetentionPeriod != nil {
node["RetentionPeriod"] = retentionPeriodDefinitionToJSON(o.RetentionPeriod)
}
node["OptionKind"] = o.OptionKind
return node
}

func retentionPeriodDefinitionToJSON(r *ast.RetentionPeriodDefinition) jsonNode {
node := jsonNode{
"$type": "RetentionPeriodDefinition",
}
if r.Duration != nil {
node["Duration"] = scalarExpressionToJSON(r.Duration)
}
node["Units"] = r.Units
node["IsInfinity"] = r.IsInfinity
return node
}

func createExternalDataSourceStatementToJSON(s *ast.CreateExternalDataSourceStatement) jsonNode {
node := jsonNode{
"$type": "CreateExternalDataSourceStatement",
Expand Down
171 changes: 171 additions & 0 deletions parser/parse_ddl.go
Original file line number Diff line number Diff line change
Expand Up @@ -2132,6 +2132,11 @@ func (p *Parser) parseAlterTableStatement() (ast.Statement, error) {
return p.parseAlterTableConstraintModificationStatement(tableName)
}

// Check for SET
if strings.ToUpper(p.curTok.Literal) == "SET" {
return p.parseAlterTableSetStatement(tableName)
}

return nil, fmt.Errorf("unexpected token in ALTER TABLE: %s", p.curTok.Literal)
}

Expand Down Expand Up @@ -2685,6 +2690,172 @@ func (p *Parser) parseAlterTableConstraintModificationStatement(tableName *ast.S
return stmt, nil
}

func (p *Parser) parseAlterTableSetStatement(tableName *ast.SchemaObjectName) (*ast.AlterTableSetStatement, error) {
stmt := &ast.AlterTableSetStatement{
SchemaObjectName: tableName,
}

// Consume SET
p.nextToken()

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

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

if optionName == "SYSTEM_VERSIONING" {
opt, err := p.parseSystemVersioningTableOption()
if err != nil {
return nil, err
}
stmt.Options = append(stmt.Options, opt)
}

if p.curTok.Type == TokenComma {
p.nextToken()
}
}

// Consume )
if p.curTok.Type == TokenRParen {
p.nextToken()
}

// Skip optional semicolon
if p.curTok.Type == TokenSemicolon {
p.nextToken()
}

return stmt, nil
}

func (p *Parser) parseSystemVersioningTableOption() (*ast.SystemVersioningTableOption, error) {
opt := &ast.SystemVersioningTableOption{
OptionKind: "LockEscalation",
ConsistencyCheckEnabled: "NotSet",
}

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

// Parse ON or OFF
stateVal := strings.ToUpper(p.curTok.Literal)
if stateVal == "ON" {
opt.OptionState = "On"
} else if stateVal == "OFF" {
opt.OptionState = "Off"
} else {
return nil, fmt.Errorf("expected ON or OFF after =, got %s", p.curTok.Literal)
}
p.nextToken()

// Check for optional sub-options in parentheses
if p.curTok.Type == TokenLParen {
p.nextToken()

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

if p.curTok.Type == TokenEquals {
p.nextToken()
}

switch subOptName {
case "HISTORY_TABLE":
histTable, err := p.parseSchemaObjectName()
if err != nil {
return nil, err
}
opt.HistoryTable = histTable

case "DATA_CONSISTENCY_CHECK":
checkVal := strings.ToUpper(p.curTok.Literal)
if checkVal == "ON" {
opt.ConsistencyCheckEnabled = "On"
} else if checkVal == "OFF" {
opt.ConsistencyCheckEnabled = "Off"
}
p.nextToken()

case "HISTORY_RETENTION_PERIOD":
retPeriod, err := p.parseRetentionPeriodDefinition()
if err != nil {
return nil, err
}
opt.RetentionPeriod = retPeriod
}

if p.curTok.Type == TokenComma {
p.nextToken()
}
}

// Consume )
if p.curTok.Type == TokenRParen {
p.nextToken()
}
}

return opt, nil
}

func (p *Parser) parseRetentionPeriodDefinition() (*ast.RetentionPeriodDefinition, error) {
ret := &ast.RetentionPeriodDefinition{}

// Check for INFINITE
if strings.ToUpper(p.curTok.Literal) == "INFINITE" {
ret.IsInfinity = true
ret.Units = "Day" // Default unit for INFINITE
p.nextToken()
return ret, nil
}

// Parse numeric duration
ret.IsInfinity = false

// Parse integer literal
if p.curTok.Type == TokenNumber {
lit := &ast.IntegerLiteral{
LiteralType: "Integer",
Value: p.curTok.Literal,
}
ret.Duration = lit
p.nextToken()
} else {
return nil, fmt.Errorf("expected number for retention period, got %s", p.curTok.Literal)
}

// Parse unit
unitVal := strings.ToUpper(p.curTok.Literal)
switch unitVal {
case "DAY", "DAYS":
ret.Units = "Day"
case "WEEK", "WEEKS":
ret.Units = "Week"
case "MONTH":
ret.Units = "Month"
case "MONTHS":
ret.Units = "Months"
case "YEAR", "YEARS":
ret.Units = "Year"
default:
return nil, fmt.Errorf("unexpected unit %s for retention period", unitVal)
}
p.nextToken()

return ret, nil
}

func (p *Parser) parseAlterRoleStatement() (*ast.AlterRoleStatement, error) {
// Consume ROLE
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