Skip to content

Commit 05eae61

Browse files
authored
Add support of parsing TTL policy when creating table (#137)
1 parent 648ae29 commit 05eae61

23 files changed

+1252
-41
lines changed

parser/ast.go

Lines changed: 165 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
type OrderDirection string
88

99
const (
10-
OrderDirectionNone OrderDirection = "None"
10+
OrderDirectionNone OrderDirection = ""
1111
OrderDirectionAsc OrderDirection = "ASC"
1212
OrderDirectionDesc OrderDirection = "DESC"
1313
)
@@ -2690,9 +2690,164 @@ func (s *SampleByClause) Accept(visitor ASTVisitor) error {
26902690
return visitor.VisitSampleByExpr(s)
26912691
}
26922692

2693+
type TTLPolicyRuleAction struct {
2694+
ActionPos Pos
2695+
ActionEnd Pos
2696+
Action string
2697+
Codec *CompressionCodec
2698+
}
2699+
2700+
func (t *TTLPolicyRuleAction) Pos() Pos {
2701+
return t.ActionPos
2702+
}
2703+
2704+
func (t *TTLPolicyRuleAction) End() Pos {
2705+
if t.Codec != nil {
2706+
return t.Codec.End()
2707+
}
2708+
return t.ActionEnd
2709+
}
2710+
2711+
func (t *TTLPolicyRuleAction) String() string {
2712+
var builder strings.Builder
2713+
builder.WriteString(t.Action)
2714+
if t.Codec != nil {
2715+
builder.WriteString(" ")
2716+
builder.WriteString(t.Codec.String())
2717+
}
2718+
return builder.String()
2719+
}
2720+
2721+
func (t *TTLPolicyRuleAction) Accept(visitor ASTVisitor) error {
2722+
visitor.enter(t)
2723+
defer visitor.leave(t)
2724+
if t.Codec != nil {
2725+
if err := t.Codec.Accept(visitor); err != nil {
2726+
return err
2727+
}
2728+
}
2729+
return visitor.VisitTTLPolicyItemAction(t)
2730+
}
2731+
2732+
type TTLPolicyRule struct {
2733+
RulePos Pos
2734+
ToVolume *StringLiteral
2735+
ToDisk *StringLiteral
2736+
Action *TTLPolicyRuleAction
2737+
}
2738+
2739+
func (t *TTLPolicyRule) Pos() Pos {
2740+
return t.RulePos
2741+
}
2742+
2743+
func (t *TTLPolicyRule) End() Pos {
2744+
if t.Action != nil {
2745+
return t.Action.End()
2746+
}
2747+
if t.ToDisk != nil {
2748+
return t.ToDisk.LiteralEnd
2749+
}
2750+
return t.ToVolume.LiteralEnd
2751+
}
2752+
2753+
func (t *TTLPolicyRule) String() string {
2754+
var builder strings.Builder
2755+
if t.ToVolume != nil {
2756+
builder.WriteString("TO VOLUME ")
2757+
builder.WriteString(t.ToVolume.String())
2758+
} else if t.ToDisk != nil {
2759+
builder.WriteString("TO DISK ")
2760+
builder.WriteString(t.ToDisk.String())
2761+
} else if t.Action != nil {
2762+
builder.WriteString(t.Action.String())
2763+
}
2764+
return builder.String()
2765+
}
2766+
2767+
func (t *TTLPolicyRule) Accept(visitor ASTVisitor) error {
2768+
visitor.enter(t)
2769+
defer visitor.leave(t)
2770+
if t.ToVolume != nil {
2771+
if err := t.ToVolume.Accept(visitor); err != nil {
2772+
return err
2773+
}
2774+
}
2775+
if t.ToDisk != nil {
2776+
if err := t.ToDisk.Accept(visitor); err != nil {
2777+
return err
2778+
}
2779+
}
2780+
return visitor.VisitTTLPolicyRule(t)
2781+
}
2782+
2783+
type TTLPolicy struct {
2784+
Item *TTLPolicyRule
2785+
Where *WhereClause
2786+
GroupBy *GroupByClause
2787+
}
2788+
2789+
func (t *TTLPolicy) Pos() Pos {
2790+
if t.Item != nil {
2791+
return t.Item.Pos()
2792+
}
2793+
if t.Where != nil {
2794+
return t.Where.Pos()
2795+
}
2796+
return t.GroupBy.Pos()
2797+
}
2798+
2799+
func (t *TTLPolicy) End() Pos {
2800+
if t.GroupBy != nil {
2801+
return t.GroupBy.End()
2802+
}
2803+
if t.Where != nil {
2804+
return t.Where.End()
2805+
}
2806+
return t.Item.End()
2807+
}
2808+
2809+
func (t *TTLPolicy) String() string {
2810+
var builder strings.Builder
2811+
2812+
if t.Item != nil {
2813+
builder.WriteString(t.Item.String())
2814+
}
2815+
if t.Where != nil {
2816+
builder.WriteString(" ")
2817+
builder.WriteString(t.Where.String())
2818+
}
2819+
if t.GroupBy != nil {
2820+
builder.WriteString(" ")
2821+
builder.WriteString(t.GroupBy.String())
2822+
}
2823+
return builder.String()
2824+
}
2825+
2826+
func (t *TTLPolicy) Accept(visitor ASTVisitor) error {
2827+
visitor.enter(t)
2828+
defer visitor.leave(t)
2829+
if t.Item != nil {
2830+
if err := t.Item.Accept(visitor); err != nil {
2831+
return err
2832+
}
2833+
}
2834+
if t.Where != nil {
2835+
if err := t.Where.Accept(visitor); err != nil {
2836+
return err
2837+
}
2838+
}
2839+
if t.GroupBy != nil {
2840+
if err := t.GroupBy.Accept(visitor); err != nil {
2841+
return err
2842+
}
2843+
}
2844+
return visitor.VisitTTLPolicy(t)
2845+
}
2846+
26932847
type TTLExpr struct {
26942848
TTLPos Pos
26952849
Expr Expr
2850+
Policy *TTLPolicy
26962851
}
26972852

26982853
func (t *TTLExpr) Pos() Pos {
@@ -2706,6 +2861,10 @@ func (t *TTLExpr) End() Pos {
27062861
func (t *TTLExpr) String() string {
27072862
var builder strings.Builder
27082863
builder.WriteString(t.Expr.String())
2864+
if t.Policy != nil {
2865+
builder.WriteString(" ")
2866+
builder.WriteString(t.Policy.String())
2867+
}
27092868
return builder.String()
27102869
}
27112870

@@ -2715,6 +2874,11 @@ func (t *TTLExpr) Accept(visitor ASTVisitor) error {
27152874
if err := t.Expr.Accept(visitor); err != nil {
27162875
return err
27172876
}
2877+
if t.Policy != nil {
2878+
if err := t.Policy.Accept(visitor); err != nil {
2879+
return err
2880+
}
2881+
}
27182882
return visitor.VisitTTLExpr(t)
27192883
}
27202884

parser/ast_visitor.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ type ASTVisitor interface {
6161
VisitSampleByExpr(expr *SampleByClause) error
6262
VisitTTLExpr(expr *TTLExpr) error
6363
VisitTTLExprList(expr *TTLClause) error
64+
VisitTTLPolicy(expr *TTLPolicy) error
65+
VisitTTLPolicyRule(expr *TTLPolicyRule) error
66+
VisitTTLPolicyItemAction(expr *TTLPolicyRuleAction) error
6467
VisitOrderByExpr(expr *OrderExpr) error
6568
VisitOrderByListExpr(expr *OrderByClause) error
6669
VisitSettingsExpr(expr *SettingExprList) error
@@ -596,6 +599,27 @@ func (v *DefaultASTVisitor) VisitTTLExprList(expr *TTLClause) error {
596599
return nil
597600
}
598601

602+
func (v *DefaultASTVisitor) VisitTTLPolicy(expr *TTLPolicy) error {
603+
if v.Visit != nil {
604+
return v.Visit(expr)
605+
}
606+
return nil
607+
}
608+
609+
func (v *DefaultASTVisitor) VisitTTLPolicyRule(expr *TTLPolicyRule) error {
610+
if v.Visit != nil {
611+
return v.Visit(expr)
612+
}
613+
return nil
614+
}
615+
616+
func (v *DefaultASTVisitor) VisitTTLPolicyItemAction(expr *TTLPolicyRuleAction) error {
617+
if v.Visit != nil {
618+
return v.Visit(expr)
619+
}
620+
return nil
621+
}
622+
599623
func (v *DefaultASTVisitor) VisitOrderByExpr(expr *OrderExpr) error {
600624
if v.Visit != nil {
601625
return v.Visit(expr)

parser/keyword.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ const (
158158
KeywordQueues = "QUEUES"
159159
KeywordQuota = "QUOTA"
160160
KeywordRange = "RANGE"
161+
KeywordRecompress = "RECOMPRESS"
161162
KeywordRefresh = "REFRESH"
162163
KeywordRegexp = "REGEXP"
163164
KeywordReload = "RELOAD"
@@ -385,6 +386,7 @@ var keywords = NewSet(
385386
KeywordQueues,
386387
KeywordQuota,
387388
KeywordRange,
389+
KeywordRecompress,
388390
KeywordRefresh,
389391
KeywordRegexp,
390392
KeywordReload,

parser/parser_table.go

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,11 @@ func (p *Parser) parseOrderExpr(pos Pos) (*OrderExpr, error) {
691691
if err != nil {
692692
return nil, err
693693
}
694+
} else if p.matchKeyword(KeywordTtl) {
695+
return &OrderExpr{
696+
OrderPos: pos,
697+
Expr: columnExpr,
698+
}, nil
694699
}
695700

696701
direction := OrderDirectionNone
@@ -746,28 +751,71 @@ func (p *Parser) parseTTLClause(pos Pos, allowMultiValues bool) ([]*TTLExpr, err
746751
return items, nil
747752
}
748753

749-
func (p *Parser) parseTTLExpr(pos Pos) (*TTLExpr, error) {
750-
columnExpr, err := p.parseExpr(pos)
751-
if err != nil {
752-
return nil, err
753-
}
754+
func (p *Parser) tryParseTTLPolicy(pos Pos) (*TTLPolicy, error) {
755+
var rule *TTLPolicyRule
754756
switch {
755-
case p.matchKeyword(KeywordDelete):
756-
_ = p.lexer.consumeToken()
757-
case p.matchKeyword(KeywordTo):
758-
_ = p.lexer.consumeToken()
759-
if p.tryConsumeKeyword(KeywordDisk) != nil || p.tryConsumeKeyword(KeywordVolume) != nil {
760-
_, err := p.parseString(p.Pos())
757+
case p.tryConsumeKeyword(KeywordTo) != nil:
758+
if p.tryConsumeKeyword(KeywordDisk) != nil {
759+
value, err := p.parseString(p.Pos())
761760
if err != nil {
762761
return nil, err
763762
}
763+
rule = &TTLPolicyRule{RulePos: pos, ToDisk: value}
764+
} else if p.tryConsumeKeyword(KeywordVolume) != nil {
765+
value, err := p.parseString(p.Pos())
766+
if err != nil {
767+
return nil, err
768+
}
769+
rule = &TTLPolicyRule{RulePos: pos, ToVolume: value}
764770
} else {
765-
return nil, fmt.Errorf("expected keyword <DISK> or <VOLUME>, but got %q", p.last().String)
771+
return nil, fmt.Errorf("unexpected token: %q, expected DISK or VOLUME", p.lastTokenKind())
766772
}
773+
case p.matchKeyword(KeywordDelete), p.matchKeyword(KeywordRecompress):
774+
token := p.last()
775+
_ = p.lexer.consumeToken()
776+
action := &TTLPolicyRuleAction{
777+
ActionPos: token.Pos,
778+
ActionEnd: token.End,
779+
Action: token.ToString(),
780+
}
781+
codec, err := p.tryParseCompressionCodecs(p.Pos())
782+
if err != nil {
783+
return nil, err
784+
}
785+
action.Codec = codec
786+
rule = &TTLPolicyRule{RulePos: pos, Action: action}
787+
default:
788+
return nil, nil // nolint
789+
}
790+
policy := &TTLPolicy{Item: rule}
791+
792+
where, err := p.tryParseWhereClause(p.Pos())
793+
if err != nil {
794+
return nil, err
795+
}
796+
policy.Where = where
797+
798+
groupBy, err := p.tryParseGroupByClause(p.Pos())
799+
if err != nil {
800+
return nil, err
801+
}
802+
policy.GroupBy = groupBy
803+
return policy, nil
804+
}
805+
806+
func (p *Parser) parseTTLExpr(pos Pos) (*TTLExpr, error) {
807+
columnExpr, err := p.parseExpr(pos)
808+
if err != nil {
809+
return nil, err
810+
}
811+
policy, err := p.tryParseTTLPolicy(p.Pos())
812+
if err != nil {
813+
return nil, err
767814
}
768815
return &TTLExpr{
769816
TTLPos: pos,
770817
Expr: columnExpr,
818+
Policy: policy,
771819
}, nil
772820
}
773821

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
CREATE TABLE tab
2+
(
3+
d DateTime,
4+
a Int
5+
)
6+
ENGINE = MergeTree
7+
PARTITION BY toYYYYMM(d)
8+
ORDER BY d
9+
TTL d + INTERVAL 1 MONTH DELETE,
10+
d + INTERVAL 1 WEEK TO VOLUME 'aaa',
11+
d + INTERVAL 2 WEEK TO DISK 'bbb';
12+
13+
14+
CREATE TABLE table_with_where
15+
(
16+
d DateTime,
17+
a Int
18+
)
19+
ENGINE = MergeTree
20+
PARTITION BY toYYYYMM(d)
21+
ORDER BY d
22+
TTL d + INTERVAL 1 MONTH DELETE WHERE toDayOfWeek(d) = 1;
23+
24+
CREATE TABLE table_for_recompression
25+
(
26+
d DateTime,
27+
key UInt64,
28+
value String
29+
) ENGINE MergeTree()
30+
ORDER BY tuple()
31+
PARTITION BY key
32+
TTL d + INTERVAL 1 MONTH RECOMPRESS CODEC(ZSTD(17)), d + INTERVAL 1 YEAR RECOMPRESS CODEC(LZ4HC(10))
33+
SETTINGS min_rows_for_wide_part = 0, min_bytes_for_wide_part = 0;

parser/testdata/ddl/format/create_mv_with_order_by.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ AS
1313
SELECT * FROM test_table;
1414

1515
-- Format SQL:
16-
CREATE MATERIALIZED VIEW IF NOT EXISTS test_mv ENGINE = ReplacingMergeTree() PRIMARY KEY (id) ORDER BY (id) AS SELECT * FROM test_table;
16+
CREATE MATERIALIZED VIEW IF NOT EXISTS test_mv ENGINE = ReplacingMergeTree() PRIMARY KEY (id) ORDER BY (id) AS SELECT * FROM test_table;
1717
CREATE MATERIALIZED VIEW IF NOT EXISTS test_mv ENGINE = ReplacingMergeTree() PRIMARY KEY (id) AS SELECT * FROM test_table;

0 commit comments

Comments
 (0)