@@ -17,6 +17,10 @@ func Explain(stmt ast.Statement) string {
1717// explainNode writes the EXPLAIN AST output for an AST node.
1818func explainNode (sb * strings.Builder , node interface {}, depth int ) {
1919 if node == nil {
20+ // nil can represent an empty tuple in function arguments
21+ indent := strings .Repeat (" " , depth )
22+ fmt .Fprintf (sb , "%sFunction tuple (children %d)\n " , indent , 1 )
23+ fmt .Fprintf (sb , "%s ExpressionList\n " , indent )
2024 return
2125 }
2226
@@ -144,9 +148,16 @@ func explainNode(sb *strings.Builder, node interface{}, depth int) {
144148 }
145149
146150 case * ast.Literal :
147- // Check if this is a tuple with complex expressions that should be rendered as Function tuple
151+ // Check if this is a tuple - either with expressions or empty
148152 if n .Type == ast .LiteralTuple {
149153 if exprs , ok := n .Value .([]ast.Expression ); ok {
154+ // Check if empty tuple or has complex expressions
155+ if len (exprs ) == 0 {
156+ // Empty tuple renders as Function tuple with empty ExpressionList
157+ fmt .Fprintf (sb , "%sFunction tuple (children %d)\n " , indent , 1 )
158+ fmt .Fprintf (sb , "%s ExpressionList\n " , indent )
159+ return
160+ }
150161 hasComplexExpr := false
151162 for _ , e := range exprs {
152163 if _ , isLit := e .(* ast.Literal ); ! isLit {
@@ -163,6 +174,32 @@ func explainNode(sb *strings.Builder, node interface{}, depth int) {
163174 }
164175 return
165176 }
177+ } else if n .Value == nil {
178+ // nil value means empty tuple
179+ fmt .Fprintf (sb , "%sFunction tuple (children %d)\n " , indent , 1 )
180+ fmt .Fprintf (sb , "%s ExpressionList\n " , indent )
181+ return
182+ }
183+ }
184+ // Check if this is an array with complex expressions that should be rendered as Function array
185+ if n .Type == ast .LiteralArray {
186+ if exprs , ok := n .Value .([]ast.Expression ); ok {
187+ hasComplexExpr := false
188+ for _ , e := range exprs {
189+ if _ , isLit := e .(* ast.Literal ); ! isLit {
190+ hasComplexExpr = true
191+ break
192+ }
193+ }
194+ if hasComplexExpr {
195+ // Render as Function array instead of Literal
196+ fmt .Fprintf (sb , "%sFunction array (children %d)\n " , indent , 1 )
197+ fmt .Fprintf (sb , "%s ExpressionList (children %d)\n " , indent , len (exprs ))
198+ for _ , e := range exprs {
199+ explainNode (sb , e , depth + 2 )
200+ }
201+ return
202+ }
166203 }
167204 }
168205 fmt .Fprintf (sb , "%sLiteral %s\n " , indent , formatLiteral (n ))
@@ -172,10 +209,12 @@ func explainNode(sb *strings.Builder, node interface{}, depth int) {
172209 if len (n .Parameters ) > 0 {
173210 children ++ // parameters ExpressionList
174211 }
212+ // Normalize function name
213+ fnName := normalizeFunctionName (n .Name )
175214 if n .Alias != "" {
176- fmt .Fprintf (sb , "%sFunction %s (alias %s) (children %d)\n " , indent , n . Name , n .Alias , children )
215+ fmt .Fprintf (sb , "%sFunction %s (alias %s) (children %d)\n " , indent , fnName , n .Alias , children )
177216 } else {
178- fmt .Fprintf (sb , "%sFunction %s (children %d)\n " , indent , n . Name , children )
217+ fmt .Fprintf (sb , "%sFunction %s (children %d)\n " , indent , fnName , children )
179218 }
180219 // Arguments
181220 fmt .Fprintf (sb , "%s ExpressionList" , indent )
@@ -469,14 +508,16 @@ func explainNode(sb *strings.Builder, node interface{}, depth int) {
469508 }
470509
471510 case * ast.SystemQuery :
472- fmt .Fprintf (sb , "%sSystem %s \n " , indent , n . Command )
511+ fmt .Fprintf (sb , "%sSYSTEM query \n " , indent )
473512
474513 case * ast.ExplainQuery :
475514 fmt .Fprintf (sb , "%sExplain %s (children %d)\n " , indent , n .ExplainType , 1 )
476515 explainNode (sb , n .Statement , depth + 1 )
477516
478517 case * ast.ShowQuery :
479- fmt .Fprintf (sb , "%sShow%s\n " , indent , n .ShowType )
518+ // Capitalize ShowType correctly for display
519+ showType := strings .Title (strings .ToLower (string (n .ShowType )))
520+ fmt .Fprintf (sb , "%sShow%s\n " , indent , showType )
480521
481522 case * ast.UseQuery :
482523 fmt .Fprintf (sb , "%sUse %s\n " , indent , n .Database )
@@ -692,6 +733,37 @@ func formatDataType(dt *ast.DataType) string {
692733 return fmt .Sprintf ("%s(%s)" , dt .Name , strings .Join (params , ", " ))
693734}
694735
736+ // normalizeFunctionName normalizes function names to match ClickHouse's EXPLAIN AST output
737+ func normalizeFunctionName (name string ) string {
738+ // ClickHouse normalizes certain function names in EXPLAIN AST
739+ normalized := map [string ]string {
740+ "ltrim" : "trimLeft" ,
741+ "rtrim" : "trimRight" ,
742+ "lcase" : "lower" ,
743+ "ucase" : "upper" ,
744+ "mid" : "substring" ,
745+ "substr" : "substring" ,
746+ "pow" : "power" ,
747+ "ceil" : "ceiling" ,
748+ "ln" : "log" ,
749+ "log10" : "log10" ,
750+ "log2" : "log2" ,
751+ "rand" : "rand" ,
752+ "ifnull" : "ifNull" ,
753+ "nullif" : "nullIf" ,
754+ "coalesce" : "coalesce" ,
755+ "greatest" : "greatest" ,
756+ "least" : "least" ,
757+ "concat_ws" : "concat" ,
758+ "length" : "length" ,
759+ "char_length" : "length" ,
760+ }
761+ if n , ok := normalized [strings .ToLower (name )]; ok {
762+ return n
763+ }
764+ return name
765+ }
766+
695767// operatorToFunction maps binary operators to ClickHouse function names
696768func operatorToFunction (op string ) string {
697769 switch op {
0 commit comments