Skip to content

Commit a0842a7

Browse files
authored
Add support of OR REPLACE expression for CREATE TABLE|VIEW (#143)
1 parent edb2565 commit a0842a7

23 files changed

+578
-13
lines changed

parser/ast.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1516,6 +1516,7 @@ func (c *CreateDatabase) Accept(visitor ASTVisitor) error {
15161516
type CreateTable struct {
15171517
CreatePos Pos // position of CREATE|ATTACH keyword
15181518
StatementEnd Pos
1519+
OrReplace bool
15191520
Name *TableIdentifier
15201521
IfNotExists bool
15211522
UUID *UUID
@@ -1542,6 +1543,9 @@ func (c *CreateTable) Type() string {
15421543
func (c *CreateTable) String() string {
15431544
var builder strings.Builder
15441545
builder.WriteString("CREATE")
1546+
if c.OrReplace {
1547+
builder.WriteString(" OR REPLACE")
1548+
}
15451549
if c.HasTemporary {
15461550
builder.WriteString(" TEMPORARY")
15471551
}
@@ -1706,6 +1710,7 @@ func (c *CreateMaterializedView) Accept(visitor ASTVisitor) error {
17061710
type CreateView struct {
17071711
CreatePos Pos // position of CREATE|ATTACH keyword
17081712
StatementEnd Pos
1713+
OrReplace bool
17091714
Name *TableIdentifier
17101715
IfNotExists bool
17111716
UUID *UUID
@@ -1728,7 +1733,11 @@ func (c *CreateView) Type() string {
17281733

17291734
func (c *CreateView) String() string {
17301735
var builder strings.Builder
1731-
builder.WriteString("CREATE VIEW ")
1736+
builder.WriteString("CREATE")
1737+
if c.OrReplace {
1738+
builder.WriteString(" OR REPLACE")
1739+
}
1740+
builder.WriteString(" VIEW ")
17321741
if c.IfNotExists {
17331742
builder.WriteString("IF NOT EXISTS ")
17341743
}

parser/parser_common.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,15 @@ func (p *Parser) matchKeyword(keyword string) bool {
6161
return p.matchTokenKind(TokenKindKeyword) && strings.EqualFold(p.last().String, keyword)
6262
}
6363

64+
func (p *Parser) matchOneOfKeywords(keywords ...string) bool {
65+
for _, keyword := range keywords {
66+
if p.matchKeyword(keyword) {
67+
return true
68+
}
69+
}
70+
return false
71+
}
72+
6473
func (p *Parser) expectKeyword(keyword string) error {
6574
if !p.matchKeyword(keyword) {
6675
return fmt.Errorf("expected keyword: %s, but got %s", keyword, p.lastTokenKind())

parser/parser_table.go

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,28 @@ func (p *Parser) parseDDL(pos Pos) (DDL, error) {
99
case p.matchKeyword(KeywordCreate),
1010
p.matchKeyword(KeywordAttach):
1111
_ = p.lexer.consumeToken()
12+
orReplace := p.tryConsumeKeywords(KeywordOr, KeywordReplace)
13+
if orReplace && !p.matchOneOfKeywords(KeywordTemporary, KeywordTable, KeywordView) {
14+
return nil, fmt.Errorf("expected keyword: TEMPORARY|TABLE|VIEW, but got %q", p.last().String)
15+
}
1216
switch {
1317
case p.matchKeyword(KeywordDatabase):
1418
return p.parseCreateDatabase(pos)
1519
case p.matchKeyword(KeywordTable),
1620
p.matchKeyword(KeywordTemporary):
17-
return p.parseCreateTable(pos)
21+
return p.parseCreateTable(pos, orReplace)
1822
case p.matchKeyword(KeywordFunction):
1923
return p.parseCreateFunction(pos)
2024
case p.matchKeyword(KeywordMaterialized):
2125
return p.parseCreateMaterializedView(pos)
2226
case p.matchKeyword(KeywordLive):
2327
return p.parseCreateLiveView(pos)
2428
case p.matchKeyword(KeywordView):
25-
return p.parseCreateView(pos)
29+
return p.parseCreateView(pos, orReplace)
2630
case p.matchKeyword(KeywordRole):
2731
return p.parseCreateRole(pos)
28-
case p.matchKeyword(KeywordDictionary):
29-
case p.matchKeyword(KeywordFunction):
30-
case p.matchKeyword(KeywordRow):
31-
case p.matchKeyword(KeywordSettings):
3232
default:
33-
return nil, fmt.Errorf("expected keyword: DATABASE|TABLE|VIEW|DICTIONARY|FUNCTION|ROW|QUOTA|SETTINGS, but got %q",
33+
return nil, fmt.Errorf("expected keyword: DATABASE|TABLE|VIEW, but got %q",
3434
p.last().String)
3535
}
3636
case p.matchKeyword(KeywordAlter):
@@ -108,9 +108,8 @@ func (p *Parser) parseCreateDatabase(pos Pos) (*CreateDatabase, error) {
108108
}, nil
109109
}
110110

111-
func (p *Parser) parseCreateTable(pos Pos) (*CreateTable, error) {
112-
createTable := &CreateTable{CreatePos: pos}
113-
111+
func (p *Parser) parseCreateTable(pos Pos, orReplace bool) (*CreateTable, error) {
112+
createTable := &CreateTable{CreatePos: pos, OrReplace: orReplace}
114113
createTable.HasTemporary = p.tryConsumeKeywords(KeywordTemporary)
115114

116115
if err := p.expectKeyword(KeywordTable); err != nil {

parser/parser_view.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,12 @@ func (p *Parser) parseCreateMaterializedView(pos Pos) (*CreateMaterializedView,
8080
}
8181

8282
// (ATTACH | CREATE) (OR REPLACE)? VIEW (IF NOT EXISTS)? tableIdentifier uuidClause? clusterClause? tableSchemaClause? subqueryClause
83-
func (p *Parser) parseCreateView(pos Pos) (*CreateView, error) {
83+
func (p *Parser) parseCreateView(pos Pos, orReplace bool) (*CreateView, error) {
84+
createView := &CreateView{CreatePos: pos, OrReplace: orReplace}
8485
if err := p.expectKeyword(KeywordView); err != nil {
8586
return nil, err
8687
}
8788

88-
createView := &CreateView{CreatePos: pos}
8989
var err error
9090
createView.IfNotExists, err = p.tryParseIfNotExists()
9191
if err != nil {
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
-- It's a short link events table
2+
/**
3+
* @name Short link events
4+
* @description It's a short link events table
5+
*/
6+
CREATE OR REPLACE TABLE IF NOT EXISTS test.events_local (
7+
f0 String,
8+
f1 String CODEC(ZSTD(1)),
9+
f2 VARCHAR(255),
10+
) ENGINE = MergeTree
11+
PRIMARY KEY (f0, f1, f2)
12+
PARTITION BY toYYYYMMDD(f1)
13+
TTL f1 + INTERVAL 6 MONTH
14+
ORDER BY (f1,f2)
15+
COMMENT 'Comment for table';
16+
17+
CREATE OR REPLACE VIEW IF NOT EXISTS my_view(col1 String, col2 String)
18+
AS
19+
SELECT
20+
id,
21+
name
22+
FROM
23+
my_table;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-- Origin SQL:
2+
-- It's a short link events table
3+
/**
4+
* @name Short link events
5+
* @description It's a short link events table
6+
*/
7+
CREATE OR REPLACE TABLE IF NOT EXISTS test.events_local (
8+
f0 String,
9+
f1 String CODEC(ZSTD(1)),
10+
f2 VARCHAR(255),
11+
) ENGINE = MergeTree
12+
PRIMARY KEY (f0, f1, f2)
13+
PARTITION BY toYYYYMMDD(f1)
14+
TTL f1 + INTERVAL 6 MONTH
15+
ORDER BY (f1,f2)
16+
COMMENT 'Comment for table';
17+
18+
CREATE OR REPLACE VIEW IF NOT EXISTS my_view(col1 String, col2 String)
19+
AS
20+
SELECT
21+
id,
22+
name
23+
FROM
24+
my_table;
25+
26+
-- Format SQL:
27+
CREATE OR REPLACE TABLE IF NOT EXISTS test.events_local (f0 String, f1 String CODEC(ZSTD(1)), f2 VARCHAR(255)) ENGINE = MergeTree PRIMARY KEY (f0, f1, f2) PARTITION BY toYYYYMMDD(f1) TTL f1 + INTERVAL 6 MONTH ORDER BY (f1, f2) COMMENT 'Comment for table';
28+
CREATE OR REPLACE VIEW IF NOT EXISTS my_view (col1 String, col2 String) AS SELECT id, name FROM my_table;

parser/testdata/ddl/output/attach_table_basic.sql.golden.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{
33
"CreatePos": 0,
44
"StatementEnd": 399,
5+
"OrReplace": false,
56
"Name": {
67
"Database": {
78
"Name": "test",

parser/testdata/ddl/output/create_distributed_table.sql.golden.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{
33
"CreatePos": 0,
44
"StatementEnd": 191,
5+
"OrReplace": false,
56
"Name": {
67
"Database": {
78
"Name": "test",

0 commit comments

Comments
 (0)