diff --git a/parser/ast.go b/parser/ast.go index b780304..f95f271 100644 --- a/parser/ast.go +++ b/parser/ast.go @@ -4914,6 +4914,30 @@ func (s *StringLiteral) Accept(visitor ASTVisitor) error { return visitor.VisitStringLiteral(s) } +type BoolLiteral struct { + LiteralPos Pos + LiteralEnd Pos + Literal string +} + +func (b *BoolLiteral) Pos() Pos { + return b.LiteralPos +} + +func (b *BoolLiteral) End() Pos { + return b.LiteralEnd +} + +func (b *BoolLiteral) String() string { + return b.Literal +} + +func (b *BoolLiteral) Accept(visitor ASTVisitor) error { + visitor.Enter(b) + defer visitor.Leave(b) + return visitor.VisitBoolLiteral(b) +} + type PlaceHolder struct { PlaceholderPos Pos PlaceHolderEnd Pos diff --git a/parser/ast_visitor.go b/parser/ast_visitor.go index 2f94142..b33aa49 100644 --- a/parser/ast_visitor.go +++ b/parser/ast_visitor.go @@ -192,6 +192,7 @@ type ASTVisitor interface { VisitSelectItem(expr *SelectItem) error VisitTargetPairExpr(expr *TargetPair) error VisitDistinctOn(expr *DistinctOn) error + VisitBoolLiteral(expr *BoolLiteral) error Enter(expr Expr) Leave(expr Expr) @@ -1540,6 +1541,13 @@ func (v *DefaultASTVisitor) VisitDistinctOn(expr *DistinctOn) error { return nil } +func (v *DefaultASTVisitor) VisitBoolLiteral(expr *BoolLiteral) error { + if v.Visit != nil { + return v.Visit(expr) + } + return nil +} + func (v *DefaultASTVisitor) Enter(expr Expr) {} func (v *DefaultASTVisitor) Leave(expr Expr) {} diff --git a/parser/parse_system.go b/parser/parse_system.go index 4a5a17b..e7e1aaf 100644 --- a/parser/parse_system.go +++ b/parser/parse_system.go @@ -19,6 +19,20 @@ func (p *Parser) parseSetStmt(pos Pos) (*SetStmt, error) { }, nil } +func (p *Parser) parseSettingsStmt(pos Pos) (*SetStmt, error) { + if err := p.expectKeyword(KeywordSettings); err != nil { + return nil, err + } + settings, err := p.parseSettingsClause(p.Pos()) + if err != nil { + return nil, err + } + return &SetStmt{ + SetPos: pos, + Settings: settings, + }, nil +} + func (p *Parser) parseSystemFlushExpr(pos Pos) (*SystemFlushExpr, error) { if err := p.expectKeyword(KeywordFlush); err != nil { return nil, err diff --git a/parser/parser_table.go b/parser/parser_table.go index 4dadf85..2ce0a81 100644 --- a/parser/parser_table.go +++ b/parser/parser_table.go @@ -1096,8 +1096,17 @@ func (p *Parser) parseSettingsExpr(pos Pos) (*SettingExpr, error) { return nil, err } expr = m + case p.matchKeyword(KeywordTrue), p.matchKeyword(KeywordFalse): + // Handle TRUE/FALSE keywords as boolean literals + lastToken := p.last() + _ = p.lexer.consumeToken() + expr = &BoolLiteral{ + LiteralPos: lastToken.Pos, + LiteralEnd: lastToken.End, + Literal: lastToken.String, + } default: - return nil, fmt.Errorf("unexpected token: %q, expected or ", p.last().String) + return nil, fmt.Errorf("unexpected token: %q, expected , or ", p.last().String) } return &SettingExpr{ @@ -1232,6 +1241,8 @@ func (p *Parser) parseStmt(pos Pos) (Expr, error) { expr, err = p.parseUseStmt(pos) case p.matchKeyword(KeywordSet): expr, err = p.parseSetStmt(pos) + case p.matchKeyword(KeywordSettings): + expr, err = p.parseSettingsStmt(pos) case p.matchKeyword(KeywordSystem): expr, err = p.parseSystemStmt(pos) case p.matchKeyword(KeywordOptimize): diff --git a/parser/testdata/basic/format/set_statement.sql b/parser/testdata/basic/format/set_statement.sql new file mode 100644 index 0000000..bd63a4e --- /dev/null +++ b/parser/testdata/basic/format/set_statement.sql @@ -0,0 +1,18 @@ +-- Origin SQL: +SET allow_suspicious_low_cardinality_types = true; + +SET max_block_size = 65536; + +SET output_format_json_quote_64bit_integers = 'true'; + +SET max_threads = 8, max_memory_usage = 10000000000, enable_optimize_predicate_expression = false; + +SET allow_experimental_analyzer = true; + + +-- Format SQL: +SET allow_suspicious_low_cardinality_types=true; +SET max_block_size=65536; +SET output_format_json_quote_64bit_integers='true'; +SET max_threads=8, max_memory_usage=10000000000, enable_optimize_predicate_expression=false; +SET allow_experimental_analyzer=true; diff --git a/parser/testdata/basic/format/settings_statement.sql b/parser/testdata/basic/format/settings_statement.sql new file mode 100644 index 0000000..0485703 --- /dev/null +++ b/parser/testdata/basic/format/settings_statement.sql @@ -0,0 +1,18 @@ +-- Origin SQL: +SETTINGS allow_suspicious_low_cardinality_types = true; + +SETTINGS max_block_size = 65536; + +SETTINGS output_format_json_quote_64bit_integers = 'true'; + +SETTINGS max_threads = 8, max_memory_usage = 10000000000, enable_optimize_predicate_expression = false; + +SETTINGS allow_experimental_analyzer = true; + + +-- Format SQL: +SET allow_suspicious_low_cardinality_types=true; +SET max_block_size=65536; +SET output_format_json_quote_64bit_integers='true'; +SET max_threads=8, max_memory_usage=10000000000, enable_optimize_predicate_expression=false; +SET allow_experimental_analyzer=true; diff --git a/parser/testdata/basic/output/set_statement.sql.golden.json b/parser/testdata/basic/output/set_statement.sql.golden.json new file mode 100644 index 0000000..bb91531 --- /dev/null +++ b/parser/testdata/basic/output/set_statement.sql.golden.json @@ -0,0 +1,148 @@ +[ + { + "SetPos": 0, + "Settings": { + "SettingsPos": 4, + "ListEnd": 49, + "Items": [ + { + "SettingsPos": 4, + "Name": { + "Name": "allow_suspicious_low_cardinality_types", + "QuoteType": 1, + "NamePos": 4, + "NameEnd": 42 + }, + "Expr": { + "LiteralPos": 45, + "LiteralEnd": 49, + "Literal": "true" + } + } + ] + } + }, + { + "SetPos": 52, + "Settings": { + "SettingsPos": 56, + "ListEnd": 78, + "Items": [ + { + "SettingsPos": 56, + "Name": { + "Name": "max_block_size", + "QuoteType": 1, + "NamePos": 56, + "NameEnd": 70 + }, + "Expr": { + "NumPos": 73, + "NumEnd": 78, + "Literal": "65536", + "Base": 10 + } + } + ] + } + }, + { + "SetPos": 81, + "Settings": { + "SettingsPos": 85, + "ListEnd": 132, + "Items": [ + { + "SettingsPos": 85, + "Name": { + "Name": "output_format_json_quote_64bit_integers", + "QuoteType": 1, + "NamePos": 85, + "NameEnd": 124 + }, + "Expr": { + "LiteralPos": 128, + "LiteralEnd": 132, + "Literal": "true" + } + } + ] + } + }, + { + "SetPos": 136, + "Settings": { + "SettingsPos": 140, + "ListEnd": 233, + "Items": [ + { + "SettingsPos": 140, + "Name": { + "Name": "max_threads", + "QuoteType": 1, + "NamePos": 140, + "NameEnd": 151 + }, + "Expr": { + "NumPos": 154, + "NumEnd": 155, + "Literal": "8", + "Base": 10 + } + }, + { + "SettingsPos": 157, + "Name": { + "Name": "max_memory_usage", + "QuoteType": 1, + "NamePos": 157, + "NameEnd": 173 + }, + "Expr": { + "NumPos": 176, + "NumEnd": 187, + "Literal": "10000000000", + "Base": 10 + } + }, + { + "SettingsPos": 189, + "Name": { + "Name": "enable_optimize_predicate_expression", + "QuoteType": 1, + "NamePos": 189, + "NameEnd": 225 + }, + "Expr": { + "LiteralPos": 228, + "LiteralEnd": 233, + "Literal": "false" + } + } + ] + } + }, + { + "SetPos": 236, + "Settings": { + "SettingsPos": 240, + "ListEnd": 274, + "Items": [ + { + "SettingsPos": 240, + "Name": { + "Name": "allow_experimental_analyzer", + "QuoteType": 1, + "NamePos": 240, + "NameEnd": 267 + }, + "Expr": { + "LiteralPos": 270, + "LiteralEnd": 274, + "Literal": "true" + } + } + ] + } + } +] \ No newline at end of file diff --git a/parser/testdata/basic/output/settings_statement.sql.golden.json b/parser/testdata/basic/output/settings_statement.sql.golden.json new file mode 100644 index 0000000..8c074f0 --- /dev/null +++ b/parser/testdata/basic/output/settings_statement.sql.golden.json @@ -0,0 +1,148 @@ +[ + { + "SetPos": 0, + "Settings": { + "SettingsPos": 9, + "ListEnd": 54, + "Items": [ + { + "SettingsPos": 9, + "Name": { + "Name": "allow_suspicious_low_cardinality_types", + "QuoteType": 1, + "NamePos": 9, + "NameEnd": 47 + }, + "Expr": { + "LiteralPos": 50, + "LiteralEnd": 54, + "Literal": "true" + } + } + ] + } + }, + { + "SetPos": 57, + "Settings": { + "SettingsPos": 66, + "ListEnd": 88, + "Items": [ + { + "SettingsPos": 66, + "Name": { + "Name": "max_block_size", + "QuoteType": 1, + "NamePos": 66, + "NameEnd": 80 + }, + "Expr": { + "NumPos": 83, + "NumEnd": 88, + "Literal": "65536", + "Base": 10 + } + } + ] + } + }, + { + "SetPos": 91, + "Settings": { + "SettingsPos": 100, + "ListEnd": 147, + "Items": [ + { + "SettingsPos": 100, + "Name": { + "Name": "output_format_json_quote_64bit_integers", + "QuoteType": 1, + "NamePos": 100, + "NameEnd": 139 + }, + "Expr": { + "LiteralPos": 143, + "LiteralEnd": 147, + "Literal": "true" + } + } + ] + } + }, + { + "SetPos": 151, + "Settings": { + "SettingsPos": 160, + "ListEnd": 253, + "Items": [ + { + "SettingsPos": 160, + "Name": { + "Name": "max_threads", + "QuoteType": 1, + "NamePos": 160, + "NameEnd": 171 + }, + "Expr": { + "NumPos": 174, + "NumEnd": 175, + "Literal": "8", + "Base": 10 + } + }, + { + "SettingsPos": 177, + "Name": { + "Name": "max_memory_usage", + "QuoteType": 1, + "NamePos": 177, + "NameEnd": 193 + }, + "Expr": { + "NumPos": 196, + "NumEnd": 207, + "Literal": "10000000000", + "Base": 10 + } + }, + { + "SettingsPos": 209, + "Name": { + "Name": "enable_optimize_predicate_expression", + "QuoteType": 1, + "NamePos": 209, + "NameEnd": 245 + }, + "Expr": { + "LiteralPos": 248, + "LiteralEnd": 253, + "Literal": "false" + } + } + ] + } + }, + { + "SetPos": 256, + "Settings": { + "SettingsPos": 265, + "ListEnd": 299, + "Items": [ + { + "SettingsPos": 265, + "Name": { + "Name": "allow_experimental_analyzer", + "QuoteType": 1, + "NamePos": 265, + "NameEnd": 292 + }, + "Expr": { + "LiteralPos": 295, + "LiteralEnd": 299, + "Literal": "true" + } + } + ] + } + } +] \ No newline at end of file diff --git a/parser/testdata/basic/set_statement.sql b/parser/testdata/basic/set_statement.sql new file mode 100644 index 0000000..d83b05e --- /dev/null +++ b/parser/testdata/basic/set_statement.sql @@ -0,0 +1,9 @@ +SET allow_suspicious_low_cardinality_types = true; + +SET max_block_size = 65536; + +SET output_format_json_quote_64bit_integers = 'true'; + +SET max_threads = 8, max_memory_usage = 10000000000, enable_optimize_predicate_expression = false; + +SET allow_experimental_analyzer = true; diff --git a/parser/testdata/basic/settings_statement.sql b/parser/testdata/basic/settings_statement.sql new file mode 100644 index 0000000..4096307 --- /dev/null +++ b/parser/testdata/basic/settings_statement.sql @@ -0,0 +1,9 @@ +SETTINGS allow_suspicious_low_cardinality_types = true; + +SETTINGS max_block_size = 65536; + +SETTINGS output_format_json_quote_64bit_integers = 'true'; + +SETTINGS max_threads = 8, max_memory_usage = 10000000000, enable_optimize_predicate_expression = false; + +SETTINGS allow_experimental_analyzer = true; diff --git a/parser/testdata/ddl/create_table_with_ttl_policy.sql b/parser/testdata/ddl/create_table_with_ttl_policy.sql index 3082530..3f0a3a4 100644 --- a/parser/testdata/ddl/create_table_with_ttl_policy.sql +++ b/parser/testdata/ddl/create_table_with_ttl_policy.sql @@ -30,4 +30,4 @@ CREATE TABLE table_for_recompression 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; +SETTINGS min_rows_for_wide_part = 0, min_bytes_for_wide_part = 0, allow_experimental_replacing_merge_with_cleanup = true; diff --git a/parser/testdata/ddl/format/create_table_with_ttl_policy.sql b/parser/testdata/ddl/format/create_table_with_ttl_policy.sql index 8e2287d..c410f03 100644 --- a/parser/testdata/ddl/format/create_table_with_ttl_policy.sql +++ b/parser/testdata/ddl/format/create_table_with_ttl_policy.sql @@ -31,10 +31,10 @@ CREATE TABLE table_for_recompression 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; +SETTINGS min_rows_for_wide_part = 0, min_bytes_for_wide_part = 0, allow_experimental_replacing_merge_with_cleanup = true; -- Format SQL: CREATE TABLE tab (d DateTime, a Int) ENGINE = MergeTree ORDER BY d PARTITION BY toYYYYMM(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 ORDER BY d PARTITION BY toYYYYMM(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; +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, allow_experimental_replacing_merge_with_cleanup=true; 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 index afcb822..7dc8048 100644 --- 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 @@ -545,7 +545,7 @@ }, { "CreatePos": 399, - "StatementEnd": 707, + "StatementEnd": 763, "OrReplace": false, "Name": { "Database": null, @@ -659,7 +659,7 @@ }, "Engine": { "EnginePos": 489, - "EngineEnd": 707, + "EngineEnd": 763, "Name": "MergeTree", "Params": { "LeftParenPos": 505, @@ -823,7 +823,7 @@ }, "Settings": { "SettingsPos": 643, - "ListEnd": 707, + "ListEnd": 763, "Items": [ { "SettingsPos": 652, @@ -854,6 +854,20 @@ "Literal": "0", "Base": 10 } + }, + { + "SettingsPos": 709, + "Name": { + "Name": "allow_experimental_replacing_merge_with_cleanup", + "QuoteType": 1, + "NamePos": 709, + "NameEnd": 756 + }, + "Expr": { + "LiteralPos": 759, + "LiteralEnd": 763, + "Literal": "true" + } } ] },