diff --git a/parser/ast.go b/parser/ast.go index 34f7233..fb4ce10 100644 --- a/parser/ast.go +++ b/parser/ast.go @@ -3504,3 +3504,57 @@ func (n *UnaryExpr) End() Pos { func (n *UnaryExpr) String(level int) string { return "-" + n.Expr.String(level+1) } + +type RenameTable struct { + RenamePos Pos + StatementEnd Pos + TablePairList []*TablePair + OnCluster *OnClusterExpr +} + +func (r *RenameTable) Pos() Pos { + return r.RenamePos +} + +func (r *RenameTable) End() Pos { + return r.StatementEnd +} + +func (r *RenameTable) Type() string { + return "RENAME TABLE" +} + +func (r *RenameTable) String(level int) string { + var builder strings.Builder + builder.WriteString("RENAME TABLE ") + for i, pair := range r.TablePairList { + if i > 0 { + builder.WriteString(", ") + } + builder.WriteString(pair.Old.String(level)) + builder.WriteString(" TO ") + builder.WriteString(pair.New.String(level)) + } + if r.OnCluster != nil { + builder.WriteString(NewLine(level)) + builder.WriteString(r.OnCluster.String(level)) + } + return builder.String() +} + +type TablePair struct { + Old *TableIdentifier + New *TableIdentifier +} + +func (t *TablePair) Pos() Pos { + return t.Old.Pos() +} + +func (t *TablePair) End() Pos { + return t.New.End() +} + +func (t *TablePair) String() string { + return t.Old.String(0) + " TO " + t.New.String(0) +} diff --git a/parser/parser_table.go b/parser/parser_table.go index 51ccb75..69ae212 100644 --- a/parser/parser_table.go +++ b/parser/parser_table.go @@ -46,6 +46,8 @@ func (p *Parser) parseDDL(pos Pos) (DDL, error) { } case p.matchKeyword(KeywordTruncate): return p.parseTruncateTable(pos) + case p.matchKeyword(KeywordRename): + return p.parseRenameTable(pos) } return nil, nil // nolint } @@ -890,7 +892,8 @@ func (p *Parser) parseStatement(pos Pos) (Expr, error) { p.matchKeyword(KeywordAlter), p.matchKeyword(KeywordDrop), p.matchKeyword(KeywordDetach), - p.matchKeyword(KeywordTruncate): + p.matchKeyword(KeywordTruncate), + p.matchKeyword(KeywordRename): expr, err = p.parseDDL(pos) case p.matchKeyword(KeywordSelect), p.matchKeyword(KeywordWith): expr, err = p.parseSelectQuery(pos) @@ -1166,3 +1169,61 @@ func (p *Parser) parseInsertExpr(pos Pos) (*InsertExpr, error) { return insertExpr, nil } + +func (p *Parser) parseRenameTable(pos Pos) (*RenameTable, error) { + if err := p.consumeKeyword(KeywordRename); err != nil { + return nil, err + } + if err := p.consumeKeyword(KeywordTable); err != nil { + return nil, err + } + + tablePair, err := p.parseTablePair(p.Pos()) + if err != nil { + return nil, err + } + tablePairList := []*TablePair{tablePair} + for p.tryConsumeTokenKind(",") != nil { + tablePair, err := p.parseTablePair(p.Pos()) + if err != nil { + return nil, err + } + tablePairList = append(tablePairList, tablePair) + } + + renameTable := &RenameTable{ + RenamePos: pos, + StatementEnd: tablePairList[len(tablePairList)-1].End(), + TablePairList: tablePairList, + } + + onClusterExpr, err := p.tryParseOnCluster(p.Pos()) + if err != nil { + return nil, err + } + if onClusterExpr != nil { + renameTable.OnCluster = onClusterExpr + renameTable.StatementEnd = onClusterExpr.End() + } + + return renameTable, nil +} + +func (p *Parser) parseTablePair(_ Pos) (*TablePair, error) { + oldTable, err := p.parseTableIdentifier(p.Pos()) + if err != nil { + return nil, err + } + if err = p.consumeKeyword(KeywordTo); err != nil { + return nil, err + } + newTable, err := p.parseTableIdentifier(p.Pos()) + if err != nil { + return nil, err + } + + return &TablePair{ + Old: oldTable, + New: newTable, + }, nil +} diff --git a/parser/testdata/ddl/format/rename_table.sql b/parser/testdata/ddl/format/rename_table.sql new file mode 100644 index 0000000..f7055d2 --- /dev/null +++ b/parser/testdata/ddl/format/rename_table.sql @@ -0,0 +1,14 @@ +-- Origin SQL: +RENAME TABLE t1 TO t11; +RENAME TABLE t1 TO t11 ON CLUSTER 'default_cluster'; +RENAME TABLE t1 TO t11, t2 TO t22; +RENAME TABLE t1 TO t11, t2 TO t22 ON CLUSTER 'default_cluster'; + + +-- Format SQL: +RENAME TABLE t1 TO t11; +RENAME TABLE t1 TO t11 +ON CLUSTER 'default_cluster'; +RENAME TABLE t1 TO t11, t2 TO t22; +RENAME TABLE t1 TO t11, t2 TO t22 +ON CLUSTER 'default_cluster'; diff --git a/parser/testdata/ddl/output/rename_table.sql.golden.json b/parser/testdata/ddl/output/rename_table.sql.golden.json new file mode 100644 index 0000000..27170bd --- /dev/null +++ b/parser/testdata/ddl/output/rename_table.sql.golden.json @@ -0,0 +1,164 @@ +[ + { + "RenamePos": 0, + "StatementEnd": 22, + "TablePairList": [ + { + "Old": { + "Database": null, + "Table": { + "Name": "t1", + "Unquoted": false, + "NamePos": 13, + "NameEnd": 15 + } + }, + "New": { + "Database": null, + "Table": { + "Name": "t11", + "Unquoted": false, + "NamePos": 19, + "NameEnd": 22 + } + } + } + ], + "OnCluster": null + }, + { + "RenamePos": 24, + "StatementEnd": 74, + "TablePairList": [ + { + "Old": { + "Database": null, + "Table": { + "Name": "t1", + "Unquoted": false, + "NamePos": 37, + "NameEnd": 39 + } + }, + "New": { + "Database": null, + "Table": { + "Name": "t11", + "Unquoted": false, + "NamePos": 43, + "NameEnd": 46 + } + } + } + ], + "OnCluster": { + "OnPos": 47, + "Expr": { + "LiteralPos": 59, + "LiteralEnd": 74, + "Literal": "default_cluster" + } + } + }, + { + "RenamePos": 77, + "StatementEnd": 110, + "TablePairList": [ + { + "Old": { + "Database": null, + "Table": { + "Name": "t1", + "Unquoted": false, + "NamePos": 90, + "NameEnd": 92 + } + }, + "New": { + "Database": null, + "Table": { + "Name": "t11", + "Unquoted": false, + "NamePos": 96, + "NameEnd": 99 + } + } + }, + { + "Old": { + "Database": null, + "Table": { + "Name": "t2", + "Unquoted": false, + "NamePos": 101, + "NameEnd": 103 + } + }, + "New": { + "Database": null, + "Table": { + "Name": "t22", + "Unquoted": false, + "NamePos": 107, + "NameEnd": 110 + } + } + } + ], + "OnCluster": null + }, + { + "RenamePos": 112, + "StatementEnd": 173, + "TablePairList": [ + { + "Old": { + "Database": null, + "Table": { + "Name": "t1", + "Unquoted": false, + "NamePos": 125, + "NameEnd": 127 + } + }, + "New": { + "Database": null, + "Table": { + "Name": "t11", + "Unquoted": false, + "NamePos": 131, + "NameEnd": 134 + } + } + }, + { + "Old": { + "Database": null, + "Table": { + "Name": "t2", + "Unquoted": false, + "NamePos": 136, + "NameEnd": 138 + } + }, + "New": { + "Database": null, + "Table": { + "Name": "t22", + "Unquoted": false, + "NamePos": 142, + "NameEnd": 145 + } + } + } + ], + "OnCluster": { + "OnPos": 146, + "Expr": { + "LiteralPos": 158, + "LiteralEnd": 173, + "Literal": "default_cluster" + } + } + } +] \ No newline at end of file diff --git a/parser/testdata/ddl/rename_table.sql b/parser/testdata/ddl/rename_table.sql new file mode 100644 index 0000000..6229d65 --- /dev/null +++ b/parser/testdata/ddl/rename_table.sql @@ -0,0 +1,4 @@ +RENAME TABLE t1 TO t11; +RENAME TABLE t1 TO t11 ON CLUSTER 'default_cluster'; +RENAME TABLE t1 TO t11, t2 TO t22; +RENAME TABLE t1 TO t11, t2 TO t22 ON CLUSTER 'default_cluster';