Skip to content

Commit 6274fc1

Browse files
authored
Adding support MV definer (#160)
1 parent 8a20c12 commit 6274fc1

13 files changed

+491
-15
lines changed

parser/ast.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1634,6 +1634,8 @@ type CreateMaterializedView struct {
16341634
SubQuery *SubQuery
16351635
Populate bool
16361636
Comment *StringLiteral
1637+
Definer *Ident
1638+
SQLSecurity string
16371639
}
16381640

16391641
func (c *CreateMaterializedView) Pos() Pos {
@@ -1697,14 +1699,21 @@ func (c *CreateMaterializedView) String() string {
16971699
if c.HasEmpty {
16981700
builder.WriteString(" EMPTY")
16991701
}
1702+
if c.Definer != nil {
1703+
builder.WriteString(" DEFINER = ")
1704+
builder.WriteString(c.Definer.String())
1705+
}
1706+
if c.SQLSecurity != "" {
1707+
builder.WriteString(" SQL SECURITY ")
1708+
builder.WriteString(c.SQLSecurity)
1709+
}
17001710
if c.Populate {
1701-
builder.WriteString(" POPULATE ")
1711+
builder.WriteString(" POPULATE")
17021712
}
17031713
if c.SubQuery != nil {
17041714
builder.WriteString(" AS ")
17051715
builder.WriteString(c.SubQuery.String())
17061716
}
1707-
17081717
if c.Comment != nil {
17091718
builder.WriteString(" COMMENT ")
17101719
builder.WriteString(c.Comment.String())
@@ -1765,6 +1774,16 @@ func (c *CreateMaterializedView) Accept(visitor ASTVisitor) error {
17651774
return err
17661775
}
17671776
}
1777+
if c.Definer != nil {
1778+
if err := c.Definer.Accept(visitor); err != nil {
1779+
return err
1780+
}
1781+
}
1782+
if c.Comment != nil {
1783+
if err := c.Comment.Accept(visitor); err != nil {
1784+
return err
1785+
}
1786+
}
17681787
return visitor.VisitCreateMaterializedView(c)
17691788
}
17701789

parser/keyword.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,9 @@ const (
234234
KeywordWindow = "WINDOW"
235235
KeywordWith = "WITH"
236236
KeywordYear = "YEAR"
237+
KeywordDefiner = "DEFINER"
238+
KeywordSQL = "SQL"
239+
KeywordSecurity = "SECURITY"
237240
)
238241

239242
var keywords = NewSet(
@@ -470,4 +473,7 @@ var keywords = NewSet(
470473
KeywordWindow,
471474
KeywordWith,
472475
KeywordYear,
476+
KeywordDefiner,
477+
KeywordSQL,
478+
KeywordSecurity,
473479
)

parser/parser_view.go

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,16 +103,44 @@ func (p *Parser) parseCreateMaterializedView(pos Pos) (*CreateMaterializedView,
103103
}
104104
createMaterializedView.Engine = engineExpr
105105
createMaterializedView.StatementEnd = engineExpr.End()
106-
107-
if p.tryConsumeKeywords(KeywordPopulate) {
108-
createMaterializedView.Populate = true
109-
createMaterializedView.StatementEnd = p.Pos()
110-
}
111106
default:
112107
return nil, fmt.Errorf("unexpected token: %q, expected TO or ENGINE", p.lastTokenKind())
113108
}
114109
createMaterializedView.HasEmpty = p.tryConsumeKeywords(KeywordEmpty)
115110

111+
// Parse DEFINER clause
112+
if p.tryConsumeKeywords(KeywordDefiner) {
113+
if err := p.expectTokenKind(TokenKindSingleEQ); err != nil {
114+
return nil, err
115+
}
116+
definer, err := p.parseIdent()
117+
if err != nil {
118+
return nil, err
119+
}
120+
createMaterializedView.Definer = definer
121+
}
122+
123+
// Parse SQL SECURITY clause
124+
if p.tryConsumeKeywords(KeywordSQL, KeywordSecurity) {
125+
if !p.matchOneOfKeywords(KeywordDefiner, KeywordNone) {
126+
return nil, fmt.Errorf("expected DEFINER or NONE after SQL SECURITY, got %q", p.lastTokenKind())
127+
}
128+
createMaterializedView.SQLSecurity = p.last().String
129+
_ = p.lexer.consumeToken()
130+
}
131+
132+
// Check for POPULATE before AS SELECT - only valid with ENGINE and no Destination
133+
if p.tryConsumeKeywords(KeywordPopulate) {
134+
if createMaterializedView.Destination != nil {
135+
return nil, fmt.Errorf("POPULATE is only allowed when using ENGINE, not with TO clause")
136+
}
137+
if createMaterializedView.Engine == nil {
138+
return nil, fmt.Errorf("POPULATE requires ENGINE to be specified")
139+
}
140+
createMaterializedView.Populate = true
141+
createMaterializedView.StatementEnd = p.Pos()
142+
}
143+
116144
if p.tryConsumeKeywords(KeywordAs) {
117145
subQuery, err := p.parseSubQuery(p.Pos())
118146
if err != nil {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
CREATE MATERIALIZED VIEW fresh_mv
2+
REFRESH EVERY 1 HOUR OFFSET 10 MINUTE APPEND TO events_export
3+
(
4+
`timestamp` DateTime64(9),
5+
`field_1` String,
6+
`field_2` String,
7+
)
8+
DEFINER = default SQL SECURITY DEFINER
9+
AS (SELECT
10+
timestamp,
11+
field_1,
12+
field_2,
13+
FROM event_table
14+
WHERE toStartOfHour(timestamp) = toStartOfHour(now() - toIntervalHour(1)))
15+
COMMENT 'Test comment'
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
-- Origin SQL:
2+
CREATE MATERIALIZED VIEW fresh_mv
3+
REFRESH EVERY 1 HOUR OFFSET 10 MINUTE APPEND TO events_export
4+
(
5+
`timestamp` DateTime64(9),
6+
`field_1` String,
7+
`field_2` String,
8+
)
9+
DEFINER = default SQL SECURITY DEFINER
10+
AS (SELECT
11+
timestamp,
12+
field_1,
13+
field_2,
14+
FROM event_table
15+
WHERE toStartOfHour(timestamp) = toStartOfHour(now() - toIntervalHour(1)))
16+
COMMENT 'Test comment'
17+
18+
19+
-- Format SQL:
20+
CREATE MATERIALIZED VIEW fresh_mv REFRESH EVERY 1 HOUR OFFSET 10 MINUTE APPEND TO events_export (`timestamp` DateTime64(9), `field_1` String, `field_2` String) DEFINER = default SQL SECURITY DEFINER AS (SELECT timestamp, field_1, field_2, FROM AS event_table WHERE toStartOfHour(timestamp) = toStartOfHour(now() - toIntervalHour(1))) COMMENT 'Test comment';

parser/testdata/ddl/format/create_materialized_view_with_empty_table_schema.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ from
1616
where rn = 1;
1717

1818
-- Format SQL:
19-
CREATE MATERIALIZED VIEW test.t0 ON CLUSTER default_cluster ENGINE = ReplicatedAggregatingMergeTree('/clickhouse/{layer}-{shard}/test/t0', '{replica}') PARTITION BY toYYYYMM(f0) ORDER BY (f0) POPULATE AS SELECT f0, f1, f2, coalesce(f0, f1) AS f333 FROM (SELECT f0, f1, f2, ROW_NUMBER() OVER ( PARTITION BY f0 ORDER BY coalesce(f1, f2)) AS rn FROM test.t WHERE f3 IN ('foo', 'bar', 'test') AND env = 'test') AS tmp WHERE rn = 1;
19+
CREATE MATERIALIZED VIEW test.t0 ON CLUSTER default_cluster ENGINE = ReplicatedAggregatingMergeTree('/clickhouse/{layer}-{shard}/test/t0', '{replica}') PARTITION BY toYYYYMM(f0) ORDER BY (f0) POPULATE AS SELECT f0, f1, f2, coalesce(f0, f1) AS f333 FROM (SELECT f0, f1, f2, ROW_NUMBER() OVER ( PARTITION BY f0 ORDER BY coalesce(f1, f2)) AS rn FROM test.t WHERE f3 IN ('foo', 'bar', 'test') AND env = 'test') AS tmp WHERE rn = 1;

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,8 @@
571571
}
572572
},
573573
"Populate": false,
574-
"Comment": null
574+
"Comment": null,
575+
"Definer": null,
576+
"SQLSecurity": ""
575577
}
576578
]

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,8 @@
542542
"LiteralPos": 548,
543543
"LiteralEnd": 565,
544544
"Literal": "Comment for table"
545-
}
545+
},
546+
"Definer": null,
547+
"SQLSecurity": ""
546548
}
547549
]

0 commit comments

Comments
 (0)