Repository: AfterShip/clickhouse-sql-parser
Pull request #137 (2025-03-25 08:34:40)

Add support of parsing TTL policy when creating table
For the syntax, could refer to https://clickhouse.com/docs/engines/table-engines/mergetree-family/mergetree#table_engine-mergetree-ttl

coveralls: ## Pull Request Test Coverage Report for [Build 14055321360](https://coveralls.io/builds/72905684)


### Details

* **106** of **186**   **(56.99%)**  changed or added relevant lines in **3** files are covered.
* **1** unchanged line in **1** file lost coverage.
* Overall coverage decreased (**-0.05%**) to **62.573%**

---

|  Changes Missing Coverage | Covered Lines | Changed/Added Lines | % |
| :-----|--------------|--------|---: |
| [parser/ast_visitor.go](https://coveralls.io/builds/72905684/source?filename=parser%2Fast_visitor.go#L604) | 6 | 15 | 40.0%
| [parser/parser_table.go](https://coveralls.io/builds/72905684/source?filename=parser%2Fparser_table.go#L767) | 45 | 57 | 78.95%
| [parser/ast.go](https://coveralls.io/builds/72905684/source?filename=parser%2Fast.go#L2700) | 55 | 114 | 48.25%
<!-- | **Total:** | **106** | **186** | **56.99%** | -->

|  Files with Coverage Reduction | New Missed Lines | % |
| :-----|--------------|--: |
| [parser/parser_table.go](https://coveralls.io/builds/72905684/source?filename=parser%2Fparser_table.go#L814) | 1 | 70.0% |
<!-- | **Total:** | **1** |  | -->

|  Totals | [![Coverage Status](https://coveralls.io/builds/72905684/badge)](https://coveralls.io/builds/72905684) |
| :-- | --: |
| Change from base [Build 14033683295](https://coveralls.io/builds/72884733): |  -0.05% |
| Covered Lines: | 6848 |
| Relevant Lines: | 10944 |

---
##### 💛  - [Coveralls](https://coveralls.io)


Patch:
diff --git a/parser/ast.go b/parser/ast.go
index c3206a0..f050deb 100644
--- a/parser/ast.go
+++ b/parser/ast.go
@@ -7,7 +7,7 @@ import (
 type OrderDirection string
 
 const (
-	OrderDirectionNone OrderDirection = "None"
+	OrderDirectionNone OrderDirection = ""
 	OrderDirectionAsc  OrderDirection = "ASC"
 	OrderDirectionDesc OrderDirection = "DESC"
 )
@@ -2690,9 +2690,164 @@ func (s *SampleByClause) Accept(visitor ASTVisitor) error {
 	return visitor.VisitSampleByExpr(s)
 }
 
+type TTLPolicyRuleAction struct {
+	ActionPos Pos
+	ActionEnd Pos
+	Action    string
+	Codec     *CompressionCodec
+}
+
+func (t *TTLPolicyRuleAction) Pos() Pos {
+	return t.ActionPos
+}
+
+func (t *TTLPolicyRuleAction) End() Pos {
+	if t.Codec != nil {
+		return t.Codec.End()
+	}
+	return t.ActionEnd
+}
+
+func (t *TTLPolicyRuleAction) String() string {
+	var builder strings.Builder
+	builder.WriteString(t.Action)
+	if t.Codec != nil {
+		builder.WriteString(" ")
+		builder.WriteString(t.Codec.String())
+	}
+	return builder.String()
+}
+
+func (t *TTLPolicyRuleAction) Accept(visitor ASTVisitor) error {
+	visitor.enter(t)
+	defer visitor.leave(t)
+	if t.Codec != nil {
+		if err := t.Codec.Accept(visitor); err != nil {
+			return err
+		}
+	}
+	return visitor.VisitTTLPolicyItemAction(t)
+}
+
+type TTLPolicyRule struct {
+	RulePos  Pos
+	ToVolume *StringLiteral
+	ToDisk   *StringLiteral
+	Action   *TTLPolicyRuleAction
+}
+
+func (t *TTLPolicyRule) Pos() Pos {
+	return t.RulePos
+}
+
+func (t *TTLPolicyRule) End() Pos {
+	if t.Action != nil {
+		return t.Action.End()
+	}
+	if t.ToDisk != nil {
+		return t.ToDisk.LiteralEnd
+	}
+	return t.ToVolume.LiteralEnd
+}
+
+func (t *TTLPolicyRule) String() string {
+	var builder strings.Builder
+	if t.ToVolume != nil {
+		builder.WriteString("TO VOLUME ")
+		builder.WriteString(t.ToVolume.String())
+	} else if t.ToDisk != nil {
+		builder.WriteString("TO DISK ")
+		builder.WriteString(t.ToDisk.String())
+	} else if t.Action != nil {
+		builder.WriteString(t.Action.String())
+	}
+	return builder.String()
+}
+
+func (t *TTLPolicyRule) Accept(visitor ASTVisitor) error {
+	visitor.enter(t)
+	defer visitor.leave(t)
+	if t.ToVolume != nil {
+		if err := t.ToVolume.Accept(visitor); err != nil {
+			return err
+		}
+	}
+	if t.ToDisk != nil {
+		if err := t.ToDisk.Accept(visitor); err != nil {
+			return err
+		}
+	}
+	return visitor.VisitTTLPolicyRule(t)
+}
+
+type TTLPolicy struct {
+	Item    *TTLPolicyRule
+	Where   *WhereClause
+	GroupBy *GroupByClause
+}
+
+func (t *TTLPolicy) Pos() Pos {
+	if t.Item != nil {
+		return t.Item.Pos()
+	}
+	if t.Where != nil {
+		return t.Where.Pos()
+	}
+	return t.GroupBy.Pos()
+}
+
+func (t *TTLPolicy) End() Pos {
+	if t.GroupBy != nil {
+		return t.GroupBy.End()
+	}
+	if t.Where != nil {
+		return t.Where.End()
+	}
+	return t.Item.End()
+}
+
+func (t *TTLPolicy) String() string {
+	var builder strings.Builder
+
+	if t.Item != nil {
+		builder.WriteString(t.Item.String())
+	}
+	if t.Where != nil {
+		builder.WriteString(" ")
+		builder.WriteString(t.Where.String())
+	}
+	if t.GroupBy != nil {
+		builder.WriteString(" ")
+		builder.WriteString(t.GroupBy.String())
+	}
+	return builder.String()
+}
+
+func (t *TTLPolicy) Accept(visitor ASTVisitor) error {
+	visitor.enter(t)
+	defer visitor.leave(t)
+	if t.Item != nil {
+		if err := t.Item.Accept(visitor); err != nil {
+			return err
+		}
+	}
+	if t.Where != nil {
+		if err := t.Where.Accept(visitor); err != nil {
+			return err
+		}
+	}
+	if t.GroupBy != nil {
+		if err := t.GroupBy.Accept(visitor); err != nil {
+			return err
+		}
+	}
+	return visitor.VisitTTLPolicy(t)
+}
+
 type TTLExpr struct {
 	TTLPos Pos
 	Expr   Expr
+	Policy *TTLPolicy
 }
 
 func (t *TTLExpr) Pos() Pos {
@@ -2706,6 +2861,10 @@ func (t *TTLExpr) End() Pos {
 func (t *TTLExpr) String() string {
 	var builder strings.Builder
 	builder.WriteString(t.Expr.String())
+	if t.Policy != nil {
+		builder.WriteString(" ")
+		builder.WriteString(t.Policy.String())
+	}
 	return builder.String()
 }
 
@@ -2715,6 +2874,11 @@ func (t *TTLExpr) Accept(visitor ASTVisitor) error {
 	if err := t.Expr.Accept(visitor); err != nil {
 		return err
 	}
+	if t.Policy != nil {
+		if err := t.Policy.Accept(visitor); err != nil {
+			return err
+		}
+	}
 	return visitor.VisitTTLExpr(t)
 }
 
diff --git a/parser/ast_visitor.go b/parser/ast_visitor.go
index 6bd10e7..8577bc4 100644
--- a/parser/ast_visitor.go
+++ b/parser/ast_visitor.go
@@ -61,6 +61,9 @@ type ASTVisitor interface {
 	VisitSampleByExpr(expr *SampleByClause) error
 	VisitTTLExpr(expr *TTLExpr) error
 	VisitTTLExprList(expr *TTLClause) error
+	VisitTTLPolicy(expr *TTLPolicy) error
+	VisitTTLPolicyRule(expr *TTLPolicyRule) error
+	VisitTTLPolicyItemAction(expr *TTLPolicyRuleAction) error
 	VisitOrderByExpr(expr *OrderExpr) error
 	VisitOrderByListExpr(expr *OrderByClause) error
 	VisitSettingsExpr(expr *SettingExprList) error
@@ -596,6 +599,27 @@ func (v *DefaultASTVisitor) VisitTTLExprList(expr *TTLClause) error {
 	return nil
 }
 
+func (v *DefaultASTVisitor) VisitTTLPolicy(expr *TTLPolicy) error {
+	if v.Visit != nil {
+		return v.Visit(expr)
+	}
+	return nil
+}
+
+func (v *DefaultASTVisitor) VisitTTLPolicyRule(expr *TTLPolicyRule) error {
+	if v.Visit != nil {
+		return v.Visit(expr)
+	}
+	return nil
+}
+
+func (v *DefaultASTVisitor) VisitTTLPolicyItemAction(expr *TTLPolicyRuleAction) error {
+	if v.Visit != nil {
+		return v.Visit(expr)
+	}
+	return nil
+}
+
 func (v *DefaultASTVisitor) VisitOrderByExpr(expr *OrderExpr) error {
 	if v.Visit != nil {
 		return v.Visit(expr)
diff --git a/parser/keyword.go b/parser/keyword.go
index 3486735..fa9aa51 100644
--- a/parser/keyword.go
+++ b/parser/keyword.go
@@ -158,6 +158,7 @@ const (
 	KeywordQueues       = "QUEUES"
 	KeywordQuota        = "QUOTA"
 	KeywordRange        = "RANGE"
+	KeywordRecompress   = "RECOMPRESS"
 	KeywordRefresh      = "REFRESH"
 	KeywordRegexp       = "REGEXP"
 	KeywordReload       = "RELOAD"
@@ -385,6 +386,7 @@ var keywords = NewSet(
 	KeywordQueues,
 	KeywordQuota,
 	KeywordRange,
+	KeywordRecompress,
 	KeywordRefresh,
 	KeywordRegexp,
 	KeywordReload,
diff --git a/parser/parser_table.go b/parser/parser_table.go
index 56ee15a..aa4a85b 100644
--- a/parser/parser_table.go
+++ b/parser/parser_table.go
@@ -691,6 +691,11 @@ func (p *Parser) parseOrderExpr(pos Pos) (*OrderExpr, error) {
 		if err != nil {
 			return nil, err
 		}
+	} else if p.matchKeyword(KeywordTtl) {
+		return &OrderExpr{
+			OrderPos: pos,
+			Expr:     columnExpr,
+		}, nil
 	}
 
 	direction := OrderDirectionNone
@@ -746,28 +751,71 @@ func (p *Parser) parseTTLClause(pos Pos, allowMultiValues bool) ([]*TTLExpr, err
 	return items, nil
 }
 
-func (p *Parser) parseTTLExpr(pos Pos) (*TTLExpr, error) {
-	columnExpr, err := p.parseExpr(pos)
-	if err != nil {
-		return nil, err
-	}
+func (p *Parser) tryParseTTLPolicy(pos Pos) (*TTLPolicy, error) {
+	var rule *TTLPolicyRule
 	switch {
-	case p.matchKeyword(KeywordDelete):
-		_ = p.lexer.consumeToken()
-	case p.matchKeyword(KeywordTo):
-		_ = p.lexer.consumeToken()
-		if p.tryConsumeKeyword(KeywordDisk) != nil || p.tryConsumeKeyword(KeywordVolume) != nil {
-			_, err := p.parseString(p.Pos())
+	case p.tryConsumeKeyword(KeywordTo) != nil:
+		if p.tryConsumeKeyword(KeywordDisk) != nil {
+			value, err := p.parseString(p.Pos())
 			if err != nil {
 				return nil, err
 			}
+			rule = &TTLPolicyRule{RulePos: pos, ToDisk: value}
+		} else if p.tryConsumeKeyword(KeywordVolume) != nil {
+			value, err := p.parseString(p.Pos())
+			if err != nil {
+				return nil, err
+			}
+			rule = &TTLPolicyRule{RulePos: pos, ToVolume: value}
 		} else {
-			return nil, fmt.Errorf("expected keyword <DISK> or <VOLUME>, but got %q", p.last().String)
+			return nil, fmt.Errorf("unexpected token: %q, expected DISK or VOLUME", p.lastTokenKind())
 		}
+	case p.matchKeyword(KeywordDelete), p.matchKeyword(KeywordRecompress):
+		token := p.last()
+		_ = p.lexer.consumeToken()
+		action := &TTLPolicyRuleAction{
+			ActionPos: token.Pos,
+			ActionEnd: token.End,
+			Action:    token.ToString(),
+		}
+		codec, err := p.tryParseCompressionCodecs(p.Pos())
+		if err != nil {
+			return nil, err
+		}
+		action.Codec = codec
+		rule = &TTLPolicyRule{RulePos: pos, Action: action}
+	default:
+		return nil, nil // nolint
+	}
+	policy := &TTLPolicy{Item: rule}
+
+	where, err := p.tryParseWhereClause(p.Pos())
+	if err != nil {
+		return nil, err
+	}
+	policy.Where = where
+
+	groupBy, err := p.tryParseGroupByClause(p.Pos())
+	if err != nil {
+		return nil, err
+	}
+	policy.GroupBy = groupBy
+	return policy, nil
+}
+
+func (p *Parser) parseTTLExpr(pos Pos) (*TTLExpr, error) {
+	columnExpr, err := p.parseExpr(pos)
+	if err != nil {
+		return nil, err
+	}
+	policy, err := p.tryParseTTLPolicy(p.Pos())
+	if err != nil {
+		return nil, err
 	}
 	return &TTLExpr{
 		TTLPos: pos,
 		Expr:   columnExpr,
+		Policy: policy,
 	}, nil
 }
 


Test patch:
diff --git a/parser/testdata/ddl/create_table_with_ttl_policy.sql b/parser/testdata/ddl/create_table_with_ttl_policy.sql
new file mode 100644
index 0000000..3082530
--- /dev/null
+++ b/parser/testdata/ddl/create_table_with_ttl_policy.sql
@@ -0,0 +1,33 @@
+CREATE TABLE tab
+(
+    d DateTime,
+    a Int
+)
+    ENGINE = MergeTree
+PARTITION BY toYYYYMM(d)
+ORDER BY d
+TTL d + INTERVAL 1 MONTH DELETE,
+    d + INTERVAL 1 WEEK TO VOLUME 'aaa',
+    d + INTERVAL 2 WEEK TO DISK 'bbb';
+
+
+CREATE TABLE table_with_where
+(
+    d DateTime,
+    a Int
+)
+    ENGINE = MergeTree
+PARTITION BY toYYYYMM(d)
+ORDER BY d
+TTL d + INTERVAL 1 MONTH DELETE WHERE toDayOfWeek(d) = 1;
+
+CREATE TABLE table_for_recompression
+(
+    d DateTime,
+    key UInt64,
+    value String
+) ENGINE MergeTree()
+ORDER BY tuple()
+PARTITION BY key
+TTL d + INTERVAL 1 MONTH RECOMPRESS CODEC(ZSTD(17)), d + INTERVAL 1 YEAR RECOMPRESS CODEC(LZ4HC(10))
+SETTINGS min_rows_for_wide_part = 0, min_bytes_for_wide_part = 0;
diff --git a/parser/testdata/ddl/format/create_mv_with_order_by.sql b/parser/testdata/ddl/format/create_mv_with_order_by.sql
index 8c29062..8c40585 100644
--- a/parser/testdata/ddl/format/create_mv_with_order_by.sql
+++ b/parser/testdata/ddl/format/create_mv_with_order_by.sql
@@ -13,5 +13,5 @@ AS
 SELECT * FROM test_table;
 
 -- Format SQL:
-CREATE MATERIALIZED VIEW IF NOT EXISTS test_mv ENGINE = ReplacingMergeTree() PRIMARY KEY (id) ORDER BY (id)  AS SELECT * FROM test_table;
+CREATE MATERIALIZED VIEW IF NOT EXISTS test_mv ENGINE = ReplacingMergeTree() PRIMARY KEY (id) ORDER BY (id) AS SELECT * FROM test_table;
 CREATE MATERIALIZED VIEW IF NOT EXISTS test_mv ENGINE = ReplacingMergeTree() PRIMARY KEY (id) AS SELECT * FROM test_table;
diff --git a/parser/testdata/ddl/format/create_table_with_ttl_policy.sql b/parser/testdata/ddl/format/create_table_with_ttl_policy.sql
new file mode 100644
index 0000000..160da67
--- /dev/null
+++ b/parser/testdata/ddl/format/create_table_with_ttl_policy.sql
@@ -0,0 +1,40 @@
+-- Origin SQL:
+CREATE TABLE tab
+(
+    d DateTime,
+    a Int
+)
+    ENGINE = MergeTree
+PARTITION BY toYYYYMM(d)
+ORDER BY d
+TTL d + INTERVAL 1 MONTH DELETE,
+    d + INTERVAL 1 WEEK TO VOLUME 'aaa',
+    d + INTERVAL 2 WEEK TO DISK 'bbb';
+
+
+CREATE TABLE table_with_where
+(
+    d DateTime,
+    a Int
+)
+    ENGINE = MergeTree
+PARTITION BY toYYYYMM(d)
+ORDER BY d
+TTL d + INTERVAL 1 MONTH DELETE WHERE toDayOfWeek(d) = 1;
+
+CREATE TABLE table_for_recompression
+(
+    d DateTime,
+    key UInt64,
+    value String
+) ENGINE MergeTree()
+ORDER BY tuple()
+PARTITION BY key
+TTL d + INTERVAL 1 MONTH RECOMPRESS CODEC(ZSTD(17)), d + INTERVAL 1 YEAR RECOMPRESS CODEC(LZ4HC(10))
+SETTINGS min_rows_for_wide_part = 0, min_bytes_for_wide_part = 0;
+
+
+-- Format SQL:
+CREATE TABLE tab (d DateTime, a Int) ENGINE = MergeTree PARTITION BY toYYYYMM(d) TTL d + INTERVAL 1 MONTH DELETE, d + INTERVAL 1 WEEK TO VOLUME 'aaa', d + INTERVAL 2 WEEK TO DISK 'bbb' ORDER BY d;
+CREATE TABLE table_with_where (d DateTime, a Int) ENGINE = MergeTree PARTITION BY toYYYYMM(d) TTL d + INTERVAL 1 MONTH DELETE WHERE toDayOfWeek(d) = 1 ORDER BY d;
+CREATE TABLE table_for_recompression (d DateTime, key UInt64, value String) ENGINE = MergeTree() PARTITION BY key TTL d + INTERVAL 1 MONTH RECOMPRESS CODEC(ZSTD(17)), d + INTERVAL 1 YEAR RECOMPRESS CODEC(LZ4HC(10)) SETTINGS min_rows_for_wide_part=0, min_bytes_for_wide_part=0 ORDER BY tuple();
diff --git a/parser/testdata/ddl/output/attach_table_basic.sql.golden.json b/parser/testdata/ddl/output/attach_table_basic.sql.golden.json
index b5ae026..708bfd6 100644
--- a/parser/testdata/ddl/output/attach_table_basic.sql.golden.json
+++ b/parser/testdata/ddl/output/attach_table_basic.sql.golden.json
@@ -419,7 +419,8 @@
               },
               "HasGlobal": false,
               "HasNot": false
-            }
+            },
+            "Policy": null
           }
         ]
       },
@@ -470,7 +471,7 @@
               "ColumnArgList": null
             },
             "Alias": null,
-            "Direction": "None"
+            "Direction": ""
           }
         ]
       }
diff --git a/parser/testdata/ddl/output/create_materialized_view_with_empty_table_schema.sql.golden.json b/parser/testdata/ddl/output/create_materialized_view_with_empty_table_schema.sql.golden.json
index ac35e60..5810051 100644
--- a/parser/testdata/ddl/output/create_materialized_view_with_empty_table_schema.sql.golden.json
+++ b/parser/testdata/ddl/output/create_materialized_view_with_empty_table_schema.sql.golden.json
@@ -132,7 +132,7 @@
               "ColumnArgList": null
             },
             "Alias": null,
-            "Direction": "None"
+            "Direction": ""
           }
         ]
       }
@@ -359,7 +359,7 @@
                                     }
                                   },
                                   "Alias": null,
-                                  "Direction": "None"
+                                  "Direction": ""
                                 }
                               ]
                             },
diff --git a/parser/testdata/ddl/output/create_table_basic.sql.golden.json b/parser/testdata/ddl/output/create_table_basic.sql.golden.json
index c87a758..f1fd3c9 100644
--- a/parser/testdata/ddl/output/create_table_basic.sql.golden.json
+++ b/parser/testdata/ddl/output/create_table_basic.sql.golden.json
@@ -863,7 +863,8 @@
               },
               "HasGlobal": false,
               "HasNot": false
-            }
+            },
+            "Policy": null
           }
         ]
       },
@@ -914,7 +915,7 @@
               "ColumnArgList": null
             },
             "Alias": null,
-            "Direction": "None"
+            "Direction": ""
           }
         ]
       }
diff --git a/parser/testdata/ddl/output/create_table_with_codec_delta.sql.golden.json b/parser/testdata/ddl/output/create_table_with_codec_delta.sql.golden.json
index 8ba0468..94939ec 100644
--- a/parser/testdata/ddl/output/create_table_with_codec_delta.sql.golden.json
+++ b/parser/testdata/ddl/output/create_table_with_codec_delta.sql.golden.json
@@ -561,7 +561,8 @@
               },
               "HasGlobal": false,
               "HasNot": false
-            }
+            },
+            "Policy": null
           },
           {
             "TTLPos": 535,
@@ -613,7 +614,8 @@
               },
               "HasGlobal": false,
               "HasNot": false
-            }
+            },
+            "Policy": null
           }
         ]
       },
@@ -757,7 +759,7 @@
               "ColumnArgList": null
             },
             "Alias": null,
-            "Direction": "None"
+            "Direction": ""
           }
         ]
       }
diff --git a/parser/testdata/ddl/output/create_table_with_enum_fields.sql.golden.json b/parser/testdata/ddl/output/create_table_with_enum_fields.sql.golden.json
index c1cf590..cdefd55 100644
--- a/parser/testdata/ddl/output/create_table_with_enum_fields.sql.golden.json
+++ b/parser/testdata/ddl/output/create_table_with_enum_fields.sql.golden.json
@@ -399,7 +399,8 @@
               },
               "HasGlobal": false,
               "HasNot": false
-            }
+            },
+            "Policy": null
           }
         ]
       },
@@ -461,7 +462,7 @@
               "ColumnArgList": null
             },
             "Alias": null,
-            "Direction": "None"
+            "Direction": ""
           }
         ]
       }
diff --git a/parser/testdata/ddl/output/create_table_with_index.sql.golden.json b/parser/testdata/ddl/output/create_table_with_index.sql.golden.json
index 816f63a..9344c35 100644
--- a/parser/testdata/ddl/output/create_table_with_index.sql.golden.json
+++ b/parser/testdata/ddl/output/create_table_with_index.sql.golden.json
@@ -615,7 +615,8 @@
               },
               "HasGlobal": false,
               "HasNot": false
-            }
+            },
+            "Policy": null
           },
           {
             "TTLPos": 641,
@@ -667,7 +668,8 @@
               },
               "HasGlobal": false,
               "HasNot": false
-            }
+            },
+            "Policy": null
           }
         ]
       },
@@ -811,7 +813,7 @@
               "ColumnArgList": null
             },
             "Alias": null,
-            "Direction": "None"
+            "Direction": ""
           }
         ]
       }
diff --git a/parser/testdata/ddl/output/create_table_with_keyword_partition_by.sql.golden.json b/parser/testdata/ddl/output/create_table_with_keyword_partition_by.sql.golden.json
index 5201d5e..8498bda 100644
--- a/parser/testdata/ddl/output/create_table_with_keyword_partition_by.sql.golden.json
+++ b/parser/testdata/ddl/output/create_table_with_keyword_partition_by.sql.golden.json
@@ -238,7 +238,7 @@
               "ColumnArgList": null
             },
             "Alias": null,
-            "Direction": "None"
+            "Direction": ""
           }
         ]
       }
diff --git a/parser/testdata/ddl/output/create_table_with_nullable.sql.golden.json b/parser/testdata/ddl/output/create_table_with_nullable.sql.golden.json
index 5ca1b1a..b63320b 100644
--- a/parser/testdata/ddl/output/create_table_with_nullable.sql.golden.json
+++ b/parser/testdata/ddl/output/create_table_with_nullable.sql.golden.json
@@ -416,7 +416,7 @@
               "NameEnd": 386
             },
             "Alias": null,
-            "Direction": "None"
+            "Direction": ""
           }
         ]
       }
diff --git a/parser/testdata/ddl/output/create_table_with_on_clsuter.sql.golden.json b/parser/testdata/ddl/output/create_table_with_on_clsuter.sql.golden.json
index b5ae026..708bfd6 100644
--- a/parser/testdata/ddl/output/create_table_with_on_clsuter.sql.golden.json
+++ b/parser/testdata/ddl/output/create_table_with_on_clsuter.sql.golden.json
@@ -419,7 +419,8 @@
               },
               "HasGlobal": false,
               "HasNot": false
-            }
+            },
+            "Policy": null
           }
         ]
       },
@@ -470,7 +471,7 @@
               "ColumnArgList": null
             },
             "Alias": null,
-            "Direction": "None"
+            "Direction": ""
           }
         ]
       }
diff --git a/parser/testdata/ddl/output/create_table_with_sample_by.sql.golden.json b/parser/testdata/ddl/output/create_table_with_sample_by.sql.golden.json
index 2df3fb1..ab1836d 100644
--- a/parser/testdata/ddl/output/create_table_with_sample_by.sql.golden.json
+++ b/parser/testdata/ddl/output/create_table_with_sample_by.sql.golden.json
@@ -298,7 +298,7 @@
               "ColumnArgList": null
             },
             "Alias": null,
-            "Direction": "None"
+            "Direction": ""
           }
         ]
       }
diff --git a/parser/testdata/ddl/output/create_table_with_ttl_policy.sql.golden.json b/parser/testdata/ddl/output/create_table_with_ttl_policy.sql.golden.json
new file mode 100644
index 0000000..564f25c
--- /dev/null
+++ b/parser/testdata/ddl/output/create_table_with_ttl_policy.sql.golden.json
@@ -0,0 +1,888 @@
+[
+  {
+    "CreatePos": 0,
+    "StatementEnd": 203,
+    "Name": {
+      "Database": null,
+      "Table": {
+        "Name": "tab",
+        "QuoteType": 1,
+        "NamePos": 13,
+        "NameEnd": 16
+      }
+    },
+    "IfNotExists": false,
+    "UUID": null,
+    "OnCluster": null,
+    "TableSchema": {
+      "SchemaPos": 17,
+      "SchemaEnd": 45,
+      "Columns": [
+        {
+          "NamePos": 23,
+          "ColumnEnd": 33,
+          "Name": {
+            "Ident": {
+              "Name": "d",
+              "QuoteType": 1,
+              "NamePos": 23,
+              "NameEnd": 24
+            },
+            "DotIdent": null
+          },
+          "Type": {
+            "Name": {
+              "Name": "DateTime",
+              "QuoteType": 1,
+              "NamePos": 25,
+              "NameEnd": 33
+            }
+          },
+          "NotNull": null,
+          "Nullable": null,
+          "DefaultExpr": null,
+          "MaterializedExpr": null,
+          "AliasExpr": null,
+          "Codec": null,
+          "TTL": null,
+          "Comment": null,
+          "CompressionCodec": null
+        },
+        {
+          "NamePos": 39,
+          "ColumnEnd": 44,
+          "Name": {
+            "Ident": {
+              "Name": "a",
+              "QuoteType": 1,
+              "NamePos": 39,
+              "NameEnd": 40
+            },
+            "DotIdent": null
+          },
+          "Type": {
+            "Name": {
+              "Name": "Int",
+              "QuoteType": 1,
+              "NamePos": 41,
+              "NameEnd": 44
+            }
+          },
+          "NotNull": null,
+          "Nullable": null,
+          "DefaultExpr": null,
+          "MaterializedExpr": null,
+          "AliasExpr": null,
+          "Codec": null,
+          "TTL": null,
+          "Comment": null,
+          "CompressionCodec": null
+        }
+      ],
+      "AliasTable": null,
+      "TableFunction": null
+    },
+    "Engine": {
+      "EnginePos": 51,
+      "EngineEnd": 203,
+      "Name": "MergeTree",
+      "Params": null,
+      "PrimaryKey": null,
+      "PartitionBy": {
+        "PartitionPos": 70,
+        "Expr": {
+          "ListPos": 83,
+          "ListEnd": 93,
+          "HasDistinct": false,
+          "Items": [
+            {
+              "Expr": {
+                "Name": {
+                  "Name": "toYYYYMM",
+                  "QuoteType": 1,
+                  "NamePos": 83,
+                  "NameEnd": 91
+                },
+                "Params": {
+                  "LeftParenPos": 91,
+                  "RightParenPos": 93,
+                  "Items": {
+                    "ListPos": 92,
+                    "ListEnd": 93,
+                    "HasDistinct": false,
+                    "Items": [
+                      {
+                        "Expr": {
+                          "Name": "d",
+                          "QuoteType": 1,
+                          "NamePos": 92,
+                          "NameEnd": 93
+                        },
+                        "Alias": null
+                      }
+                    ]
+                  },
+                  "ColumnArgList": null
+                }
+              },
+              "Alias": null
+            }
+          ]
+        }
+      },
+      "SampleBy": null,
+      "TTL": {
+        "TTLPos": 106,
+        "ListEnd": 203,
+        "Items": [
+          {
+            "TTLPos": 106,
+            "Expr": {
+              "LeftExpr": {
+                "Name": "d",
+                "QuoteType": 1,
+                "NamePos": 110,
+                "NameEnd": 111
+              },
+              "Operation": "+",
+              "RightExpr": {
+                "IntervalPos": 114,
+                "Expr": {
+                  "NumPos": 123,
+                  "NumEnd": 124,
+                  "Literal": "1",
+                  "Base": 10
+                },
+                "Unit": {
+                  "Name": "MONTH",
+                  "QuoteType": 1,
+                  "NamePos": 125,
+                  "NameEnd": 130
+                }
+              },
+              "HasGlobal": false,
+              "HasNot": false
+            },
+            "Policy": {
+              "Item": {
+                "RulePos": 131,
+                "ToVolume": null,
+                "ToDisk": null,
+                "Action": {
+                  "ActionPos": 131,
+                  "ActionEnd": 137,
+                  "Action": "DELETE",
+                  "Codec": null
+                }
+              },
+              "Where": null,
+              "GroupBy": null
+            }
+          },
+          {
+            "TTLPos": 106,
+            "Expr": {
+              "LeftExpr": {
+                "Name": "d",
+                "QuoteType": 1,
+                "NamePos": 143,
+                "NameEnd": 144
+              },
+              "Operation": "+",
+              "RightExpr": {
+                "IntervalPos": 147,
+                "Expr": {
+                  "NumPos": 156,
+                  "NumEnd": 157,
+                  "Literal": "1",
+                  "Base": 10
+                },
+                "Unit": {
+                  "Name": "WEEK",
+                  "QuoteType": 1,
+                  "NamePos": 158,
+                  "NameEnd": 162
+                }
+              },
+              "HasGlobal": false,
+              "HasNot": false
+            },
+            "Policy": {
+              "Item": {
+                "RulePos": 163,
+                "ToVolume": {
+                  "LiteralPos": 174,
+                  "LiteralEnd": 177,
+                  "Literal": "aaa"
+                },
+                "ToDisk": null,
+                "Action": null
+              },
+              "Where": null,
+              "GroupBy": null
+            }
+          },
+          {
+            "TTLPos": 106,
+            "Expr": {
+              "LeftExpr": {
+                "Name": "d",
+                "QuoteType": 1,
+                "NamePos": 184,
+                "NameEnd": 185
+              },
+              "Operation": "+",
+              "RightExpr": {
+                "IntervalPos": 188,
+                "Expr": {
+                  "NumPos": 197,
+                  "NumEnd": 198,
+                  "Literal": "2",
+                  "Base": 10
+                },
+                "Unit": {
+                  "Name": "WEEK",
+                  "QuoteType": 1,
+                  "NamePos": 199,
+                  "NameEnd": 203
+                }
+              },
+              "HasGlobal": false,
+              "HasNot": false
+            },
+            "Policy": {
+              "Item": {
+                "RulePos": 204,
+                "ToVolume": null,
+                "ToDisk": {
+                  "LiteralPos": 213,
+                  "LiteralEnd": 216,
+                  "Literal": "bbb"
+                },
+                "Action": null
+              },
+              "Where": null,
+              "GroupBy": null
+            }
+          }
+        ]
+      },
+      "Settings": null,
+      "OrderBy": {
+        "OrderPos": 95,
+        "ListEnd": 105,
+        "Items": [
+          {
+            "OrderPos": 95,
+            "Expr": {
+              "Name": "d",
+              "QuoteType": 1,
+              "NamePos": 104,
+              "NameEnd": 105
+            },
+            "Alias": null,
+            "Direction": ""
+          }
+        ]
+      }
+    },
+    "SubQuery": null,
+    "HasTemporary": false,
+    "Comment": null
+  },
+  {
+    "CreatePos": 221,
+    "StatementEnd": 364,
+    "Name": {
+      "Database": null,
+      "Table": {
+        "Name": "table_with_where",
+        "QuoteType": 1,
+        "NamePos": 234,
+        "NameEnd": 250
+      }
+    },
+    "IfNotExists": false,
+    "UUID": null,
+    "OnCluster": null,
+    "TableSchema": {
+      "SchemaPos": 251,
+      "SchemaEnd": 279,
+      "Columns": [
+        {
+          "NamePos": 257,
+          "ColumnEnd": 267,
+          "Name": {
+            "Ident": {
+              "Name": "d",
+              "QuoteType": 1,
+              "NamePos": 257,
+              "NameEnd": 258
+            },
+            "DotIdent": null
+          },
+          "Type": {
+            "Name": {
+              "Name": "DateTime",
+              "QuoteType": 1,
+              "NamePos": 259,
+              "NameEnd": 267
+            }
+          },
+          "NotNull": null,
+          "Nullable": null,
+          "DefaultExpr": null,
+          "MaterializedExpr": null,
+          "AliasExpr": null,
+          "Codec": null,
+          "TTL": null,
+          "Comment": null,
+          "CompressionCodec": null
+        },
+        {
+          "NamePos": 273,
+          "ColumnEnd": 278,
+          "Name": {
+            "Ident": {
+              "Name": "a",
+              "QuoteType": 1,
+              "NamePos": 273,
+              "NameEnd": 274
+            },
+            "DotIdent": null
+          },
+          "Type": {
+            "Name": {
+              "Name": "Int",
+              "QuoteType": 1,
+              "NamePos": 275,
+              "NameEnd": 278
+            }
+          },
+          "NotNull": null,
+          "Nullable": null,
+          "DefaultExpr": null,
+          "MaterializedExpr": null,
+          "AliasExpr": null,
+          "Codec": null,
+          "TTL": null,
+          "Comment": null,
+          "CompressionCodec": null
+        }
+      ],
+      "AliasTable": null,
+      "TableFunction": null
+    },
+    "Engine": {
+      "EnginePos": 285,
+      "EngineEnd": 364,
+      "Name": "MergeTree",
+      "Params": null,
+      "PrimaryKey": null,
+      "PartitionBy": {
+        "PartitionPos": 304,
+        "Expr": {
+          "ListPos": 317,
+          "ListEnd": 327,
+          "HasDistinct": false,
+          "Items": [
+            {
+              "Expr": {
+                "Name": {
+                  "Name": "toYYYYMM",
+                  "QuoteType": 1,
+                  "NamePos": 317,
+                  "NameEnd": 325
+                },
+                "Params": {
+                  "LeftParenPos": 325,
+                  "RightParenPos": 327,
+                  "Items": {
+                    "ListPos": 326,
+                    "ListEnd": 327,
+                    "HasDistinct": false,
+                    "Items": [
+                      {
+                        "Expr": {
+                          "Name": "d",
+                          "QuoteType": 1,
+                          "NamePos": 326,
+                          "NameEnd": 327
+                        },
+                        "Alias": null
+                      }
+                    ]
+                  },
+                  "ColumnArgList": null
+                }
+              },
+              "Alias": null
+            }
+          ]
+        }
+      },
+      "SampleBy": null,
+      "TTL": {
+        "TTLPos": 340,
+        "ListEnd": 364,
+        "Items": [
+          {
+            "TTLPos": 340,
+            "Expr": {
+              "LeftExpr": {
+                "Name": "d",
+                "QuoteType": 1,
+                "NamePos": 344,
+                "NameEnd": 345
+              },
+              "Operation": "+",
+              "RightExpr": {
+                "IntervalPos": 348,
+                "Expr": {
+                  "NumPos": 357,
+                  "NumEnd": 358,
+                  "Literal": "1",
+                  "Base": 10
+                },
+                "Unit": {
+                  "Name": "MONTH",
+                  "QuoteType": 1,
+                  "NamePos": 359,
+                  "NameEnd": 364
+                }
+              },
+              "HasGlobal": false,
+              "HasNot": false
+            },
+            "Policy": {
+              "Item": {
+                "RulePos": 365,
+                "ToVolume": null,
+                "ToDisk": null,
+                "Action": {
+                  "ActionPos": 365,
+                  "ActionEnd": 371,
+                  "Action": "DELETE",
+                  "Codec": null
+                }
+              },
+              "Where": {
+                "WherePos": 372,
+                "Expr": {
+                  "LeftExpr": {
+                    "Name": {
+                      "Name": "toDayOfWeek",
+                      "QuoteType": 1,
+                      "NamePos": 378,
+                      "NameEnd": 389
+                    },
+                    "Params": {
+                      "LeftParenPos": 389,
+                      "RightParenPos": 391,
+                      "Items": {
+                        "ListPos": 390,
+                        "ListEnd": 391,
+                        "HasDistinct": false,
+                        "Items": [
+                          {
+                            "Expr": {
+                              "Name": "d",
+                              "QuoteType": 1,
+                              "NamePos": 390,
+                              "NameEnd": 391
+                            },
+                            "Alias": null
+                          }
+                        ]
+                      },
+                      "ColumnArgList": null
+                    }
+                  },
+                  "Operation": "=",
+                  "RightExpr": {
+                    "NumPos": 395,
+                    "NumEnd": 396,
+                    "Literal": "1",
+                    "Base": 10
+                  },
+                  "HasGlobal": false,
+                  "HasNot": false
+                }
+              },
+              "GroupBy": null
+            }
+          }
+        ]
+      },
+      "Settings": null,
+      "OrderBy": {
+        "OrderPos": 329,
+        "ListEnd": 339,
+        "Items": [
+          {
+            "OrderPos": 329,
+            "Expr": {
+              "Name": "d",
+              "QuoteType": 1,
+              "NamePos": 338,
+              "NameEnd": 339
+            },
+            "Alias": null,
+            "Direction": ""
+          }
+        ]
+      }
+    },
+    "SubQuery": null,
+    "HasTemporary": false,
+    "Comment": null
+  },
+  {
+    "CreatePos": 399,
+    "StatementEnd": 707,
+    "Name": {
+      "Database": null,
+      "Table": {
+        "Name": "table_for_recompression",
+        "QuoteType": 1,
+        "NamePos": 412,
+        "NameEnd": 435
+      }
+    },
+    "IfNotExists": false,
+    "UUID": null,
+    "OnCluster": null,
+    "TableSchema": {
+      "SchemaPos": 436,
+      "SchemaEnd": 487,
+      "Columns": [
+        {
+          "NamePos": 442,
+          "ColumnEnd": 452,
+          "Name": {
+            "Ident": {
+              "Name": "d",
+              "QuoteType": 1,
+              "NamePos": 442,
+              "NameEnd": 443
+            },
+            "DotIdent": null
+          },
+          "Type": {
+            "Name": {
+              "Name": "DateTime",
+              "QuoteType": 1,
+              "NamePos": 444,
+              "NameEnd": 452
+            }
+          },
+          "NotNull": null,
+          "Nullable": null,
+          "DefaultExpr": null,
+          "MaterializedExpr": null,
+          "AliasExpr": null,
+          "Codec": null,
+          "TTL": null,
+          "Comment": null,
+          "CompressionCodec": null
+        },
+        {
+          "NamePos": 458,
+          "ColumnEnd": 468,
+          "Name": {
+            "Ident": {
+              "Name": "key",
+              "QuoteType": 1,
+              "NamePos": 458,
+              "NameEnd": 461
+            },
+            "DotIdent": null
+          },
+          "Type": {
+            "Name": {
+              "Name": "UInt64",
+              "QuoteType": 1,
+              "NamePos": 462,
+              "NameEnd": 468
+            }
+          },
+          "NotNull": null,
+          "Nullable": null,
+          "DefaultExpr": null,
+          "MaterializedExpr": null,
+          "AliasExpr": null,
+          "Codec": null,
+          "TTL": null,
+          "Comment": null,
+          "CompressionCodec": null
+        },
+        {
+          "NamePos": 474,
+          "ColumnEnd": 486,
+          "Name": {
+            "Ident": {
+              "Name": "value",
+              "QuoteType": 1,
+              "NamePos": 474,
+              "NameEnd": 479
+            },
+            "DotIdent": null
+          },
+          "Type": {
+            "Name": {
+              "Name": "String",
+              "QuoteType": 1,
+              "NamePos": 480,
+              "NameEnd": 486
+            }
+          },
+          "NotNull": null,
+          "Nullable": null,
+          "DefaultExpr": null,
+          "MaterializedExpr": null,
+          "AliasExpr": null,
+          "Codec": null,
+          "TTL": null,
+          "Comment": null,
+          "CompressionCodec": null
+        }
+      ],
+      "AliasTable": null,
+      "TableFunction": null
+    },
+    "Engine": {
+      "EnginePos": 489,
+      "EngineEnd": 707,
+      "Name": "MergeTree",
+      "Params": {
+        "LeftParenPos": 505,
+        "RightParenPos": 506,
+        "Items": {
+          "ListPos": 506,
+          "ListEnd": 506,
+          "HasDistinct": false,
+          "Items": []
+        },
+        "ColumnArgList": null
+      },
+      "PrimaryKey": null,
+      "PartitionBy": {
+        "PartitionPos": 525,
+        "Expr": {
+          "ListPos": 538,
+          "ListEnd": 541,
+          "HasDistinct": false,
+          "Items": [
+            {
+              "Expr": {
+                "Name": "key",
+                "QuoteType": 1,
+                "NamePos": 538,
+                "NameEnd": 541
+              },
+              "Alias": null
+            }
+          ]
+        }
+      },
+      "SampleBy": null,
+      "TTL": {
+        "TTLPos": 542,
+        "ListEnd": 614,
+        "Items": [
+          {
+            "TTLPos": 542,
+            "Expr": {
+              "LeftExpr": {
+                "Name": "d",
+                "QuoteType": 1,
+                "NamePos": 546,
+                "NameEnd": 547
+              },
+              "Operation": "+",
+              "RightExpr": {
+                "IntervalPos": 550,
+                "Expr": {
+                  "NumPos": 559,
+                  "NumEnd": 560,
+                  "Literal": "1",
+                  "Base": 10
+                },
+                "Unit": {
+                  "Name": "MONTH",
+                  "QuoteType": 1,
+                  "NamePos": 561,
+                  "NameEnd": 566
+                }
+              },
+              "HasGlobal": false,
+              "HasNot": false
+            },
+            "Policy": {
+              "Item": {
+                "RulePos": 567,
+                "ToVolume": null,
+                "ToDisk": null,
+                "Action": {
+                  "ActionPos": 567,
+                  "ActionEnd": 577,
+                  "Action": "RECOMPRESS",
+                  "Codec": {
+                    "CodecPos": 578,
+                    "RightParenPos": 593,
+                    "Type": null,
+                    "TypeLevel": null,
+                    "Name": {
+                      "Name": "ZSTD",
+                      "QuoteType": 1,
+                      "NamePos": 584,
+                      "NameEnd": 588
+                    },
+                    "Level": {
+                      "NumPos": 588,
+                      "NumEnd": 591,
+                      "Literal": "17",
+                      "Base": 10
+                    }
+                  }
+                }
+              },
+              "Where": null,
+              "GroupBy": null
+            }
+          },
+          {
+            "TTLPos": 542,
+            "Expr": {
+              "LeftExpr": {
+                "Name": "d",
+                "QuoteType": 1,
+                "NamePos": 595,
+                "NameEnd": 596
+              },
+              "Operation": "+",
+              "RightExpr": {
+                "IntervalPos": 599,
+                "Expr": {
+                  "NumPos": 608,
+                  "NumEnd": 609,
+                  "Literal": "1",
+                  "Base": 10
+                },
+                "Unit": {
+                  "Name": "YEAR",
+                  "QuoteType": 1,
+                  "NamePos": 610,
+                  "NameEnd": 614
+                }
+              },
+              "HasGlobal": false,
+              "HasNot": false
+            },
+            "Policy": {
+              "Item": {
+                "RulePos": 615,
+                "ToVolume": null,
+                "ToDisk": null,
+                "Action": {
+                  "ActionPos": 615,
+                  "ActionEnd": 625,
+                  "Action": "RECOMPRESS",
+                  "Codec": {
+                    "CodecPos": 626,
+                    "RightParenPos": 642,
+                    "Type": null,
+                    "TypeLevel": null,
+                    "Name": {
+                      "Name": "LZ4HC",
+                      "QuoteType": 1,
+                      "NamePos": 632,
+                      "NameEnd": 637
+                    },
+                    "Level": {
+                      "NumPos": 637,
+                      "NumEnd": 640,
+                      "Literal": "10",
+                      "Base": 10
+                    }
+                  }
+                }
+              },
+              "Where": null,
+              "GroupBy": null
+            }
+          }
+        ]
+      },
+      "Settings": {
+        "SettingsPos": 643,
+        "ListEnd": 707,
+        "Items": [
+          {
+            "SettingsPos": 652,
+            "Name": {
+              "Name": "min_rows_for_wide_part",
+              "QuoteType": 1,
+              "NamePos": 652,
+              "NameEnd": 674
+            },
+            "Expr": {
+              "NumPos": 677,
+              "NumEnd": 678,
+              "Literal": "0",
+              "Base": 10
+            }
+          },
+          {
+            "SettingsPos": 680,
+            "Name": {
+              "Name": "min_bytes_for_wide_part",
+              "QuoteType": 1,
+              "NamePos": 680,
+              "NameEnd": 703
+            },
+            "Expr": {
+              "NumPos": 706,
+              "NumEnd": 707,
+              "Literal": "0",
+              "Base": 10
+            }
+          }
+        ]
+      },
+      "OrderBy": {
+        "OrderPos": 508,
+        "ListEnd": 523,
+        "Items": [
+          {
+            "OrderPos": 508,
+            "Expr": {
+              "Name": {
+                "Name": "tuple",
+                "QuoteType": 1,
+                "NamePos": 517,
+                "NameEnd": 522
+              },
+              "Params": {
+                "LeftParenPos": 522,
+                "RightParenPos": 523,
+                "Items": {
+                  "ListPos": 523,
+                  "ListEnd": 523,
+                  "HasDistinct": false,
+                  "Items": []
+                },
+                "ColumnArgList": null
+              }
+            },
+            "Alias": null,
+            "Direction": ""
+          }
+        ]
+      }
+    },
+    "SubQuery": null,
+    "HasTemporary": false,
+    "Comment": null
+  }
+]
\ No newline at end of file
diff --git a/parser/testdata/ddl/output/create_table_with_uuid.sql.golden.json b/parser/testdata/ddl/output/create_table_with_uuid.sql.golden.json
index ae96c7c..c69a8ad 100644
--- a/parser/testdata/ddl/output/create_table_with_uuid.sql.golden.json
+++ b/parser/testdata/ddl/output/create_table_with_uuid.sql.golden.json
@@ -425,7 +425,8 @@
               },
               "HasGlobal": false,
               "HasNot": false
-            }
+            },
+            "Policy": null
           }
         ]
       },
@@ -476,7 +477,7 @@
               "ColumnArgList": null
             },
             "Alias": null,
-            "Direction": "None"
+            "Direction": ""
           }
         ]
       }
diff --git a/parser/testdata/dml/output/alter_table_with_modify_ttl.sql.golden.json b/parser/testdata/dml/output/alter_table_with_modify_ttl.sql.golden.json
index 136a58c..f21c4fa 100644
--- a/parser/testdata/dml/output/alter_table_with_modify_ttl.sql.golden.json
+++ b/parser/testdata/dml/output/alter_table_with_modify_ttl.sql.golden.json
@@ -56,7 +56,8 @@
             },
             "HasGlobal": false,
             "HasNot": false
-          }
+          },
+          "Policy": null
         }
       }
     ]
diff --git a/parser/testdata/dml/output/create_column_with_ttl.sql.golden.json b/parser/testdata/dml/output/create_column_with_ttl.sql.golden.json
index 64babed..78652cf 100644
--- a/parser/testdata/dml/output/create_column_with_ttl.sql.golden.json
+++ b/parser/testdata/dml/output/create_column_with_ttl.sql.golden.json
@@ -105,7 +105,8 @@
                   },
                   "HasGlobal": false,
                   "HasNot": false
-                }
+                },
+                "Policy": null
               }
             ]
           },
@@ -169,7 +170,8 @@
                   },
                   "HasGlobal": false,
                   "HasNot": false
-                }
+                },
+                "Policy": null
               }
             ]
           },
@@ -216,7 +218,7 @@
               }
             },
             "Alias": null,
-            "Direction": "None"
+            "Direction": ""
           }
         ]
       }
diff --git a/parser/testdata/query/output/select_order_by_timestamp.sql.golden.json b/parser/testdata/query/output/select_order_by_timestamp.sql.golden.json
index f122fc0..2c64363 100644
--- a/parser/testdata/query/output/select_order_by_timestamp.sql.golden.json
+++ b/parser/testdata/query/output/select_order_by_timestamp.sql.golden.json
@@ -59,7 +59,7 @@
             "NameEnd": 47
           },
           "Alias": null,
-          "Direction": "None"
+          "Direction": ""
         }
       ]
     },
diff --git a/parser/testdata/query/output/select_simple_with_group_by_with_cube_totals.sql.golden.json b/parser/testdata/query/output/select_simple_with_group_by_with_cube_totals.sql.golden.json
index 00e350a..5e85a82 100644
--- a/parser/testdata/query/output/select_simple_with_group_by_with_cube_totals.sql.golden.json
+++ b/parser/testdata/query/output/select_simple_with_group_by_with_cube_totals.sql.golden.json
@@ -119,7 +119,7 @@
             "NameEnd": 86
           },
           "Alias": null,
-          "Direction": "None"
+          "Direction": ""
         }
       ]
     },