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
131 changes: 131 additions & 0 deletions pkg/sql/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,49 @@ func nodifyExpressions(exprs []Expression) []Node {
return nodes
}

// RollupExpression represents ROLLUP(col1, col2, ...) in GROUP BY clause
// ROLLUP generates hierarchical grouping sets from right to left
// Example: ROLLUP(a, b, c) generates grouping sets:
//
// (a, b, c), (a, b), (a), ()
type RollupExpression struct {
Expressions []Expression
}

func (r *RollupExpression) expressionNode() {}
func (r RollupExpression) TokenLiteral() string { return "ROLLUP" }
func (r RollupExpression) Children() []Node { return nodifyExpressions(r.Expressions) }

// CubeExpression represents CUBE(col1, col2, ...) in GROUP BY clause
// CUBE generates all possible combinations of grouping sets
// Example: CUBE(a, b) generates grouping sets:
//
// (a, b), (a), (b), ()
type CubeExpression struct {
Expressions []Expression
}

func (c *CubeExpression) expressionNode() {}
func (c CubeExpression) TokenLiteral() string { return "CUBE" }
func (c CubeExpression) Children() []Node { return nodifyExpressions(c.Expressions) }

// GroupingSetsExpression represents GROUPING SETS(...) in GROUP BY clause
// Allows explicit specification of grouping sets
// Example: GROUPING SETS((a, b), (a), ())
type GroupingSetsExpression struct {
Sets [][]Expression // Each inner slice is one grouping set
}

func (g *GroupingSetsExpression) expressionNode() {}
func (g GroupingSetsExpression) TokenLiteral() string { return "GROUPING SETS" }
func (g GroupingSetsExpression) Children() []Node {
children := make([]Node, 0)
for _, set := range g.Sets {
children = append(children, nodifyExpressions(set)...)
}
return children
}

// Identifier represents a column or table name
type Identifier struct {
Name string
Expand Down Expand Up @@ -845,6 +888,94 @@ func (i *IndexColumn) expressionNode() {}
func (i IndexColumn) TokenLiteral() string { return i.Column }
func (i IndexColumn) Children() []Node { return nil }

// MergeStatement represents a MERGE statement (SQL:2003 F312)
// Syntax: MERGE INTO target USING source ON condition
//
// WHEN MATCHED THEN UPDATE/DELETE
// WHEN NOT MATCHED THEN INSERT
// WHEN NOT MATCHED BY SOURCE THEN UPDATE/DELETE
type MergeStatement struct {
TargetTable TableReference // The table being merged into
TargetAlias string // Optional alias for target
SourceTable TableReference // The source table or subquery
SourceAlias string // Optional alias for source
OnCondition Expression // The join/match condition
WhenClauses []*MergeWhenClause // List of WHEN clauses
}

func (m *MergeStatement) statementNode() {}
func (m MergeStatement) TokenLiteral() string { return "MERGE" }
func (m MergeStatement) Children() []Node {
children := []Node{&m.TargetTable, &m.SourceTable}
if m.OnCondition != nil {
children = append(children, m.OnCondition)
}
for _, when := range m.WhenClauses {
children = append(children, when)
}
return children
}

// MergeWhenClause represents a WHEN clause in a MERGE statement
// Types: MATCHED, NOT_MATCHED, NOT_MATCHED_BY_SOURCE
type MergeWhenClause struct {
Type string // "MATCHED", "NOT_MATCHED", "NOT_MATCHED_BY_SOURCE"
Condition Expression // Optional AND condition
Action *MergeAction // The action to perform (UPDATE/INSERT/DELETE)
}

func (w *MergeWhenClause) expressionNode() {}
func (w MergeWhenClause) TokenLiteral() string { return "WHEN " + w.Type }
func (w MergeWhenClause) Children() []Node {
children := make([]Node, 0)
if w.Condition != nil {
children = append(children, w.Condition)
}
if w.Action != nil {
children = append(children, w.Action)
}
return children
}

// MergeAction represents the action in a WHEN clause
// ActionType: UPDATE, INSERT, DELETE
type MergeAction struct {
ActionType string // "UPDATE", "INSERT", "DELETE"
SetClauses []SetClause // For UPDATE: SET column = value pairs
Columns []string // For INSERT: column list
Values []Expression // For INSERT: value list
DefaultValues bool // For INSERT: use DEFAULT VALUES
}

func (a *MergeAction) expressionNode() {}
func (a MergeAction) TokenLiteral() string { return a.ActionType }
func (a MergeAction) Children() []Node {
children := make([]Node, 0)
for _, set := range a.SetClauses {
set := set // G601: Create local copy
children = append(children, &set)
}
for _, val := range a.Values {
children = append(children, val)
}
return children
}

// SetClause represents a SET clause in UPDATE (also used in MERGE UPDATE)
type SetClause struct {
Column string
Value Expression
}

func (s *SetClause) expressionNode() {}
func (s SetClause) TokenLiteral() string { return s.Column }
func (s SetClause) Children() []Node {
if s.Value != nil {
return []Node{s.Value}
}
return nil
}

// AST represents the root of the Abstract Syntax Tree
type AST struct {
Statements []Statement
Expand Down
17 changes: 11 additions & 6 deletions pkg/sql/keywords/categories.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,21 @@ func (k *Keywords) initialize() {
"NULLS": models.TokenTypeKeyword,
"FIRST": models.TokenTypeKeyword,
"LAST": models.TokenTypeKeyword,
"ROLLUP": models.TokenTypeKeyword, // SQL-99 grouping operation
"CUBE": models.TokenTypeKeyword, // SQL-99 grouping operation
"GROUPING": models.TokenTypeKeyword, // SQL-99 GROUPING SETS
"SETS": models.TokenTypeKeyword, // SQL-99 GROUPING SETS
}

// Initialize compound keywords
k.CompoundKeywords = map[string]models.TokenType{
"FULL JOIN": models.TokenTypeKeyword,
"CROSS JOIN": models.TokenTypeKeyword,
"NATURAL JOIN": models.TokenTypeKeyword,
"GROUP BY": models.TokenTypeKeyword,
"ORDER BY": models.TokenTypeKeyword,
"LEFT JOIN": models.TokenTypeKeyword,
"FULL JOIN": models.TokenTypeKeyword,
"CROSS JOIN": models.TokenTypeKeyword,
"NATURAL JOIN": models.TokenTypeKeyword,
"GROUP BY": models.TokenTypeKeyword,
"ORDER BY": models.TokenTypeKeyword,
"LEFT JOIN": models.TokenTypeKeyword,
"GROUPING SETS": models.TokenTypeKeyword, // SQL-99 grouping operation
}

// Add all keywords to the main keyword map
Expand Down
12 changes: 12 additions & 0 deletions pkg/sql/keywords/keywords.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,17 @@ var ADDITIONAL_KEYWORDS = []Keyword{
{Word: "LEAD", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false},
{Word: "FIRST_VALUE", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false},
{Word: "LAST_VALUE", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false},
// SQL-99 grouping operations
{Word: "ROLLUP", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: false},
{Word: "CUBE", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: false},
{Word: "GROUPING", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: false},
{Word: "SETS", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: false},
// MERGE statement keywords (SQL:2003 F312)
{Word: "MERGE", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true},
{Word: "USING", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: true},
{Word: "MATCHED", Type: models.TokenTypeKeyword, Reserved: true, ReservedForTableAlias: false},
{Word: "SOURCE", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false},
{Word: "TARGET", Type: models.TokenTypeKeyword, Reserved: false, ReservedForTableAlias: false},
}

// addKeywordsWithCategory is a helper method to add multiple keywords
Expand All @@ -130,6 +141,7 @@ func New(dialect SQLDialect, ignoreCase bool) *Keywords {
k.CompoundKeywords["FULL JOIN"] = models.TokenTypeKeyword
k.CompoundKeywords["CROSS JOIN"] = models.TokenTypeKeyword
k.CompoundKeywords["NATURAL JOIN"] = models.TokenTypeKeyword
k.CompoundKeywords["GROUPING SETS"] = models.TokenTypeKeyword // SQL-99 grouping operation

// Add standard keywords
k.addKeywordsWithCategory(RESERVED_FOR_TABLE_ALIAS)
Expand Down
Loading
Loading