@@ -83,8 +83,19 @@ func explainCastExpr(sb *strings.Builder, n *ast.CastExpr, indent string, depth
8383}
8484
8585func explainCastExprWithAlias (sb * strings.Builder , n * ast.CastExpr , alias string , indent string , depth int ) {
86+ // For :: operator syntax, ClickHouse hides alias only when expression is
87+ // an array/tuple with complex content that gets formatted as string
88+ hideAlias := false
89+ if n .OperatorSyntax {
90+ if lit , ok := n .Expr .(* ast.Literal ); ok {
91+ if lit .Type == ast .LiteralArray || lit .Type == ast .LiteralTuple {
92+ hideAlias = ! containsOnlyPrimitives (lit )
93+ }
94+ }
95+ }
96+
8697 // CAST is represented as Function CAST with expr and type as arguments
87- if alias != "" {
98+ if alias != "" && ! hideAlias {
8899 fmt .Fprintf (sb , "%sFunction CAST (alias %s) (children %d)\n " , indent , alias , 1 )
89100 } else {
90101 fmt .Fprintf (sb , "%sFunction CAST (children %d)\n " , indent , 1 )
@@ -205,6 +216,18 @@ func explainArrayAccess(sb *strings.Builder, n *ast.ArrayAccess, indent string,
205216 Node (sb , n .Index , depth + 2 )
206217}
207218
219+ func explainArrayAccessWithAlias (sb * strings.Builder , n * ast.ArrayAccess , alias string , indent string , depth int ) {
220+ // Array access is represented as Function arrayElement
221+ if alias != "" {
222+ fmt .Fprintf (sb , "%sFunction arrayElement (alias %s) (children %d)\n " , indent , alias , 1 )
223+ } else {
224+ fmt .Fprintf (sb , "%sFunction arrayElement (children %d)\n " , indent , 1 )
225+ }
226+ fmt .Fprintf (sb , "%s ExpressionList (children %d)\n " , indent , 2 )
227+ Node (sb , n .Array , depth + 2 )
228+ Node (sb , n .Index , depth + 2 )
229+ }
230+
208231func explainTupleAccess (sb * strings.Builder , n * ast.TupleAccess , indent string , depth int ) {
209232 // Tuple access is represented as Function tupleElement
210233 fmt .Fprintf (sb , "%sFunction tupleElement (children %d)\n " , indent , 1 )
@@ -213,6 +236,18 @@ func explainTupleAccess(sb *strings.Builder, n *ast.TupleAccess, indent string,
213236 Node (sb , n .Index , depth + 2 )
214237}
215238
239+ func explainTupleAccessWithAlias (sb * strings.Builder , n * ast.TupleAccess , alias string , indent string , depth int ) {
240+ // Tuple access is represented as Function tupleElement
241+ if alias != "" {
242+ fmt .Fprintf (sb , "%sFunction tupleElement (alias %s) (children %d)\n " , indent , alias , 1 )
243+ } else {
244+ fmt .Fprintf (sb , "%sFunction tupleElement (children %d)\n " , indent , 1 )
245+ }
246+ fmt .Fprintf (sb , "%s ExpressionList (children %d)\n " , indent , 2 )
247+ Node (sb , n .Tuple , depth + 2 )
248+ Node (sb , n .Index , depth + 2 )
249+ }
250+
216251func explainLikeExpr (sb * strings.Builder , n * ast.LikeExpr , indent string , depth int ) {
217252 // LIKE is represented as Function like
218253 fnName := "like"
@@ -229,17 +264,37 @@ func explainLikeExpr(sb *strings.Builder, n *ast.LikeExpr, indent string, depth
229264}
230265
231266func explainBetweenExpr (sb * strings.Builder , n * ast.BetweenExpr , indent string , depth int ) {
232- // BETWEEN is represented as Function and with two comparisons
233- // But for explain, we can use a simpler form
234- fnName := "between"
235267 if n .Not {
236- fnName = "notBetween"
268+ // NOT BETWEEN is transformed to: expr < low OR expr > high
269+ // Represented as: Function or with two comparisons: less and greater
270+ fmt .Fprintf (sb , "%sFunction or (children %d)\n " , indent , 1 )
271+ fmt .Fprintf (sb , "%s ExpressionList (children %d)\n " , indent , 2 )
272+ // less(expr, low)
273+ fmt .Fprintf (sb , "%s Function less (children %d)\n " , indent , 1 )
274+ fmt .Fprintf (sb , "%s ExpressionList (children %d)\n " , indent , 2 )
275+ Node (sb , n .Expr , depth + 4 )
276+ Node (sb , n .Low , depth + 4 )
277+ // greater(expr, high)
278+ fmt .Fprintf (sb , "%s Function greater (children %d)\n " , indent , 1 )
279+ fmt .Fprintf (sb , "%s ExpressionList (children %d)\n " , indent , 2 )
280+ Node (sb , n .Expr , depth + 4 )
281+ Node (sb , n .High , depth + 4 )
282+ } else {
283+ // BETWEEN is represented as Function and with two comparisons
284+ // expr >= low AND expr <= high
285+ fmt .Fprintf (sb , "%sFunction and (children %d)\n " , indent , 1 )
286+ fmt .Fprintf (sb , "%s ExpressionList (children %d)\n " , indent , 2 )
287+ // greaterOrEquals(expr, low)
288+ fmt .Fprintf (sb , "%s Function greaterOrEquals (children %d)\n " , indent , 1 )
289+ fmt .Fprintf (sb , "%s ExpressionList (children %d)\n " , indent , 2 )
290+ Node (sb , n .Expr , depth + 4 )
291+ Node (sb , n .Low , depth + 4 )
292+ // lessOrEquals(expr, high)
293+ fmt .Fprintf (sb , "%s Function lessOrEquals (children %d)\n " , indent , 1 )
294+ fmt .Fprintf (sb , "%s ExpressionList (children %d)\n " , indent , 2 )
295+ Node (sb , n .Expr , depth + 4 )
296+ Node (sb , n .High , depth + 4 )
237297 }
238- fmt .Fprintf (sb , "%sFunction %s (children %d)\n " , indent , fnName , 1 )
239- fmt .Fprintf (sb , "%s ExpressionList (children %d)\n " , indent , 3 )
240- Node (sb , n .Expr , depth + 2 )
241- Node (sb , n .Low , depth + 2 )
242- Node (sb , n .High , depth + 2 )
243298}
244299
245300func explainIsNullExpr (sb * strings.Builder , n * ast.IsNullExpr , indent string , depth int ) {
@@ -325,6 +380,7 @@ func explainExtractExpr(sb *strings.Builder, n *ast.ExtractExpr, indent string,
325380func explainWindowSpec (sb * strings.Builder , n * ast.WindowSpec , indent string , depth int ) {
326381 // Window spec is represented as WindowDefinition
327382 // For simple cases like OVER (), just output WindowDefinition without children
383+ // Note: ClickHouse's EXPLAIN AST does not output frame info (ROWS BETWEEN etc)
328384 children := 0
329385 if n .Name != "" {
330386 children ++
@@ -335,9 +391,6 @@ func explainWindowSpec(sb *strings.Builder, n *ast.WindowSpec, indent string, de
335391 if len (n .OrderBy ) > 0 {
336392 children ++
337393 }
338- if n .Frame != nil {
339- children ++
340- }
341394 if children > 0 {
342395 fmt .Fprintf (sb , "%sWindowDefinition (children %d)\n " , indent , children )
343396 if n .Name != "" {
@@ -352,7 +405,7 @@ func explainWindowSpec(sb *strings.Builder, n *ast.WindowSpec, indent string, de
352405 if len (n .OrderBy ) > 0 {
353406 fmt .Fprintf (sb , "%s ExpressionList (children %d)\n " , indent , len (n .OrderBy ))
354407 for _ , o := range n .OrderBy {
355- Node (sb , o . Expression , depth + 2 )
408+ explainOrderByElement (sb , o , strings . Repeat ( " " , depth + 2 ) , depth + 2 )
356409 }
357410 }
358411 // Frame handling would go here if needed
0 commit comments