Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
660438e
Fix UNION grouping for parenthesized queries with DISTINCT->ALL trans…
kyleconroy Jan 15, 2026
cbc4d68
Handle UUID clause in CREATE MATERIALIZED VIEW
kyleconroy Jan 15, 2026
734ba84
Handle SETTINGS/COMMENT order in CREATE TABLE explain output
kyleconroy Jan 15, 2026
6216399
Support parameterized functions in APPLY column transformers
kyleconroy Jan 15, 2026
99534b0
Support ORDER BY in CREATE DATABASE and multiple SETTINGS clauses
kyleconroy Jan 15, 2026
28717df
Support RENAME DATABASE statement
kyleconroy Jan 15, 2026
0855448
Parse dictionary SETTINGS clause and output as Dictionary settings
kyleconroy Jan 15, 2026
17fe6ac
Handle trailing comma in IN expressions as single-element tuple
kyleconroy Jan 15, 2026
2fa01e3
Fix negative number with cast in BETWEEN expressions
kyleconroy Jan 15, 2026
f304b1d
Support underscores in binary and octal literals
kyleconroy Jan 15, 2026
6ad7b56
Handle TernaryExpr with alias in WITH clause
kyleconroy Jan 15, 2026
6046d60
Handle SHOW CHANGED SETTINGS variant in parser (#117)
kyleconroy Jan 15, 2026
4576008
Remove redundant explain_todo for clientError SYNTAX_ERROR stmt (#118)
kyleconroy Jan 15, 2026
5e72b8a
Add short interval unit notations h, m, s, d, w (#119)
kyleconroy Jan 15, 2026
eb9f9a9
Fix ternary operator precedence to be lower than AND (#120)
kyleconroy Jan 15, 2026
96db89a
Strip trailing OK from ClickHouse EXPLAIN output in tests (#121)
kyleconroy Jan 15, 2026
7bf233f
Eliminate unary plus from AST (no-op in ClickHouse) (#122)
kyleconroy Jan 15, 2026
0d9f7c9
Add ALTER TABLE MODIFY QUERY support (#123)
kyleconroy Jan 15, 2026
917864d
Escape single quotes in function aliases for EXPLAIN output
kyleconroy Jan 15, 2026
599dee5
Skip EXPLAIN tests for statements with --{clientError annotations
kyleconroy Jan 15, 2026
9261423
Handle escape sequences in backtick identifiers and sanitize invalid …
kyleconroy Jan 15, 2026
619c89d
Always output CASE expression alias in EXPLAIN output
kyleconroy Jan 15, 2026
03c44b0
Fix column declaration child order in EXPLAIN output
kyleconroy Jan 15, 2026
75354e3
Handle PARTITION ID syntax in APPLY DELETED MASK command
kyleconroy Jan 15, 2026
67ba7b0
Support FORCE keyword in OPTIMIZE TABLE statement
kyleconroy Jan 15, 2026
317d037
Handle parenthesized literals in nested arrays for EXPLAIN output
kyleconroy Jan 15, 2026
18a6990
Handle USE DATABASE syntax and improve clientError detection
kyleconroy Jan 15, 2026
1e10e2e
Output WINDOW clause before QUALIFY in SelectQuery EXPLAIN
kyleconroy Jan 15, 2026
5b6ea17
Format function calls in AggregateFunction type parameters
kyleconroy Jan 15, 2026
c370f54
Handle QueryParameter with alias in EXPLAIN output
kyleconroy Jan 15, 2026
35d1e5f
Handle FROM (SELECT...) as clause keyword after trailing comma
kyleconroy Jan 15, 2026
613e3e5
Output GROUP BY before ORDER BY in ProjectionSelectQuery
kyleconroy Jan 15, 2026
5121d5c
Fix EPHEMERAL column parsing to not consume COMMENT keyword
kyleconroy Jan 15, 2026
b218c3d
Support CREATE INDEX expression without parentheses
kyleconroy Jan 15, 2026
cbde83c
Fix SYSTEM FLUSH DISTRIBUTED table name parsing
kyleconroy Jan 15, 2026
8738b5d
Add support for ALTER TABLE DROP DETACHED PARTITION
kyleconroy Jan 15, 2026
a9e48cb
Handle IF NOT EXISTS in CREATE WORKLOAD parsing
kyleconroy Jan 15, 2026
20d3016
Allow keywords as column names in ALTER TABLE DROP COLUMN
kyleconroy Jan 15, 2026
50d0966
Handle LIKE expression alias in WITH clause
kyleconroy Jan 15, 2026
b7d0a23
Add backtick quoting for special characters in type parameters
kyleconroy Jan 15, 2026
a26db45
Fix DESCRIBE parsing to handle SETTINGS after FORMAT
kyleconroy Jan 15, 2026
e471e45
Handle PARTITION ID syntax in UPDATE mutation commands
kyleconroy Jan 15, 2026
5e4634c
Add FROM clause parsing for ATTACH TABLE
kyleconroy Jan 15, 2026
9564725
Handle SYNC keyword token in KILL QUERY parsing
kyleconroy Jan 15, 2026
c34e702
Add ON CLUSTER clause parsing to DELETE statement
kyleconroy Jan 15, 2026
3da9497
Fix CRLF line ending comparison in explain tests
kyleconroy Jan 15, 2026
9a96fab
Allow SYNC keyword as implicit alias in expressions
kyleconroy Jan 15, 2026
bcc22f3
Handle ASSUME keyword in ALTER TABLE ADD CONSTRAINT
kyleconroy Jan 15, 2026
75af8d1
Fix SETTINGS clause parsing after MODIFY COLUMN REMOVE
kyleconroy Jan 15, 2026
d8ab7eb
Fix EXPLAIN children count when both options and SETTINGS present
kyleconroy Jan 15, 2026
3d7ed55
Skip FINAL keyword in DESCRIBE to parse SETTINGS clause
kyleconroy Jan 15, 2026
e72bb31
Allow keywords as column names in ALTER UPDATE assignments
kyleconroy Jan 15, 2026
befdafd
Handle alias on IS NULL expressions in explain output
kyleconroy Jan 15, 2026
050d0b2
Allow NOT NULL constraint after DEFAULT expression
kyleconroy Jan 15, 2026
04d68a5
Preserve function name case from SQL source in EXPLAIN AST output
kyleconroy Jan 15, 2026
5ec3094
Handle DISTINCT modifier in parametric function calls
kyleconroy Jan 15, 2026
242667a
Handle implicit aliases in projection SELECT column parsing
kyleconroy Jan 15, 2026
aed95c0
Map CLEAR_PROJECTION to DROP_PROJECTION in EXPLAIN AST output
kyleconroy Jan 15, 2026
5bb6e5e
Recursively check nested arrays for non-literal expressions in EXPLAI…
kyleconroy Jan 15, 2026
d7b6e81
Parse ON CLUSTER before column definitions in CREATE MATERIALIZED VIEW
kyleconroy Jan 15, 2026
353708c
Parse and output COMMENT clause for CREATE DICTIONARY
kyleconroy Jan 15, 2026
cc208ea
Parse MOVE PARTITION TO DISK/VOLUME syntax in ALTER statements
kyleconroy Jan 15, 2026
4b241a1
Add SETTINGS clause support for SYSTEM queries
kyleconroy Jan 15, 2026
d928cce
Add duplicate table output for LOAD/UNLOAD PRIMARY KEY commands
kyleconroy Jan 15, 2026
98e6e8e
Add TRUNCATE DATABASE support
kyleconroy Jan 15, 2026
90ff84d
Add REMOVE TTL support for ALTER TABLE
kyleconroy Jan 15, 2026
d7968a1
Add KILL QUERY SETTINGS support and fix operator mapping
kyleconroy Jan 15, 2026
37d572e
Remove incorrect concat_ws to concat normalization
kyleconroy Jan 15, 2026
a874217
Add IF EXISTS support for RENAME COLUMN in ALTER TABLE
kyleconroy Jan 15, 2026
3956e3f
Preserve tuple SpacedCommas flag in EXPLAIN AST output
kyleconroy Jan 15, 2026
26a512c
Handle all-NULL tuple literals in IN expression EXPLAIN output
kyleconroy Jan 15, 2026
8941020
Continue parsing binary operators after parenthesized ORDER BY expres…
kyleconroy Jan 15, 2026
8790477
Handle INSERT VALUES followed by SELECT on same line (#118)
kyleconroy Jan 15, 2026
8bd61bf
Fix TTL SET clause lookahead to use peekPeek instead of consuming tok…
kyleconroy Jan 15, 2026
5a31cd5
Fix INTERVAL parsing to stop before AND operators (#120)
kyleconroy Jan 15, 2026
aa1b527
Add support for REFRESH clause in CREATE MATERIALIZED VIEW (#121)
kyleconroy Jan 15, 2026
5deac6f
Fix Settings['key'] map access being confused with SETTINGS clause (#…
kyleconroy Jan 15, 2026
caaaad4
Handle double-paren grouping sets as Function tuple (#123)
kyleconroy Jan 15, 2026
5392e89
Fix INTERVAL parsing to handle both embedded and separate units
kyleconroy Jan 15, 2026
a273941
Add support for CREATE WINDOW VIEW parsing
kyleconroy Jan 15, 2026
7a62b45
Support binary expression WITH clauses like (SELECT ...) + (SELECT ..…
kyleconroy Jan 15, 2026
e99acd5
Fix CAST parsing to handle expression type arguments like 'Str'||'ing'
kyleconroy Jan 15, 2026
294c76b
Fix REPLACE transformer consuming comma from SELECT clause
kyleconroy Jan 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"permissions": {
"allow": [
"Bash(go run:*)",
"Bash(go test:*)"
]
}
}
90 changes: 57 additions & 33 deletions ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,9 +275,17 @@ type CreateQuery struct {
Table string `json:"table,omitempty"`
View string `json:"view,omitempty"`
Materialized bool `json:"materialized,omitempty"`
WindowView bool `json:"window_view,omitempty"` // WINDOW VIEW type
InnerEngine *EngineClause `json:"inner_engine,omitempty"` // INNER ENGINE for window views
ToDatabase string `json:"to_database,omitempty"` // Target database for materialized views
To string `json:"to,omitempty"` // Target table for materialized views
Populate bool `json:"populate,omitempty"` // POPULATE for materialized views
HasRefresh bool `json:"has_refresh,omitempty"` // Has REFRESH clause
RefreshType string `json:"refresh_type,omitempty"` // AFTER or EVERY
RefreshInterval Expression `json:"refresh_interval,omitempty"` // Interval value
RefreshUnit string `json:"refresh_unit,omitempty"` // SECOND, MINUTE, etc.
RefreshAppend bool `json:"refresh_append,omitempty"` // APPEND TO was specified
Empty bool `json:"empty,omitempty"` // EMPTY keyword was specified
Columns []*ColumnDeclaration `json:"columns,omitempty"`
Indexes []*IndexDefinition `json:"indexes,omitempty"`
Projections []*Projection `json:"projections,omitempty"`
Expand All @@ -291,7 +299,9 @@ type CreateQuery struct {
PrimaryKey []Expression `json:"primary_key,omitempty"`
SampleBy Expression `json:"sample_by,omitempty"`
TTL *TTLClause `json:"ttl,omitempty"`
Settings []*SettingExpr `json:"settings,omitempty"`
Settings []*SettingExpr `json:"settings,omitempty"`
QuerySettings []*SettingExpr `json:"query_settings,omitempty"` // Query-level SETTINGS (second SETTINGS clause)
SettingsBeforeComment bool `json:"settings_before_comment,omitempty"` // True if SETTINGS comes before COMMENT
AsSelect Statement `json:"as_select,omitempty"`
AsTableFunction Expression `json:"as_table_function,omitempty"` // AS table_function(...) in CREATE TABLE
CloneAs string `json:"clone_as,omitempty"` // CLONE AS source_table in CREATE TABLE
Expand Down Expand Up @@ -622,6 +632,7 @@ type AlterCommand struct {
OrderByExpr []Expression `json:"order_by_expr,omitempty"` // For MODIFY ORDER BY
SampleByExpr Expression `json:"sample_by_expr,omitempty"` // For MODIFY SAMPLE BY
ResetSettings []string `json:"reset_settings,omitempty"` // For MODIFY COLUMN ... RESET SETTING
Query Statement `json:"query,omitempty"` // For MODIFY QUERY
}

// Projection represents a projection definition.
Expand Down Expand Up @@ -675,10 +686,12 @@ const (
AlterDropConstraint AlterCommandType = "DROP_CONSTRAINT"
AlterModifyTTL AlterCommandType = "MODIFY_TTL"
AlterMaterializeTTL AlterCommandType = "MATERIALIZE_TTL"
AlterRemoveTTL AlterCommandType = "REMOVE_TTL"
AlterModifySetting AlterCommandType = "MODIFY_SETTING"
AlterResetSetting AlterCommandType = "RESET_SETTING"
AlterDropPartition AlterCommandType = "DROP_PARTITION"
AlterDetachPartition AlterCommandType = "DETACH_PARTITION"
AlterDropPartition AlterCommandType = "DROP_PARTITION"
AlterDropDetachedPartition AlterCommandType = "DROP_DETACHED_PARTITION"
AlterDetachPartition AlterCommandType = "DETACH_PARTITION"
AlterAttachPartition AlterCommandType = "ATTACH_PARTITION"
AlterReplacePartition AlterCommandType = "REPLACE_PARTITION"
AlterFetchPartition AlterCommandType = "FETCH_PARTITION"
Expand All @@ -700,19 +713,21 @@ const (
AlterModifyComment AlterCommandType = "MODIFY_COMMENT"
AlterModifyOrderBy AlterCommandType = "MODIFY_ORDER_BY"
AlterModifySampleBy AlterCommandType = "MODIFY_SAMPLE_BY"
AlterModifyQuery AlterCommandType = "MODIFY_QUERY"
AlterRemoveSampleBy AlterCommandType = "REMOVE_SAMPLE_BY"
AlterApplyDeletedMask AlterCommandType = "APPLY_DELETED_MASK"
)

// TruncateQuery represents a TRUNCATE statement.
type TruncateQuery struct {
Position token.Position `json:"-"`
Temporary bool `json:"temporary,omitempty"`
IfExists bool `json:"if_exists,omitempty"`
Database string `json:"database,omitempty"`
Table string `json:"table"`
OnCluster string `json:"on_cluster,omitempty"`
Settings []*SettingExpr `json:"settings,omitempty"`
Position token.Position `json:"-"`
Temporary bool `json:"temporary,omitempty"`
IfExists bool `json:"if_exists,omitempty"`
TruncateDatabase bool `json:"truncate_database,omitempty"` // True for TRUNCATE DATABASE
Database string `json:"database,omitempty"`
Table string `json:"table"`
OnCluster string `json:"on_cluster,omitempty"`
Settings []*SettingExpr `json:"settings,omitempty"`
}

func (t *TruncateQuery) Pos() token.Position { return t.Position }
Expand All @@ -724,7 +739,8 @@ type DeleteQuery struct {
Position token.Position `json:"-"`
Database string `json:"database,omitempty"`
Table string `json:"table"`
Partition Expression `json:"partition,omitempty"` // IN PARTITION clause
OnCluster string `json:"on_cluster,omitempty"` // ON CLUSTER clause
Partition Expression `json:"partition,omitempty"` // IN PARTITION clause
Where Expression `json:"where,omitempty"`
Settings []*SettingExpr `json:"settings,omitempty"`
}
Expand Down Expand Up @@ -762,6 +778,7 @@ type AttachQuery struct {
Database string `json:"database,omitempty"`
Table string `json:"table,omitempty"`
Dictionary string `json:"dictionary,omitempty"`
FromPath string `json:"from_path,omitempty"` // FROM 'path' clause
Columns []*ColumnDeclaration `json:"columns,omitempty"`
ColumnsPrimaryKey []Expression `json:"columns_primary_key,omitempty"` // PRIMARY KEY in column list
HasEmptyColumnsPrimaryKey bool `json:"has_empty_columns_primary_key,omitempty"` // TRUE if PRIMARY KEY () was seen with empty parens
Expand Down Expand Up @@ -952,6 +969,7 @@ type SystemQuery struct {
Table string `json:"table,omitempty"`
OnCluster string `json:"on_cluster,omitempty"`
DuplicateTableOutput bool `json:"duplicate_table_output,omitempty"` // True for commands that need database/table output twice
Settings []*SettingExpr `json:"settings,omitempty"`
}

func (s *SystemQuery) Pos() token.Position { return s.Position }
Expand Down Expand Up @@ -979,13 +997,14 @@ type RenamePair struct {

// RenameQuery represents a RENAME TABLE statement.
type RenameQuery struct {
Position token.Position `json:"-"`
Pairs []*RenamePair `json:"pairs"` // Multiple rename pairs
From string `json:"from,omitempty"` // Deprecated: for backward compat
To string `json:"to,omitempty"` // Deprecated: for backward compat
OnCluster string `json:"on_cluster,omitempty"`
Settings []*SettingExpr `json:"settings,omitempty"`
IfExists bool `json:"if_exists,omitempty"` // IF EXISTS modifier
Position token.Position `json:"-"`
Pairs []*RenamePair `json:"pairs"` // Multiple rename pairs
From string `json:"from,omitempty"` // Deprecated: for backward compat
To string `json:"to,omitempty"` // Deprecated: for backward compat
OnCluster string `json:"on_cluster,omitempty"`
Settings []*SettingExpr `json:"settings,omitempty"`
IfExists bool `json:"if_exists,omitempty"` // IF EXISTS modifier
RenameDatabase bool `json:"rename_database,omitempty"` // True for RENAME DATABASE
}

func (r *RenameQuery) Pos() token.Position { return r.Position }
Expand Down Expand Up @@ -1058,6 +1077,7 @@ type KillQuery struct {
Sync bool `json:"sync,omitempty"` // SYNC mode (default false = ASYNC)
Test bool `json:"test,omitempty"` // TEST mode
Format string `json:"format,omitempty"` // FORMAT clause
Settings []*SettingExpr `json:"settings,omitempty"`
}

func (k *KillQuery) Pos() token.Position { return k.Position }
Expand Down Expand Up @@ -1278,12 +1298,13 @@ func (d *DropWorkloadQuery) statementNode() {}

// CreateIndexQuery represents a CREATE INDEX statement.
type CreateIndexQuery struct {
Position token.Position `json:"-"`
IndexName string `json:"index_name"`
Table string `json:"table"`
Columns []Expression `json:"columns,omitempty"`
Type string `json:"type,omitempty"` // Index type (minmax, bloom_filter, etc.)
Granularity int `json:"granularity,omitempty"` // GRANULARITY value
Position token.Position `json:"-"`
IndexName string `json:"index_name"`
Table string `json:"table"`
Columns []Expression `json:"columns,omitempty"`
ColumnsParenthesized bool `json:"columns_parenthesized,omitempty"` // True if columns in (...)
Type string `json:"type,omitempty"` // Index type (minmax, bloom_filter, etc.)
Granularity int `json:"granularity,omitempty"` // GRANULARITY value
}

func (c *CreateIndexQuery) Pos() token.Position { return c.Position }
Expand Down Expand Up @@ -1427,6 +1448,7 @@ type ColumnTransformer struct {
Position token.Position `json:"-"`
Type string `json:"type"` // "apply", "except", "replace"
Apply string `json:"apply,omitempty"` // function name for APPLY
ApplyParams []Expression `json:"apply_params,omitempty"` // parameters for parameterized APPLY functions like quantiles(0.5)
ApplyLambda Expression `json:"apply_lambda,omitempty"` // lambda expression for APPLY x -> expr
Except []string `json:"except,omitempty"` // column names for EXCEPT
Pattern string `json:"pattern,omitempty"` // regex pattern for EXCEPT('pattern')
Expand Down Expand Up @@ -1571,9 +1593,10 @@ func (s *Subquery) expressionNode() {}

// WithElement represents a WITH element (CTE).
type WithElement struct {
Position token.Position `json:"-"`
Name string `json:"name"`
Query Expression `json:"query"` // Subquery or Expression
Position token.Position `json:"-"`
Name string `json:"name"`
Query Expression `json:"query"` // Subquery or Expression
ScalarWith bool `json:"scalar_with"` // True for "(expr) AS name" syntax, false for "name AS (SELECT ...)"
}

func (w *WithElement) Pos() token.Position { return w.Position }
Expand Down Expand Up @@ -1713,12 +1736,13 @@ func (b *BetweenExpr) expressionNode() {}

// InExpr represents an IN expression.
type InExpr struct {
Position token.Position `json:"-"`
Expr Expression `json:"expr"`
Not bool `json:"not,omitempty"`
Global bool `json:"global,omitempty"`
List []Expression `json:"list,omitempty"`
Query Statement `json:"query,omitempty"`
Position token.Position `json:"-"`
Expr Expression `json:"expr"`
Not bool `json:"not,omitempty"`
Global bool `json:"global,omitempty"`
List []Expression `json:"list,omitempty"`
Query Statement `json:"query,omitempty"`
TrailingComma bool `json:"trailing_comma,omitempty"` // true if list had trailing comma like (2,)
}

func (i *InExpr) Pos() token.Position { return i.Position }
Expand Down
2 changes: 1 addition & 1 deletion internal/explain/dictionary.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func explainDictionaryDefinition(sb *strings.Builder, n *ast.DictionaryDefinitio

// SETTINGS
if len(n.Settings) > 0 {
fmt.Fprintf(sb, "%s Set\n", indent)
fmt.Fprintf(sb, "%s Dictionary settings\n", indent)
}
}

Expand Down
92 changes: 86 additions & 6 deletions internal/explain/explain.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,85 @@ func Explain(stmt ast.Statement) string {
return sb.String()
}

// ExplainStatements returns the EXPLAIN AST output for multiple statements.
// This handles the special ClickHouse behavior where INSERT VALUES followed by SELECT
// on the same line outputs the INSERT AST and then executes the SELECT, printing its result.
func ExplainStatements(stmts []ast.Statement) string {
if len(stmts) == 0 {
return ""
}

var sb strings.Builder
Node(&sb, stmts[0], 0)

// If the first statement is an INSERT and there are subsequent SELECT statements
// with simple literals, append those literal values (matching ClickHouse's behavior)
if _, isInsert := stmts[0].(*ast.InsertQuery); isInsert {
for i := 1; i < len(stmts); i++ {
if result := getSimpleSelectResult(stmts[i]); result != "" {
sb.WriteString(result)
sb.WriteString("\n")
}
}
}

return sb.String()
}

// getSimpleSelectResult extracts the literal value from a simple SELECT statement
// like "SELECT 11111" and returns it as a string. Returns empty string if not a simple SELECT.
func getSimpleSelectResult(stmt ast.Statement) string {
// Check if it's a SelectWithUnionQuery
selectUnion, ok := stmt.(*ast.SelectWithUnionQuery)
if !ok {
return ""
}

// Must have exactly one select query
if len(selectUnion.Selects) != 1 {
return ""
}

// Get the inner select query
selectQuery, ok := selectUnion.Selects[0].(*ast.SelectQuery)
if !ok {
return ""
}

// Must have exactly one expression in the select list
if len(selectQuery.Columns) != 1 {
return ""
}

// Must be a literal
literal, ok := selectQuery.Columns[0].(*ast.Literal)
if !ok {
return ""
}

// Format the literal value
return formatLiteralValue(literal)
}

// formatLiteralValue formats a literal value as it would appear in query results
func formatLiteralValue(lit *ast.Literal) string {
switch v := lit.Value.(type) {
case int64:
return fmt.Sprintf("%d", v)
case float64:
return fmt.Sprintf("%v", v)
case string:
return v
case bool:
if v {
return "1"
}
return "0"
default:
return fmt.Sprintf("%v", v)
}
}

// Node writes the EXPLAIN AST output for an AST node.
func Node(sb *strings.Builder, node interface{}, depth int) {
if node == nil {
Expand Down Expand Up @@ -350,15 +429,16 @@ func Column(sb *strings.Builder, col *ast.ColumnDeclaration, depth int) {
children++
}
if children > 0 {
fmt.Fprintf(sb, "%sColumnDeclaration %s (children %d)\n", indent, col.Name, children)
fmt.Fprintf(sb, "%sColumnDeclaration %s (children %d)\n", indent, sanitizeUTF8(col.Name), children)
} else {
fmt.Fprintf(sb, "%sColumnDeclaration %s\n", indent, col.Name)
fmt.Fprintf(sb, "%sColumnDeclaration %s\n", indent, sanitizeUTF8(col.Name))
}
if col.Type != nil {
Node(sb, col.Type, depth+1)
}
if len(col.Statistics) > 0 {
explainStatisticsExpr(sb, col.Statistics, indent+" ", depth+1)
// Settings comes right after Type in ClickHouse EXPLAIN output
if len(col.Settings) > 0 {
fmt.Fprintf(sb, "%s Set\n", indent)
}
if col.Default != nil {
Node(sb, col.Default, depth+1)
Expand All @@ -372,8 +452,8 @@ func Column(sb *strings.Builder, col *ast.ColumnDeclaration, depth int) {
if col.Codec != nil {
explainCodecExpr(sb, col.Codec, indent+" ", depth+1)
}
if len(col.Settings) > 0 {
fmt.Fprintf(sb, "%s Set\n", indent)
if len(col.Statistics) > 0 {
explainStatisticsExpr(sb, col.Statistics, indent+" ", depth+1)
}
if col.Comment != "" {
fmt.Fprintf(sb, "%s Literal \\'%s\\'\n", indent, col.Comment)
Expand Down
Loading