diff --git a/parser/ast.go b/parser/ast.go index 9169d46..8732a79 100644 --- a/parser/ast.go +++ b/parser/ast.go @@ -356,6 +356,106 @@ func (a *AlterTableDropPartition) Accept(visitor ASTVisitor) error { return visitor.VisitAlterTableDropPartition(a) } +type AlterTableMaterializeProjection struct { + MaterializedPos Pos + StatementEnd Pos + IfExists bool + ProjectionName *NestedIdentifier + Partition *PartitionExpr +} + +func (a *AlterTableMaterializeProjection) Pos() Pos { + return a.MaterializedPos +} + +func (a *AlterTableMaterializeProjection) End() Pos { + return a.StatementEnd +} + +func (a *AlterTableMaterializeProjection) AlterType() string { + return "MATERIALIZE_PROJECTION" +} + +func (a *AlterTableMaterializeProjection) String(level int) string { + var builder strings.Builder + builder.WriteString("MATERIALIZE PROJECTION") + + if a.IfExists { + builder.WriteString(" IF EXISTS") + } + builder.WriteString(" ") + builder.WriteString(a.ProjectionName.String(level)) + if a.Partition != nil { + builder.WriteString(" IN ") + builder.WriteString(a.Partition.String(level)) + } + return builder.String() +} + +func (a *AlterTableMaterializeProjection) Accept(visitor ASTVisitor) error { + visitor.enter(a) + defer visitor.leave(a) + if err := a.ProjectionName.Accept(visitor); err != nil { + return err + } + if a.Partition != nil { + if err := a.Partition.Accept(visitor); err != nil { + return err + } + } + return visitor.VisitAlterTableMaterializeProjection(a) +} + +type AlterTableMaterializeIndex struct { + MaterializedPos Pos + StatementEnd Pos + IfExists bool + IndexName *NestedIdentifier + Partition *PartitionExpr +} + +func (a *AlterTableMaterializeIndex) Pos() Pos { + return a.MaterializedPos +} + +func (a *AlterTableMaterializeIndex) End() Pos { + return a.StatementEnd +} + +func (a *AlterTableMaterializeIndex) AlterType() string { + return "MATERIALIZE_INDEX" +} + +func (a *AlterTableMaterializeIndex) String(level int) string { + var builder strings.Builder + builder.WriteString("MATERIALIZE INDEX") + + if a.IfExists { + builder.WriteString(" IF EXISTS") + } + builder.WriteString(" ") + builder.WriteString(a.IndexName.String(level)) + if a.Partition != nil { + builder.WriteString(" IN ") + builder.WriteString(a.Partition.String(level)) + } + return builder.String() +} + +func (a *AlterTableMaterializeIndex) Accept(visitor ASTVisitor) error { + visitor.enter(a) + defer visitor.leave(a) + if err := a.IndexName.Accept(visitor); err != nil { + return err + } + if a.Partition != nil { + if err := a.Partition.Accept(visitor); err != nil { + return err + } + } + return visitor.VisitAlterTableMaterializeIndex(a) +} + type AlterTableFreezePartition struct { FreezePos Pos StatementEnd Pos diff --git a/parser/ast_visitor.go b/parser/ast_visitor.go index 3df2a8a..614934b 100644 --- a/parser/ast_visitor.go +++ b/parser/ast_visitor.go @@ -22,6 +22,8 @@ type ASTVisitor interface { VisitAlterTableClearColumn(expr *AlterTableClearColumn) error VisitAlterTableClearIndex(expr *AlterTableClearIndex) error VisitAlterTableClearProjection(expr *AlterTableClearProjection) error + VisitAlterTableMaterializeIndex(expr *AlterTableMaterializeIndex) error + VisitAlterTableMaterializeProjection(expr *AlterTableMaterializeProjection) error VisitAlterTableRenameColumn(expr *AlterTableRenameColumn) error VisitAlterTableModifyTTL(expr *AlterTableModifyTTL) error VisitAlterTableModifyColumn(expr *AlterTableModifyColumn) error @@ -315,6 +317,20 @@ func (v *DefaultASTVisitor) VisitAlterTableClearProjection(expr *AlterTableClear return nil } +func (v *DefaultASTVisitor) VisitAlterTableMaterializeProjection(expr *AlterTableMaterializeProjection) error { + if v.Visit != nil { + return v.Visit(expr) + } + return nil +} + +func (v *DefaultASTVisitor) VisitAlterTableMaterializeIndex(expr *AlterTableMaterializeIndex) error { + if v.Visit != nil { + return v.Visit(expr) + } + return nil +} + func (v *DefaultASTVisitor) VisitAlterTableRenameColumn(expr *AlterTableRenameColumn) error { if v.Visit != nil { return v.Visit(expr) diff --git a/parser/parser_alter.go b/parser/parser_alter.go index dc1762a..20360fc 100644 --- a/parser/parser_alter.go +++ b/parser/parser_alter.go @@ -49,7 +49,8 @@ func (p *Parser) parseAlterTable(pos Pos) (*AlterTable, error) { alterExpr, err = p.parseAlterTableModify(p.Pos()) case p.matchKeyword(KeywordReplace): alterExpr, err = p.parseAlterTableReplacePartition(p.Pos()) - + case p.matchKeyword(KeywordMaterialize): + alterExpr, err = p.parseAlterTableMaterialize(p.Pos()) default: return nil, errors.New("expected token: ADD|DROP|ATTACH|DETACH|FREEZE|REMOVE|CLEAR") } @@ -709,3 +710,53 @@ func (p *Parser) parseAlterTableReplacePartition(pos Pos) (AlterTableExpr, error Table: table, }, nil } + +func (p *Parser) parseAlterTableMaterialize(pos Pos) (AlterTableExpr, error) { + if err := p.consumeKeyword(KeywordMaterialize); err != nil { + return nil, err + } + var kind string + switch { + case p.matchKeyword(KeywordIndex): + kind = KeywordIndex + case p.matchKeyword(KeywordProjection): + kind = KeywordProjection + default: + return nil, fmt.Errorf("expected keyword: INDEX|PROJECTION, but got %q", p.lastTokenKind()) + } + _ = p.lexer.consumeToken() + + ifExists, err := p.tryParseIfExists() + if err != nil { + return nil, err + } + name, err := p.ParseNestedIdentifier(p.Pos()) + if err != nil { + return nil, err + } + statementEnd := name.End() + var partitionExpr *PartitionExpr + if p.tryConsumeKeyword(KeywordIn) != nil { + partitionExpr, err = p.tryParsePartitionExpr(p.Pos()) + if err != nil { + return nil, err + } + statementEnd = partitionExpr.End() + } + if kind == KeywordIndex { + return &AlterTableMaterializeIndex{ + MaterializedPos: pos, + StatementEnd: statementEnd, + IfExists: ifExists, + IndexName: name, + Partition: partitionExpr, + }, nil + } + return &AlterTableMaterializeProjection{ + MaterializedPos: pos, + StatementEnd: statementEnd, + IfExists: ifExists, + ProjectionName: name, + Partition: partitionExpr, + }, nil +} diff --git a/parser/testdata/ddl/alter_table_materialize_index.sql b/parser/testdata/ddl/alter_table_materialize_index.sql new file mode 100644 index 0000000..6fde94a --- /dev/null +++ b/parser/testdata/ddl/alter_table_materialize_index.sql @@ -0,0 +1,2 @@ +ALTER TABLE visits_order MATERIALIZE INDEX IF EXISTS user_name_index IN PARTITION '20240403'; + diff --git a/parser/testdata/ddl/alter_table_materialize_projection.sql b/parser/testdata/ddl/alter_table_materialize_projection.sql new file mode 100644 index 0000000..f2fa4cf --- /dev/null +++ b/parser/testdata/ddl/alter_table_materialize_projection.sql @@ -0,0 +1,2 @@ +ALTER TABLE visits_order MATERIALIZE PROJECTION IF EXISTS user_name_projection IN PARTITION '20240403'; + diff --git a/parser/testdata/ddl/format/alter_table_materialize_index.sql b/parser/testdata/ddl/format/alter_table_materialize_index.sql new file mode 100644 index 0000000..9caa420 --- /dev/null +++ b/parser/testdata/ddl/format/alter_table_materialize_index.sql @@ -0,0 +1,8 @@ +-- Origin SQL: +ALTER TABLE visits_order MATERIALIZE INDEX IF EXISTS user_name_index IN PARTITION '20240403'; + + + +-- Format SQL: +ALTER TABLE visits_order +MATERIALIZE INDEX IF EXISTS user_name_index IN PARTITION '20240403'; diff --git a/parser/testdata/ddl/format/alter_table_materialize_projection.sql b/parser/testdata/ddl/format/alter_table_materialize_projection.sql new file mode 100644 index 0000000..4effe81 --- /dev/null +++ b/parser/testdata/ddl/format/alter_table_materialize_projection.sql @@ -0,0 +1,8 @@ +-- Origin SQL: +ALTER TABLE visits_order MATERIALIZE PROJECTION IF EXISTS user_name_projection IN PARTITION '20240403'; + + + +-- Format SQL: +ALTER TABLE visits_order +MATERIALIZE PROJECTION IF EXISTS user_name_projection IN PARTITION '20240403'; diff --git a/parser/testdata/ddl/output/alter_table_materialize_index.sql.golden.json b/parser/testdata/ddl/output/alter_table_materialize_index.sql.golden.json new file mode 100644 index 0000000..3db7173 --- /dev/null +++ b/parser/testdata/ddl/output/alter_table_materialize_index.sql.golden.json @@ -0,0 +1,42 @@ +[ + { + "AlterPos": 0, + "StatementEnd": 91, + "TableIdentifier": { + "Database": null, + "Table": { + "Name": "visits_order", + "QuoteType": 1, + "NamePos": 12, + "NameEnd": 24 + } + }, + "OnCluster": null, + "AlterExprs": [ + { + "MaterializedPos": 25, + "StatementEnd": 91, + "IfExists": true, + "IndexName": { + "Ident": { + "Name": "user_name_index", + "QuoteType": 1, + "NamePos": 53, + "NameEnd": 68 + }, + "DotIdent": null + }, + "Partition": { + "PartitionPos": 72, + "Expr": { + "LiteralPos": 83, + "LiteralEnd": 91, + "Literal": "20240403" + }, + "ID": null, + "All": false + } + } + ] + } +] \ No newline at end of file diff --git a/parser/testdata/ddl/output/alter_table_materialize_projection.sql.golden.json b/parser/testdata/ddl/output/alter_table_materialize_projection.sql.golden.json new file mode 100644 index 0000000..cb964c5 --- /dev/null +++ b/parser/testdata/ddl/output/alter_table_materialize_projection.sql.golden.json @@ -0,0 +1,42 @@ +[ + { + "AlterPos": 0, + "StatementEnd": 101, + "TableIdentifier": { + "Database": null, + "Table": { + "Name": "visits_order", + "QuoteType": 1, + "NamePos": 12, + "NameEnd": 24 + } + }, + "OnCluster": null, + "AlterExprs": [ + { + "MaterializedPos": 25, + "StatementEnd": 101, + "IfExists": true, + "ProjectionName": { + "Ident": { + "Name": "user_name_projection", + "QuoteType": 1, + "NamePos": 58, + "NameEnd": 78 + }, + "DotIdent": null + }, + "Partition": { + "PartitionPos": 82, + "Expr": { + "LiteralPos": 93, + "LiteralEnd": 101, + "Literal": "20240403" + }, + "ID": null, + "All": false + } + } + ] + } +] \ No newline at end of file