Repository: AfterShip/clickhouse-sql-parser
Pull request #80 (2024-07-16 08:57:29)

Fix the "=" operator should be present if the setting `variable` carries a value
ref: https://clickhouse.com/docs/en/sql-reference/statements/alter/role

This syntax may be an error
```sql
ALTER ROLE r3_01293 SETTINGS max_memory_usage 5000000;
```


Patch:
diff --git a/parser/ast.go b/parser/ast.go
index f654d8c..a68ed76 100644
--- a/parser/ast.go
+++ b/parser/ast.go
@@ -324,8 +324,10 @@ func (a *AlterTableDetachPartition) Accept(visitor ASTVisitor) error {
 }
 
 type AlterTableDropPartition struct {
-	DropPos   Pos
-	Partition *PartitionClause
+	DropPos     Pos
+	HasDetached bool
+	Partition   *PartitionClause
+	Settings    *SettingsClause
 }
 
 func (a *AlterTableDropPartition) Pos() Pos {
@@ -333,6 +335,9 @@ func (a *AlterTableDropPartition) Pos() Pos {
 }
 
 func (a *AlterTableDropPartition) End() Pos {
+	if a.Settings != nil {
+		a.Settings.End()
+	}
 	return a.Partition.End()
 }
 
@@ -343,7 +348,14 @@ func (a *AlterTableDropPartition) AlterType() string {
 func (a *AlterTableDropPartition) String(level int) string {
 	var builder strings.Builder
 	builder.WriteString("DROP ")
+	if a.HasDetached {
+		builder.WriteString("DETACHED ")
+	}
 	builder.WriteString(a.Partition.String(level))
+	if a.Settings != nil {
+		builder.WriteByte(' ')
+		builder.WriteString(a.Settings.String(level))
+	}
 	return builder.String()
 }
 
@@ -1787,8 +1799,9 @@ func (r *RoleName) Accept(visitor ASTVisitor) error {
 }
 
 type SettingPair struct {
-	Name  *Ident
-	Value Expr
+	Name      *Ident
+	Operation TokenKind
+	Value     Expr
 }
 
 func (s *SettingPair) Pos() Pos {
@@ -1803,7 +1816,11 @@ func (s *SettingPair) String(level int) string {
 	var builder strings.Builder
 	builder.WriteString(s.Name.String(level))
 	if s.Value != nil {
-		builder.WriteByte(' ')
+		if s.Operation == opTypeEQ {
+			builder.WriteString(string(s.Operation))
+		} else {
+			builder.WriteByte(' ')
+		}
 		builder.WriteString(s.Value.String(level))
 	}
 	return builder.String()
@@ -3276,6 +3293,7 @@ type CompressionCodec struct {
 	CodecPos      Pos
 	RightParenPos Pos
 	Type          *Ident
+	TypeLevel     *NumberLiteral
 	Name          *Ident
 	Level         *NumberLiteral // compression level
 }
@@ -3293,6 +3311,11 @@ func (c *CompressionCodec) String(level int) string {
 	builder.WriteString("CODEC(")
 	if c.Type != nil {
 		builder.WriteString(c.Type.String(level))
+		if c.TypeLevel != nil {
+			builder.WriteByte('(')
+			builder.WriteString(c.TypeLevel.String(level))
+			builder.WriteByte(')')
+		}
 		builder.WriteByte(',')
 		builder.WriteByte(' ')
 	}
@@ -3309,6 +3332,14 @@ func (c *CompressionCodec) String(level int) string {
 func (c *CompressionCodec) Accept(visitor ASTVisitor) error {
 	visitor.enter(c)
 	defer visitor.leave(c)
+	if err := c.Type.Accept(visitor); err != nil {
+		return err
+	}
+	if c.TypeLevel != nil {
+		if err := c.TypeLevel.Accept(visitor); err != nil {
+			return err
+		}
+	}
 	if err := c.Name.Accept(visitor); err != nil {
 		return err
 	}
@@ -4942,6 +4973,7 @@ type SelectQuery struct {
 	LimitBy       *LimitByClause
 	Limit         *LimitClause
 	Settings      *SettingsClause
+	Format        *FormatClause
 	UnionAll      *SelectQuery
 	UnionDistinct *SelectQuery
 	Except        *SelectQuery
@@ -5026,6 +5058,10 @@ func (s *SelectQuery) String(level int) string { // nolint: funlen
 		builder.WriteString(NewLine(level))
 		builder.WriteString(s.Settings.String(level))
 	}
+	if s.Format != nil {
+		builder.WriteString(NewLine(level))
+		builder.WriteString(s.Format.String(level))
+	}
 	if s.UnionAll != nil {
 		builder.WriteString(NewLine(level))
 		builder.WriteString(" UNION ALL ")
@@ -5115,6 +5151,11 @@ func (s *SelectQuery) Accept(visitor ASTVisitor) error {
 			return err
 		}
 	}
+	if s.Format != nil {
+		if err := s.Format.Accept(visitor); err != nil {
+			return err
+		}
+	}
 	if s.UnionAll != nil {
 		if err := s.UnionAll.Accept(visitor); err != nil {
 			return err
diff --git a/parser/parse_system.go b/parser/parse_system.go
index e5a6816..817e82f 100644
--- a/parser/parse_system.go
+++ b/parser/parse_system.go
@@ -404,18 +404,28 @@ func (p *Parser) parseRoleSetting(_ Pos) (*RoleSetting, error) {
 			}, nil
 		}
 		switch {
-		case p.matchTokenKind("="),
+		case p.matchTokenKind(opTypeEQ),
 			p.matchTokenKind(TokenInt),
 			p.matchTokenKind(TokenFloat),
 			p.matchTokenKind(TokenString):
-			_ = p.tryConsumeTokenKind("=")
+			var op TokenKind
+			if token := p.tryConsumeTokenKind(opTypeEQ); token != nil {
+				op = token.Kind
+			}
 			value, err := p.parseLiteral(p.Pos())
 			if err != nil {
 				return nil, err
 			}
+			// docs: https://clickhouse.com/docs/en/sql-reference/statements/alter/role
+			// the operator "=" was required if the variable name is NOT in
+			// ["MIN", "MAX", "PROFILE"] and value is existed.
+			if value != nil && name.Name != "MIN" && name.Name != "MAX" && name.Name != "PROFILE" && op != opTypeEQ {
+				return nil, fmt.Errorf("expected operator = or no value, but got %s", op)
+			}
 			pairs = append(pairs, &SettingPair{
-				Name:  name,
-				Value: value,
+				Name:      name,
+				Operation: op,
+				Value:     value,
 			})
 		default:
 			pairs = append(pairs, &SettingPair{
diff --git a/parser/parser_alter.go b/parser/parser_alter.go
index 5cfaaac..5a3db06 100644
--- a/parser/parser_alter.go
+++ b/parser/parser_alter.go
@@ -295,10 +295,7 @@ func (p *Parser) parseAlterTableDrop(pos Pos) (AlterTableClause, error) {
 	switch {
 	case p.matchKeyword(KeywordColumn), p.matchKeyword(KeywordIndex), p.matchKeyword(KeywordProjection):
 		return p.parseAlterTableDropClause(pos)
-	case p.matchKeyword(KeywordDetached):
-		_ = p.lexer.consumeToken()
-		return p.parseAlterTableDetachPartition(pos)
-	case p.matchKeyword(KeywordPartition):
+	case p.matchKeyword(KeywordDetached), p.matchKeyword(KeywordPartition):
 		return p.parseAlterTableDropPartition(pos)
 	default:
 		return nil, errors.New("expected keyword: COLUMN|INDEX|PROJECTION|DETACHED|PARTITION")
@@ -443,6 +440,11 @@ func (p *Parser) tryParseAfterClause() (*NestedIdentifier, error) {
 
 // Syntax: ALTER TABLE DROP partitionClause
 func (p *Parser) parseAlterTableDropPartition(pos Pos) (AlterTableClause, error) {
+	var hasDetached bool
+	if p.matchKeyword(KeywordDetached) {
+		_ = p.lexer.consumeToken()
+		hasDetached = true
+	}
 	partitionPos := p.Pos()
 	if err := p.consumeKeyword(KeywordPartition); err != nil {
 		return nil, err
@@ -456,9 +458,16 @@ func (p *Parser) parseAlterTableDropPartition(pos Pos) (AlterTableClause, error)
 	}
 	partition.Expr = expr
 
+	settings, err := p.tryParseSettingsClause(p.Pos())
+	if err != nil {
+		return nil, err
+	}
+
 	return &AlterTableDropPartition{
-		DropPos:   pos,
-		Partition: partition,
+		DropPos:     pos,
+		Partition:   partition,
+		HasDetached: hasDetached,
+		Settings:    settings,
 	}, nil
 }
 
diff --git a/parser/parser_column.go b/parser/parser_column.go
index 9b56dcc..5cf3aa9 100644
--- a/parser/parser_column.go
+++ b/parser/parser_column.go
@@ -666,7 +666,7 @@ func (p *Parser) parseColumnType(_ Pos) (Expr, error) { // nolint:funlen
 			}
 			return p.parseComplexType(ident, p.Pos())
 		case p.matchTokenKind(TokenString):
-			if peekToken, err := p.lexer.peekToken(); err == nil && peekToken.Kind == "=" {
+			if peekToken, err := p.lexer.peekToken(); err == nil && peekToken.Kind == opTypeEQ {
 				// enum values
 				return p.parseEnumExpr(p.Pos())
 			}
@@ -803,10 +803,12 @@ func (p *Parser) tryParseCompressionCodecs(pos Pos) (*CompressionCodec, error) {
 	}
 	// parse DELTA if  CODEC(Delta, ZSTD(1)) or CODEC(Delta(9), ZSTD(1))
 	var codecType *Ident
+	var typeLevel *NumberLiteral
 	if strings.ToUpper(name.Name) == "DELTA" {
 		codecType = name
 		// try parse delta level
-		if _, err := p.tryParseCompressionLevel(p.Pos()); err != nil {
+		typeLevel, err = p.tryParseCompressionLevel(p.Pos())
+		if err != nil {
 			return nil, err
 		}
 		// consume comma
@@ -838,6 +840,7 @@ func (p *Parser) tryParseCompressionCodecs(pos Pos) (*CompressionCodec, error) {
 		CodecPos:      pos,
 		RightParenPos: rightParenPos,
 		Type:          codecType,
+		TypeLevel:     typeLevel,
 		Name:          name,
 		Level:         level,
 	}, nil
@@ -852,7 +855,7 @@ func (p *Parser) parseEnumValueExpr(pos Pos) (*EnumValue, error) {
 		return nil, err
 	}
 
-	if _, err := p.consumeTokenKind("="); err != nil {
+	if _, err := p.consumeTokenKind(opTypeEQ); err != nil {
 		return nil, err
 	}
 
diff --git a/parser/parser_query.go b/parser/parser_query.go
index 6ff3a5d..6efe8a6 100644
--- a/parser/parser_query.go
+++ b/parser/parser_query.go
@@ -907,6 +907,14 @@ func (p *Parser) parseSelectStmt(pos Pos) (*SelectQuery, error) { // nolint: fun
 		statementEnd = settings.End()
 	}
 
+	format, err := p.tryParseFormat(p.Pos())
+	if err != nil {
+		return nil, err
+	}
+	if format != nil {
+		statementEnd = format.End()
+	}
+
 	return &SelectQuery{
 		With:          withClause,
 		SelectPos:     pos,
@@ -924,6 +932,7 @@ func (p *Parser) parseSelectStmt(pos Pos) (*SelectQuery, error) { // nolint: fun
 		LimitBy:       limitBy,
 		Limit:         limit,
 		Settings:      settings,
+		Format:        format,
 		WithTotal:     withTotal,
 	}, nil
 }
diff --git a/parser/parser_table.go b/parser/parser_table.go
index f20f201..8473894 100644
--- a/parser/parser_table.go
+++ b/parser/parser_table.go
@@ -773,7 +773,7 @@ func (p *Parser) parseSettingsExprList(pos Pos) (*SettingExprList, error) {
 		return nil, err
 	}
 
-	if _, err := p.consumeTokenKind("="); err != nil {
+	if _, err := p.consumeTokenKind(opTypeEQ); err != nil {
 		return nil, err
 	}
 
@@ -842,7 +842,7 @@ func (p *Parser) parseEngineExpr(pos Pos) (*EngineExpr, error) {
 	if err := p.consumeKeyword(KeywordEngine); err != nil {
 		return nil, err
 	}
-	_ = p.tryConsumeTokenKind("=")
+	_ = p.tryConsumeTokenKind(opTypeEQ)
 
 	engineExpr := &EngineExpr{EnginePos: pos}
 	var engineEnd Pos


Test patch:
diff --git a/parser/testdata/ddl/format/alter_role.sql b/parser/testdata/ddl/format/alter_role.sql
index 65bd198..4dad457 100644
--- a/parser/testdata/ddl/format/alter_role.sql
+++ b/parser/testdata/ddl/format/alter_role.sql
@@ -28,18 +28,18 @@ ALTER ROLE r1_01293 ON CLUSTER cluster_1 RENAME TO r2_01293;
 ALTER ROLE r1_01293 RENAME TO r2_01293, r3_01293 RENAME TO r4_01293;
 ALTER ROLE r1_01293 SETTINGS NONE;
 ALTER ROLE r2_01293 SETTINGS PROFILE 'default';
-ALTER ROLE r3_01293 SETTINGS max_memory_usage 5000000;
-ALTER ROLE r4_01293 SETTINGS max_memory_usage MIN 5000000;
-ALTER ROLE r5_01293 SETTINGS max_memory_usage MAX 5000000;
+ALTER ROLE r3_01293 SETTINGS max_memory_usage=5000000;
+ALTER ROLE r4_01293 SETTINGS max_memory_usage MIN=5000000;
+ALTER ROLE r5_01293 SETTINGS max_memory_usage MAX=5000000;
 ALTER ROLE r6_01293 SETTINGS max_memory_usage CONST;
 ALTER ROLE r7_01293 SETTINGS max_memory_usage WRITABLE;
-ALTER ROLE r8_01293 SETTINGS max_memory_usage 5000000 MIN 4000000 MAX 6000000 CONST;
-ALTER ROLE r9_01293 SETTINGS PROFILE 'default', max_memory_usage 5000000 WRITABLE;
+ALTER ROLE r8_01293 SETTINGS max_memory_usage=5000000 MIN 4000000 MAX 6000000 CONST;
+ALTER ROLE r9_01293 SETTINGS PROFILE 'default', max_memory_usage=5000000 WRITABLE;
 ALTER ROLE r1_01293, r2_01293;
-ALTER ROLE r1_01293 SETTINGS readonly 1;
+ALTER ROLE r1_01293 SETTINGS readonly=1;
 ALTER ROLE r2_01293 SETTINGS PROFILE 'default';
-ALTER ROLE r3_01293 SETTINGS max_memory_usage 5000000 MIN 4000000 MAX 6000000 WRITABLE;
-ALTER ROLE r4_01293 SETTINGS PROFILE 'default', max_memory_usage 5000000, readonly 1;
+ALTER ROLE r3_01293 SETTINGS max_memory_usage=5000000 MIN 4000000 MAX 6000000 WRITABLE;
+ALTER ROLE r4_01293 SETTINGS PROFILE 'default', max_memory_usage=5000000, readonly=1;
 ALTER ROLE r5_01293 SETTINGS NONE;
 ALTER ROLE r1_01293@'%';
 ALTER ROLE r2_01293@'%.myhost.com';
diff --git a/parser/testdata/ddl/format/alter_table_drop_detach_partition.sql b/parser/testdata/ddl/format/alter_table_drop_detach_partition.sql
index 867990e..acf7322 100644
--- a/parser/testdata/ddl/format/alter_table_drop_detach_partition.sql
+++ b/parser/testdata/ddl/format/alter_table_drop_detach_partition.sql
@@ -3,4 +3,4 @@ ALTER TABLE app_utc_00.app_message_as_notification_organization_sent_stats_i_d_l
 
 -- Format SQL:
 ALTER TABLE app_utc_00.app_message_as_notification_organization_sent_stats_i_d_local
-DETACH PARTITION '2022-05-24' SETTINGS allow_drop_detached=1;
+DROP DETACHED PARTITION '2022-05-24' SETTINGS allow_drop_detached=1;
diff --git a/parser/testdata/ddl/format/create_role.sql b/parser/testdata/ddl/format/create_role.sql
index 17b6ba0..9c200a3 100644
--- a/parser/testdata/ddl/format/create_role.sql
+++ b/parser/testdata/ddl/format/create_role.sql
@@ -32,18 +32,18 @@ CREATE ROLE r1_01293 ON CLUSTER cluster_1, r2_01293;
 CREATE ROLE r1_01293 ON CLUSTER cluster_1, r2_01293 ON CLUSTER cluster_2;
 CREATE ROLE r1_01293 SETTINGS NONE;
 CREATE ROLE r2_01293 SETTINGS PROFILE 'default';
-CREATE ROLE r3_01293 SETTINGS max_memory_usage 5000000;
-CREATE ROLE r4_01293 SETTINGS max_memory_usage MIN 5000000;
-CREATE ROLE r5_01293 SETTINGS max_memory_usage MAX 5000000;
+CREATE ROLE r3_01293 SETTINGS max_memory_usage=5000000;
+CREATE ROLE r4_01293 SETTINGS max_memory_usage MIN=5000000;
+CREATE ROLE r5_01293 SETTINGS max_memory_usage MAX=5000000;
 CREATE ROLE r6_01293 SETTINGS max_memory_usage CONST;
 CREATE ROLE r7_01293 SETTINGS max_memory_usage WRITABLE;
-CREATE ROLE r8_01293 SETTINGS max_memory_usage 5000000 MIN 4000000 MAX 6000000 CONST;
-CREATE ROLE r9_01293 SETTINGS PROFILE 'default', max_memory_usage 5000000 WRITABLE;
+CREATE ROLE r8_01293 SETTINGS max_memory_usage=5000000 MIN 4000000 MAX 6000000 CONST;
+CREATE ROLE r9_01293 SETTINGS PROFILE 'default', max_memory_usage=5000000 WRITABLE;
 CREATE ROLE r1_01293, r2_01293;
-CREATE ROLE r1_01293 SETTINGS readonly 1;
+CREATE ROLE r1_01293 SETTINGS readonly=1;
 CREATE ROLE r2_01293 SETTINGS PROFILE 'default';
-CREATE ROLE r3_01293 SETTINGS max_memory_usage 5000000 MIN 4000000 MAX 6000000 WRITABLE;
-CREATE ROLE r4_01293 SETTINGS PROFILE 'default', max_memory_usage 5000000, readonly 1;
+CREATE ROLE r3_01293 SETTINGS max_memory_usage=5000000 MIN 4000000 MAX 6000000 WRITABLE;
+CREATE ROLE r4_01293 SETTINGS PROFILE 'default', max_memory_usage=5000000, readonly=1;
 CREATE ROLE r5_01293 SETTINGS NONE;
 CREATE ROLE r1_01293@'%';
 CREATE ROLE r2_01293@'%.myhost.com';
diff --git a/parser/testdata/ddl/format/create_table_with_codec_delta.sql b/parser/testdata/ddl/format/create_table_with_codec_delta.sql
index 082189b..e0b4731 100644
--- a/parser/testdata/ddl/format/create_table_with_codec_delta.sql
+++ b/parser/testdata/ddl/format/create_table_with_codec_delta.sql
@@ -19,7 +19,7 @@ CREATE TABLE IF NOT EXISTS test_local
 (
   `id` UInt64 CODEC(Delta, ZSTD(1)),
   `api_id` UInt64 CODEC(ZSTD(1)),
-  `app_id` UInt64 CODEC(Delta, ZSTD(1)),
+  `app_id` UInt64 CODEC(Delta(9), ZSTD(1)),
   `timestamp` DateTime64(9) CODEC(ZSTD(1)),
   INDEX timestamp_index(timestamp) TYPE minmax GRANULARITY 4
 )
diff --git a/parser/testdata/ddl/output/alter_role.sql.golden.json b/parser/testdata/ddl/output/alter_role.sql.golden.json
index ebec9aa..2c945ca 100644
--- a/parser/testdata/ddl/output/alter_role.sql.golden.json
+++ b/parser/testdata/ddl/output/alter_role.sql.golden.json
@@ -164,6 +164,7 @@
               "NamePos": 237,
               "NameEnd": 244
             },
+            "Operation": "",
             "Value": {
               "LiteralPos": 246,
               "LiteralEnd": 253,
@@ -205,6 +206,7 @@
               "NamePos": 285,
               "NameEnd": 301
             },
+            "Operation": "=",
             "Value": {
               "NumPos": 302,
               "NumEnd": 309,
@@ -247,6 +249,7 @@
               "NamePos": 340,
               "NameEnd": 356
             },
+            "Operation": "",
             "Value": null
           },
           {
@@ -256,6 +259,7 @@
               "NamePos": 357,
               "NameEnd": 360
             },
+            "Operation": "=",
             "Value": {
               "NumPos": 361,
               "NumEnd": 368,
@@ -298,6 +302,7 @@
               "NamePos": 399,
               "NameEnd": 415
             },
+            "Operation": "",
             "Value": null
           },
           {
@@ -307,6 +312,7 @@
               "NamePos": 416,
               "NameEnd": 419
             },
+            "Operation": "=",
             "Value": {
               "NumPos": 420,
               "NumEnd": 427,
@@ -349,6 +355,7 @@
               "NamePos": 458,
               "NameEnd": 474
             },
+            "Operation": "",
             "Value": null
           }
         ],
@@ -391,6 +398,7 @@
               "NamePos": 511,
               "NameEnd": 527
             },
+            "Operation": "",
             "Value": null
           }
         ],
@@ -433,6 +441,7 @@
               "NamePos": 567,
               "NameEnd": 583
             },
+            "Operation": "=",
             "Value": {
               "NumPos": 584,
               "NumEnd": 591,
@@ -447,6 +456,7 @@
               "NamePos": 592,
               "NameEnd": 595
             },
+            "Operation": "",
             "Value": {
               "NumPos": 596,
               "NumEnd": 603,
@@ -461,6 +471,7 @@
               "NamePos": 604,
               "NameEnd": 607
             },
+            "Operation": "",
             "Value": {
               "NumPos": 608,
               "NumEnd": 615,
@@ -508,6 +519,7 @@
               "NamePos": 652,
               "NameEnd": 659
             },
+            "Operation": "",
             "Value": {
               "LiteralPos": 661,
               "LiteralEnd": 668,
@@ -526,6 +538,7 @@
               "NamePos": 671,
               "NameEnd": 687
             },
+            "Operation": "=",
             "Value": {
               "NumPos": 688,
               "NumEnd": 695,
@@ -609,6 +622,7 @@
               "NamePos": 766,
               "NameEnd": 774
             },
+            "Operation": "=",
             "Value": {
               "NumPos": 775,
               "NumEnd": 776,
@@ -651,6 +665,7 @@
               "NamePos": 807,
               "NameEnd": 814
             },
+            "Operation": "",
             "Value": {
               "LiteralPos": 816,
               "LiteralEnd": 823,
@@ -692,6 +707,7 @@
               "NamePos": 855,
               "NameEnd": 871
             },
+            "Operation": "=",
             "Value": {
               "NumPos": 872,
               "NumEnd": 879,
@@ -706,6 +722,7 @@
               "NamePos": 880,
               "NameEnd": 883
             },
+            "Operation": "",
             "Value": {
               "NumPos": 884,
               "NumEnd": 891,
@@ -720,6 +737,7 @@
               "NamePos": 892,
               "NameEnd": 895
             },
+            "Operation": "",
             "Value": {
               "NumPos": 896,
               "NumEnd": 903,
@@ -767,6 +785,7 @@
               "NamePos": 943,
               "NameEnd": 950
             },
+            "Operation": "",
             "Value": {
               "LiteralPos": 952,
               "LiteralEnd": 959,
@@ -785,6 +804,7 @@
               "NamePos": 962,
               "NameEnd": 978
             },
+            "Operation": "=",
             "Value": {
               "NumPos": 979,
               "NumEnd": 986,
@@ -804,6 +824,7 @@
               "NamePos": 988,
               "NameEnd": 996
             },
+            "Operation": "=",
             "Value": {
               "NumPos": 997,
               "NumEnd": 998,
diff --git a/parser/testdata/ddl/output/alter_table_drop_detach_partition.sql.golden.json b/parser/testdata/ddl/output/alter_table_drop_detach_partition.sql.golden.json
index 0028775..037df24 100644
--- a/parser/testdata/ddl/output/alter_table_drop_detach_partition.sql.golden.json
+++ b/parser/testdata/ddl/output/alter_table_drop_detach_partition.sql.golden.json
@@ -19,7 +19,8 @@
     "OnCluster": null,
     "AlterExprs": [
       {
-        "DetachPos": 85,
+        "DropPos": 85,
+        "HasDetached": true,
         "Partition": {
           "PartitionPos": 99,
           "Expr": {
diff --git a/parser/testdata/ddl/output/alter_table_drop_partition.sql.golden.json b/parser/testdata/ddl/output/alter_table_drop_partition.sql.golden.json
index f08d3c3..440d917 100644
--- a/parser/testdata/ddl/output/alter_table_drop_partition.sql.golden.json
+++ b/parser/testdata/ddl/output/alter_table_drop_partition.sql.golden.json
@@ -27,6 +27,7 @@
     "AlterExprs": [
       {
         "DropPos": 53,
+        "HasDetached": false,
         "Partition": {
           "PartitionPos": 58,
           "Expr": {
@@ -36,7 +37,8 @@
           },
           "ID": null,
           "All": false
-        }
+        },
+        "Settings": null
       }
     ]
   }
diff --git a/parser/testdata/ddl/output/bug_001.sql.golden.json b/parser/testdata/ddl/output/bug_001.sql.golden.json
index 3a07232..1bf1334 100644
--- a/parser/testdata/ddl/output/bug_001.sql.golden.json
+++ b/parser/testdata/ddl/output/bug_001.sql.golden.json
@@ -501,6 +501,7 @@
         "LimitBy": null,
         "Limit": null,
         "Settings": null,
+        "Format": null,
         "UnionAll": null,
         "UnionDistinct": null,
         "Except": null
diff --git a/parser/testdata/ddl/output/create_live_view_basic.sql.golden.json b/parser/testdata/ddl/output/create_live_view_basic.sql.golden.json
index 43f159f..7146443 100644
--- a/parser/testdata/ddl/output/create_live_view_basic.sql.golden.json
+++ b/parser/testdata/ddl/output/create_live_view_basic.sql.golden.json
@@ -127,6 +127,7 @@
         "LimitBy": null,
         "Limit": null,
         "Settings": null,
+        "Format": null,
         "UnionAll": null,
         "UnionDistinct": null,
         "Except": null
diff --git a/parser/testdata/ddl/output/create_materialized_view_basic.sql.golden.json b/parser/testdata/ddl/output/create_materialized_view_basic.sql.golden.json
index 3e50d25..f4aba51 100644
--- a/parser/testdata/ddl/output/create_materialized_view_basic.sql.golden.json
+++ b/parser/testdata/ddl/output/create_materialized_view_basic.sql.golden.json
@@ -486,6 +486,7 @@
         "LimitBy": null,
         "Limit": null,
         "Settings": null,
+        "Format": null,
         "UnionAll": null,
         "UnionDistinct": null,
         "Except": null
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 a379f2a..ead806a 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
@@ -427,6 +427,7 @@
                   "LimitBy": null,
                   "Limit": null,
                   "Settings": null,
+                  "Format": null,
                   "UnionAll": null,
                   "UnionDistinct": null,
                   "Except": null
@@ -476,6 +477,7 @@
         "LimitBy": null,
         "Limit": null,
         "Settings": null,
+        "Format": null,
         "UnionAll": null,
         "UnionDistinct": null,
         "Except": null
diff --git a/parser/testdata/ddl/output/create_role.sql.golden.json b/parser/testdata/ddl/output/create_role.sql.golden.json
index d1115f9..d0d6449 100644
--- a/parser/testdata/ddl/output/create_role.sql.golden.json
+++ b/parser/testdata/ddl/output/create_role.sql.golden.json
@@ -219,6 +219,7 @@
               "NamePos": 312,
               "NameEnd": 319
             },
+            "Operation": "",
             "Value": {
               "LiteralPos": 321,
               "LiteralEnd": 328,
@@ -258,6 +259,7 @@
               "NamePos": 361,
               "NameEnd": 377
             },
+            "Operation": "=",
             "Value": {
               "NumPos": 378,
               "NumEnd": 385,
@@ -298,6 +300,7 @@
               "NamePos": 417,
               "NameEnd": 433
             },
+            "Operation": "",
             "Value": null
           },
           {
@@ -307,6 +310,7 @@
               "NamePos": 434,
               "NameEnd": 437
             },
+            "Operation": "=",
             "Value": {
               "NumPos": 438,
               "NumEnd": 445,
@@ -347,6 +351,7 @@
               "NamePos": 477,
               "NameEnd": 493
             },
+            "Operation": "",
             "Value": null
           },
           {
@@ -356,6 +361,7 @@
               "NamePos": 494,
               "NameEnd": 497
             },
+            "Operation": "=",
             "Value": {
               "NumPos": 498,
               "NumEnd": 505,
@@ -396,6 +402,7 @@
               "NamePos": 537,
               "NameEnd": 553
             },
+            "Operation": "",
             "Value": null
           }
         ],
@@ -436,6 +443,7 @@
               "NamePos": 591,
               "NameEnd": 607
             },
+            "Operation": "",
             "Value": null
           }
         ],
@@ -476,6 +484,7 @@
               "NamePos": 648,
               "NameEnd": 664
             },
+            "Operation": "=",
             "Value": {
               "NumPos": 665,
               "NumEnd": 672,
@@ -490,6 +499,7 @@
               "NamePos": 673,
               "NameEnd": 676
             },
+            "Operation": "",
             "Value": {
               "NumPos": 677,
               "NumEnd": 684,
@@ -504,6 +514,7 @@
               "NamePos": 685,
               "NameEnd": 688
             },
+            "Operation": "",
             "Value": {
               "NumPos": 689,
               "NumEnd": 696,
@@ -549,6 +560,7 @@
               "NamePos": 734,
               "NameEnd": 741
             },
+            "Operation": "",
             "Value": {
               "LiteralPos": 743,
               "LiteralEnd": 750,
@@ -567,6 +579,7 @@
               "NamePos": 753,
               "NameEnd": 769
             },
+            "Operation": "=",
             "Value": {
               "NumPos": 770,
               "NumEnd": 777,
@@ -642,6 +655,7 @@
               "NamePos": 850,
               "NameEnd": 858
             },
+            "Operation": "=",
             "Value": {
               "NumPos": 859,
               "NumEnd": 860,
@@ -682,6 +696,7 @@
               "NamePos": 892,
               "NameEnd": 899
             },
+            "Operation": "",
             "Value": {
               "LiteralPos": 901,
               "LiteralEnd": 908,
@@ -721,6 +736,7 @@
               "NamePos": 941,
               "NameEnd": 957
             },
+            "Operation": "=",
             "Value": {
               "NumPos": 958,
               "NumEnd": 965,
@@ -735,6 +751,7 @@
               "NamePos": 966,
               "NameEnd": 969
             },
+            "Operation": "",
             "Value": {
               "NumPos": 970,
               "NumEnd": 977,
@@ -749,6 +766,7 @@
               "NamePos": 978,
               "NameEnd": 981
             },
+            "Operation": "",
             "Value": {
               "NumPos": 982,
               "NumEnd": 989,
@@ -794,6 +812,7 @@
               "NamePos": 1030,
               "NameEnd": 1037
             },
+            "Operation": "",
             "Value": {
               "LiteralPos": 1039,
               "LiteralEnd": 1046,
@@ -812,6 +831,7 @@
               "NamePos": 1049,
               "NameEnd": 1065
             },
+            "Operation": "=",
             "Value": {
               "NumPos": 1066,
               "NumEnd": 1073,
@@ -831,6 +851,7 @@
               "NamePos": 1075,
               "NameEnd": 1083
             },
+            "Operation": "=",
             "Value": {
               "NumPos": 1084,
               "NumEnd": 1085,
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 d928845..6bc90b8 100644
--- a/parser/testdata/ddl/output/create_table_basic.sql.golden.json
+++ b/parser/testdata/ddl/output/create_table_basic.sql.golden.json
@@ -78,6 +78,7 @@
             "CodecPos": 198,
             "RightParenPos": 212,
             "Type": null,
+            "TypeLevel": null,
             "Name": {
               "Name": "ZSTD",
               "QuoteType": 1,
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 be21b0e..e65f4aa 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
@@ -50,6 +50,7 @@
               "NamePos": 59,
               "NameEnd": 64
             },
+            "TypeLevel": null,
             "Name": {
               "Name": "ZSTD",
               "QuoteType": 1,
@@ -94,6 +95,7 @@
             "CodecPos": 93,
             "RightParenPos": 107,
             "Type": null,
+            "TypeLevel": null,
             "Name": {
               "Name": "ZSTD",
               "QuoteType": 1,
@@ -143,6 +145,12 @@
               "NamePos": 132,
               "NameEnd": 137
             },
+            "TypeLevel": {
+              "NumPos": 137,
+              "NumEnd": 139,
+              "Literal": "9",
+              "Base": 10
+            },
             "Name": {
               "Name": "ZSTD",
               "QuoteType": 1,
@@ -197,6 +205,7 @@
             "CodecPos": 179,
             "RightParenPos": 193,
             "Type": null,
+            "TypeLevel": null,
             "Name": {
               "Name": "ZSTD",
               "QuoteType": 1,
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 67e0632..da08722 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
@@ -50,6 +50,7 @@
               "NamePos": 59,
               "NameEnd": 64
             },
+            "TypeLevel": null,
             "Name": {
               "Name": "ZSTD",
               "QuoteType": 1,
@@ -94,6 +95,7 @@
             "CodecPos": 93,
             "RightParenPos": 107,
             "Type": null,
+            "TypeLevel": null,
             "Name": {
               "Name": "ZSTD",
               "QuoteType": 1,
@@ -178,6 +180,7 @@
             "CodecPos": 148,
             "RightParenPos": 162,
             "Type": null,
+            "TypeLevel": null,
             "Name": {
               "Name": "ZSTD",
               "QuoteType": 1,
diff --git a/parser/testdata/ddl/output/create_view_basic.sql.golden.json b/parser/testdata/ddl/output/create_view_basic.sql.golden.json
index 69a6817..403222e 100644
--- a/parser/testdata/ddl/output/create_view_basic.sql.golden.json
+++ b/parser/testdata/ddl/output/create_view_basic.sql.golden.json
@@ -138,6 +138,7 @@
         "LimitBy": null,
         "Limit": null,
         "Settings": null,
+        "Format": null,
         "UnionAll": null,
         "UnionDistinct": null,
         "Except": null
diff --git a/parser/testdata/ddl/output/create_view_on_cluster_with_uuid.sql.golden.json b/parser/testdata/ddl/output/create_view_on_cluster_with_uuid.sql.golden.json
index 9be946b..f5cf10a 100644
--- a/parser/testdata/ddl/output/create_view_on_cluster_with_uuid.sql.golden.json
+++ b/parser/testdata/ddl/output/create_view_on_cluster_with_uuid.sql.golden.json
@@ -93,6 +93,7 @@
         "LimitBy": null,
         "Limit": null,
         "Settings": null,
+        "Format": null,
         "UnionAll": null,
         "UnionDistinct": null,
         "Except": null
diff --git a/parser/testdata/dml/output/insert_with_select.sql.golden.json b/parser/testdata/dml/output/insert_with_select.sql.golden.json
index a2d3afa..28502bb 100644
--- a/parser/testdata/dml/output/insert_with_select.sql.golden.json
+++ b/parser/testdata/dml/output/insert_with_select.sql.golden.json
@@ -93,6 +93,7 @@
       "LimitBy": null,
       "Limit": null,
       "Settings": null,
+      "Format": null,
       "UnionAll": null,
       "UnionDistinct": null,
       "Except": null
diff --git a/parser/testdata/query/format/select_with_union_distinct.sql b/parser/testdata/query/format/select_with_union_distinct.sql
index 77c9a59..a9becdd 100644
--- a/parser/testdata/query/format/select_with_union_distinct.sql
+++ b/parser/testdata/query/format/select_with_union_distinct.sql
@@ -11,4 +11,5 @@ FROM
 SELECT 
   replica_name
 FROM
-  system.ha_unique_replicas;
+  system.ha_unique_replicas
+FORMAT JSON;
diff --git a/parser/testdata/query/output/select_cast.sql.golden.json b/parser/testdata/query/output/select_cast.sql.golden.json
index 33bc315..bab844b 100644
--- a/parser/testdata/query/output/select_cast.sql.golden.json
+++ b/parser/testdata/query/output/select_cast.sql.golden.json
@@ -51,6 +51,7 @@
     "LimitBy": null,
     "Limit": null,
     "Settings": null,
+    "Format": null,
     "UnionAll": null,
     "UnionDistinct": null,
     "Except": null
@@ -104,6 +105,7 @@
     "LimitBy": null,
     "Limit": null,
     "Settings": null,
+    "Format": null,
     "UnionAll": null,
     "UnionDistinct": null,
     "Except": null
@@ -168,6 +170,7 @@
     "LimitBy": null,
     "Limit": null,
     "Settings": null,
+    "Format": null,
     "UnionAll": null,
     "UnionDistinct": null,
     "Except": null
@@ -222,6 +225,7 @@
     "LimitBy": null,
     "Limit": null,
     "Settings": null,
+    "Format": null,
     "UnionAll": null,
     "UnionDistinct": null,
     "Except": null
diff --git a/parser/testdata/query/output/select_column_alias_string.sql.golden.json b/parser/testdata/query/output/select_column_alias_string.sql.golden.json
index 6bb5559..25e1997 100644
--- a/parser/testdata/query/output/select_column_alias_string.sql.golden.json
+++ b/parser/testdata/query/output/select_column_alias_string.sql.golden.json
@@ -37,6 +37,7 @@
     "LimitBy": null,
     "Limit": null,
     "Settings": null,
+    "Format": null,
     "UnionAll": null,
     "UnionDistinct": null,
     "Except": null
diff --git a/parser/testdata/query/output/select_simple.sql.golden.json b/parser/testdata/query/output/select_simple.sql.golden.json
index 25f492a..f6c6d37 100644
--- a/parser/testdata/query/output/select_simple.sql.golden.json
+++ b/parser/testdata/query/output/select_simple.sql.golden.json
@@ -380,6 +380,7 @@
     },
     "Limit": null,
     "Settings": null,
+    "Format": null,
     "UnionAll": null,
     "UnionDistinct": null,
     "Except": null
diff --git a/parser/testdata/query/output/select_simple_with_bracket.sql.golden.json b/parser/testdata/query/output/select_simple_with_bracket.sql.golden.json
index 6eb1271..8c12b68 100644
--- a/parser/testdata/query/output/select_simple_with_bracket.sql.golden.json
+++ b/parser/testdata/query/output/select_simple_with_bracket.sql.golden.json
@@ -155,6 +155,7 @@
     "LimitBy": null,
     "Limit": null,
     "Settings": null,
+    "Format": null,
     "UnionAll": null,
     "UnionDistinct": null,
     "Except": null
diff --git a/parser/testdata/query/output/select_simple_with_cte_with_column_aliases.sql.golden.json b/parser/testdata/query/output/select_simple_with_cte_with_column_aliases.sql.golden.json
index c818d37..d46ee71 100644
--- a/parser/testdata/query/output/select_simple_with_cte_with_column_aliases.sql.golden.json
+++ b/parser/testdata/query/output/select_simple_with_cte_with_column_aliases.sql.golden.json
@@ -110,6 +110,7 @@
             "LimitBy": null,
             "Limit": null,
             "Settings": null,
+            "Format": null,
             "UnionAll": null,
             "UnionDistinct": null,
             "Except": null
@@ -204,6 +205,7 @@
     "LimitBy": null,
     "Limit": null,
     "Settings": null,
+    "Format": null,
     "UnionAll": null,
     "UnionDistinct": null,
     "Except": null
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 863deac..5028eed 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
@@ -116,6 +116,7 @@
     "LimitBy": null,
     "Limit": null,
     "Settings": null,
+    "Format": null,
     "UnionAll": null,
     "UnionDistinct": null,
     "Except": null
diff --git a/parser/testdata/query/output/select_simple_with_is_not_null.sql.golden.json b/parser/testdata/query/output/select_simple_with_is_not_null.sql.golden.json
index 58968b2..4867063 100644
--- a/parser/testdata/query/output/select_simple_with_is_not_null.sql.golden.json
+++ b/parser/testdata/query/output/select_simple_with_is_not_null.sql.golden.json
@@ -196,6 +196,7 @@
     "LimitBy": null,
     "Limit": null,
     "Settings": null,
+    "Format": null,
     "UnionAll": null,
     "UnionDistinct": null,
     "Except": null
diff --git a/parser/testdata/query/output/select_simple_with_is_null.sql.golden.json b/parser/testdata/query/output/select_simple_with_is_null.sql.golden.json
index b41a093..f6c0114 100644
--- a/parser/testdata/query/output/select_simple_with_is_null.sql.golden.json
+++ b/parser/testdata/query/output/select_simple_with_is_null.sql.golden.json
@@ -182,6 +182,7 @@
     "LimitBy": null,
     "Limit": null,
     "Settings": null,
+    "Format": null,
     "UnionAll": null,
     "UnionDistinct": null,
     "Except": null
diff --git a/parser/testdata/query/output/select_simple_with_top_clause.sql.golden.json b/parser/testdata/query/output/select_simple_with_top_clause.sql.golden.json
index 6e75392..c584082 100644
--- a/parser/testdata/query/output/select_simple_with_top_clause.sql.golden.json
+++ b/parser/testdata/query/output/select_simple_with_top_clause.sql.golden.json
@@ -61,6 +61,7 @@
     "LimitBy": null,
     "Limit": null,
     "Settings": null,
+    "Format": null,
     "UnionAll": null,
     "UnionDistinct": null,
     "Except": null
diff --git a/parser/testdata/query/output/select_simple_with_with_clause.sql.golden.json b/parser/testdata/query/output/select_simple_with_with_clause.sql.golden.json
index 5d55b8b..a0dddd9 100644
--- a/parser/testdata/query/output/select_simple_with_with_clause.sql.golden.json
+++ b/parser/testdata/query/output/select_simple_with_with_clause.sql.golden.json
@@ -66,6 +66,7 @@
             "LimitBy": null,
             "Limit": null,
             "Settings": null,
+            "Format": null,
             "UnionAll": null,
             "UnionDistinct": null,
             "Except": null
@@ -131,6 +132,7 @@
             "LimitBy": null,
             "Limit": null,
             "Settings": null,
+            "Format": null,
             "UnionAll": null,
             "UnionDistinct": null,
             "Except": null
@@ -275,6 +277,7 @@
     "LimitBy": null,
     "Limit": null,
     "Settings": null,
+    "Format": null,
     "UnionAll": null,
     "UnionDistinct": null,
     "Except": null
diff --git a/parser/testdata/query/output/select_table_alias_without_keyword.sql.golden.json b/parser/testdata/query/output/select_table_alias_without_keyword.sql.golden.json
index 6207280..1487980 100644
--- a/parser/testdata/query/output/select_table_alias_without_keyword.sql.golden.json
+++ b/parser/testdata/query/output/select_table_alias_without_keyword.sql.golden.json
@@ -156,6 +156,7 @@
     "LimitBy": null,
     "Limit": null,
     "Settings": null,
+    "Format": null,
     "UnionAll": null,
     "UnionDistinct": null,
     "Except": null
diff --git a/parser/testdata/query/output/select_with_join_only.sql.golden.json b/parser/testdata/query/output/select_with_join_only.sql.golden.json
index 6bcefd1..2e61223 100644
--- a/parser/testdata/query/output/select_with_join_only.sql.golden.json
+++ b/parser/testdata/query/output/select_with_join_only.sql.golden.json
@@ -99,6 +99,7 @@
     "LimitBy": null,
     "Limit": null,
     "Settings": null,
+    "Format": null,
     "UnionAll": null,
     "UnionDistinct": null,
     "Except": null
diff --git a/parser/testdata/query/output/select_with_left_join.sql.golden.json b/parser/testdata/query/output/select_with_left_join.sql.golden.json
index e4eb41a..2ea4c66 100644
--- a/parser/testdata/query/output/select_with_left_join.sql.golden.json
+++ b/parser/testdata/query/output/select_with_left_join.sql.golden.json
@@ -53,6 +53,7 @@
             "LimitBy": null,
             "Limit": null,
             "Settings": null,
+            "Format": null,
             "UnionAll": null,
             "UnionDistinct": null,
             "Except": null
@@ -105,6 +106,7 @@
             "LimitBy": null,
             "Limit": null,
             "Settings": null,
+            "Format": null,
             "UnionAll": null,
             "UnionDistinct": null,
             "Except": null
@@ -209,6 +211,7 @@
     "LimitBy": null,
     "Limit": null,
     "Settings": null,
+    "Format": null,
     "UnionAll": null,
     "UnionDistinct": null,
     "Except": null
diff --git a/parser/testdata/query/output/select_with_literal_table_name.sql.golden.json b/parser/testdata/query/output/select_with_literal_table_name.sql.golden.json
index 6a0ad35..4f8a106 100644
--- a/parser/testdata/query/output/select_with_literal_table_name.sql.golden.json
+++ b/parser/testdata/query/output/select_with_literal_table_name.sql.golden.json
@@ -65,6 +65,7 @@
       "Offset": null
     },
     "Settings": null,
+    "Format": null,
     "UnionAll": null,
     "UnionDistinct": null,
     "Except": null
diff --git a/parser/testdata/query/output/select_with_multi_join.sql.golden.json b/parser/testdata/query/output/select_with_multi_join.sql.golden.json
index b4eed35..5113869 100644
--- a/parser/testdata/query/output/select_with_multi_join.sql.golden.json
+++ b/parser/testdata/query/output/select_with_multi_join.sql.golden.json
@@ -52,6 +52,7 @@
             "LimitBy": null,
             "Limit": null,
             "Settings": null,
+            "Format": null,
             "UnionAll": null,
             "UnionDistinct": null,
             "Except": null
@@ -103,6 +104,7 @@
             "LimitBy": null,
             "Limit": null,
             "Settings": null,
+            "Format": null,
             "UnionAll": null,
             "UnionDistinct": null,
             "Except": null
@@ -154,6 +156,7 @@
             "LimitBy": null,
             "Limit": null,
             "Settings": null,
+            "Format": null,
             "UnionAll": null,
             "UnionDistinct": null,
             "Except": null
@@ -419,6 +422,7 @@
     "LimitBy": null,
     "Limit": null,
     "Settings": null,
+    "Format": null,
     "UnionAll": null,
     "UnionDistinct": null,
     "Except": null
diff --git a/parser/testdata/query/output/select_with_multi_line_comment.sql.golden.json b/parser/testdata/query/output/select_with_multi_line_comment.sql.golden.json
index 393d38e..2a6b80e 100644
--- a/parser/testdata/query/output/select_with_multi_line_comment.sql.golden.json
+++ b/parser/testdata/query/output/select_with_multi_line_comment.sql.golden.json
@@ -51,6 +51,7 @@
     "LimitBy": null,
     "Limit": null,
     "Settings": null,
+    "Format": null,
     "UnionAll": null,
     "UnionDistinct": null,
     "Except": null
diff --git a/parser/testdata/query/output/select_with_string_expr.sql.golden.json b/parser/testdata/query/output/select_with_string_expr.sql.golden.json
index 90e27d4..1872786 100644
--- a/parser/testdata/query/output/select_with_string_expr.sql.golden.json
+++ b/parser/testdata/query/output/select_with_string_expr.sql.golden.json
@@ -53,6 +53,7 @@
             "LimitBy": null,
             "Limit": null,
             "Settings": null,
+            "Format": null,
             "UnionAll": null,
             "UnionDistinct": null,
             "Except": null
@@ -108,6 +109,7 @@
     "LimitBy": null,
     "Limit": null,
     "Settings": null,
+    "Format": null,
     "UnionAll": null,
     "UnionDistinct": null,
     "Except": null
diff --git a/parser/testdata/query/output/select_with_union_distinct.sql.golden.json b/parser/testdata/query/output/select_with_union_distinct.sql.golden.json
index e26ec64..a98c0b0 100644
--- a/parser/testdata/query/output/select_with_union_distinct.sql.golden.json
+++ b/parser/testdata/query/output/select_with_union_distinct.sql.golden.json
@@ -56,10 +56,11 @@
     "LimitBy": null,
     "Limit": null,
     "Settings": null,
+    "Format": null,
     "UnionAll": null,
     "UnionDistinct": {
       "SelectPos": 59,
-      "StatementEnd": 109,
+      "StatementEnd": 121,
       "With": null,
       "Top": null,
       "SelectColumns": {
@@ -114,6 +115,15 @@
       "LimitBy": null,
       "Limit": null,
       "Settings": null,
+      "Format": {
+        "FormatPos": 110,
+        "Format": {
+          "Name": "JSON",
+          "QuoteType": 1,
+          "NamePos": 117,
+          "NameEnd": 121
+        }
+      },
       "UnionAll": null,
       "UnionDistinct": null,
       "Except": null
diff --git a/parser/testdata/query/output/select_with_variable.sql.golden.json b/parser/testdata/query/output/select_with_variable.sql.golden.json
index ad5b84c..e27d903 100644
--- a/parser/testdata/query/output/select_with_variable.sql.golden.json
+++ b/parser/testdata/query/output/select_with_variable.sql.golden.json
@@ -53,6 +53,7 @@
             "LimitBy": null,
             "Limit": null,
             "Settings": null,
+            "Format": null,
             "UnionAll": null,
             "UnionDistinct": null,
             "Except": null
@@ -108,6 +109,7 @@
     "LimitBy": null,
     "Limit": null,
     "Settings": null,
+    "Format": null,
     "UnionAll": null,
     "UnionDistinct": null,
     "Except": null