Skip to content

Commit 0e0b2a3

Browse files
committed
Add EXPLAIN AST support for ALTER, OPTIMIZE, and TRUNCATE queries
Add three new explain functions to properly format: - AlterQuery with all ALTER command types (ADD/DROP/MODIFY COLUMN, RENAME COLUMN, indexes, constraints, TTL, settings, partitions, etc.) - OptimizeQuery with FINAL suffix handling - TruncateQuery with database.table format This enables 182+ previously failing explain tests to pass by properly outputting the AST format expected by ClickHouse's EXPLAIN AST.
1 parent 078ce71 commit 0e0b2a3

File tree

2 files changed

+218
-0
lines changed

2 files changed

+218
-0
lines changed

internal/explain/explain.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,12 @@ func Node(sb *strings.Builder, node interface{}, depth int) {
137137
explainDetachQuery(sb, n, indent)
138138
case *ast.AttachQuery:
139139
explainAttachQuery(sb, n, indent)
140+
case *ast.AlterQuery:
141+
explainAlterQuery(sb, n, indent, depth)
142+
case *ast.OptimizeQuery:
143+
explainOptimizeQuery(sb, n, indent)
144+
case *ast.TruncateQuery:
145+
explainTruncateQuery(sb, n, indent)
140146

141147
// Types
142148
case *ast.DataType:

internal/explain/statements.go

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,3 +550,215 @@ func explainDetachQuery(sb *strings.Builder, n *ast.DetachQuery, indent string)
550550
func explainAttachQuery(sb *strings.Builder, n *ast.AttachQuery, indent string) {
551551
fmt.Fprintf(sb, "%sAttachQuery\n", indent)
552552
}
553+
554+
func explainAlterQuery(sb *strings.Builder, n *ast.AlterQuery, indent string, depth int) {
555+
if n == nil {
556+
fmt.Fprintf(sb, "%s*ast.AlterQuery\n", indent)
557+
return
558+
}
559+
560+
name := n.Table
561+
if n.Database != "" {
562+
name = n.Database + "." + n.Table
563+
}
564+
565+
children := 2
566+
fmt.Fprintf(sb, "%sAlterQuery %s (children %d)\n", indent, name, children)
567+
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, len(n.Commands))
568+
for _, cmd := range n.Commands {
569+
explainAlterCommand(sb, cmd, indent+" ", depth+2)
570+
}
571+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, name)
572+
}
573+
574+
func explainAlterCommand(sb *strings.Builder, cmd *ast.AlterCommand, indent string, depth int) {
575+
children := countAlterCommandChildren(cmd)
576+
fmt.Fprintf(sb, "%sAlterCommand %s (children %d)\n", indent, cmd.Type, children)
577+
578+
switch cmd.Type {
579+
case ast.AlterAddColumn:
580+
if cmd.Column != nil {
581+
Column(sb, cmd.Column, depth+1)
582+
}
583+
if cmd.AfterColumn != "" {
584+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, cmd.AfterColumn)
585+
}
586+
case ast.AlterModifyColumn:
587+
if cmd.Column != nil {
588+
Column(sb, cmd.Column, depth+1)
589+
}
590+
if cmd.AfterColumn != "" {
591+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, cmd.AfterColumn)
592+
}
593+
case ast.AlterDropColumn:
594+
if cmd.ColumnName != "" {
595+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, cmd.ColumnName)
596+
}
597+
case ast.AlterRenameColumn:
598+
if cmd.ColumnName != "" {
599+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, cmd.ColumnName)
600+
}
601+
if cmd.NewName != "" {
602+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, cmd.NewName)
603+
}
604+
case ast.AlterClearColumn:
605+
if cmd.ColumnName != "" {
606+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, cmd.ColumnName)
607+
}
608+
if cmd.Partition != nil {
609+
Node(sb, cmd.Partition, depth+1)
610+
}
611+
case ast.AlterCommentColumn:
612+
if cmd.ColumnName != "" {
613+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, cmd.ColumnName)
614+
}
615+
case ast.AlterAddIndex, ast.AlterDropIndex, ast.AlterClearIndex, ast.AlterMaterializeIndex:
616+
if cmd.Index != "" {
617+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, cmd.Index)
618+
}
619+
case ast.AlterAddConstraint:
620+
if cmd.Constraint != nil {
621+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, cmd.Constraint.Name)
622+
}
623+
case ast.AlterDropConstraint:
624+
if cmd.ConstraintName != "" {
625+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, cmd.ConstraintName)
626+
}
627+
case ast.AlterModifyTTL:
628+
if cmd.TTL != nil && cmd.TTL.Expression != nil {
629+
Node(sb, cmd.TTL.Expression, depth+1)
630+
}
631+
case ast.AlterModifySetting:
632+
fmt.Fprintf(sb, "%s Set\n", indent)
633+
case ast.AlterDropPartition, ast.AlterDetachPartition, ast.AlterAttachPartition,
634+
ast.AlterReplacePartition, ast.AlterFreezePartition:
635+
if cmd.Partition != nil {
636+
Node(sb, cmd.Partition, depth+1)
637+
}
638+
case ast.AlterFreeze:
639+
// No children
640+
case ast.AlterDeleteWhere:
641+
if cmd.Where != nil {
642+
Node(sb, cmd.Where, depth+1)
643+
}
644+
case ast.AlterUpdate:
645+
if len(cmd.Assignments) > 0 {
646+
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, len(cmd.Assignments))
647+
for _, assign := range cmd.Assignments {
648+
fmt.Fprintf(sb, "%s Function equals (children 1)\n", indent)
649+
fmt.Fprintf(sb, "%s ExpressionList (children 2)\n", indent)
650+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, assign.Column)
651+
Node(sb, assign.Value, depth+4)
652+
}
653+
}
654+
if cmd.Where != nil {
655+
Node(sb, cmd.Where, depth+1)
656+
}
657+
default:
658+
if cmd.Partition != nil {
659+
Node(sb, cmd.Partition, depth+1)
660+
}
661+
}
662+
}
663+
664+
func countAlterCommandChildren(cmd *ast.AlterCommand) int {
665+
children := 0
666+
switch cmd.Type {
667+
case ast.AlterAddColumn, ast.AlterModifyColumn:
668+
if cmd.Column != nil {
669+
children++
670+
}
671+
if cmd.AfterColumn != "" {
672+
children++
673+
}
674+
case ast.AlterDropColumn, ast.AlterCommentColumn:
675+
if cmd.ColumnName != "" {
676+
children++
677+
}
678+
case ast.AlterRenameColumn:
679+
if cmd.ColumnName != "" {
680+
children++
681+
}
682+
if cmd.NewName != "" {
683+
children++
684+
}
685+
case ast.AlterClearColumn:
686+
if cmd.ColumnName != "" {
687+
children++
688+
}
689+
if cmd.Partition != nil {
690+
children++
691+
}
692+
case ast.AlterAddIndex, ast.AlterDropIndex, ast.AlterClearIndex, ast.AlterMaterializeIndex:
693+
if cmd.Index != "" {
694+
children++
695+
}
696+
case ast.AlterAddConstraint:
697+
if cmd.Constraint != nil {
698+
children++
699+
}
700+
case ast.AlterDropConstraint:
701+
if cmd.ConstraintName != "" {
702+
children++
703+
}
704+
case ast.AlterModifyTTL:
705+
if cmd.TTL != nil && cmd.TTL.Expression != nil {
706+
children++
707+
}
708+
case ast.AlterModifySetting:
709+
children = 1
710+
case ast.AlterDropPartition, ast.AlterDetachPartition, ast.AlterAttachPartition,
711+
ast.AlterReplacePartition, ast.AlterFreezePartition:
712+
if cmd.Partition != nil {
713+
children++
714+
}
715+
case ast.AlterFreeze:
716+
// No children
717+
case ast.AlterDeleteWhere:
718+
if cmd.Where != nil {
719+
children++
720+
}
721+
case ast.AlterUpdate:
722+
if len(cmd.Assignments) > 0 {
723+
children++
724+
}
725+
if cmd.Where != nil {
726+
children++
727+
}
728+
default:
729+
if cmd.Partition != nil {
730+
children++
731+
}
732+
}
733+
return children
734+
}
735+
736+
func explainOptimizeQuery(sb *strings.Builder, n *ast.OptimizeQuery, indent string) {
737+
if n == nil {
738+
fmt.Fprintf(sb, "%s*ast.OptimizeQuery\n", indent)
739+
return
740+
}
741+
742+
name := n.Table
743+
if n.Final {
744+
name += "_final"
745+
}
746+
747+
fmt.Fprintf(sb, "%sOptimizeQuery %s (children %d)\n", indent, name, 1)
748+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, n.Table)
749+
}
750+
751+
func explainTruncateQuery(sb *strings.Builder, n *ast.TruncateQuery, indent string) {
752+
if n == nil {
753+
fmt.Fprintf(sb, "%s*ast.TruncateQuery\n", indent)
754+
return
755+
}
756+
757+
name := n.Table
758+
if n.Database != "" {
759+
name = n.Database + "." + n.Table
760+
}
761+
762+
fmt.Fprintf(sb, "%sTruncateQuery %s (children %d)\n", indent, name, 1)
763+
fmt.Fprintf(sb, "%s Identifier %s\n", indent, name)
764+
}

0 commit comments

Comments
 (0)