@@ -19,19 +19,165 @@ func explainSelectIntersectExceptQuery(sb *strings.Builder, n *ast.SelectInterse
1919 }
2020 }
2121
22+ // Check if first operand has a WITH clause to be inherited by subsequent operands
23+ var inheritedWith []ast.Expression
24+ if len (n .Selects ) > 0 {
25+ inheritedWith = extractWithClause (n .Selects [0 ])
26+ }
27+
2228 childIndent := strings .Repeat (" " , depth + 1 )
2329 for i , sel := range n .Selects {
2430 if hasExcept && i == 0 {
2531 // Wrap first operand in SelectWithUnionQuery -> ExpressionList format
26- fmt .Fprintf (sb , "%sSelectWithUnionQuery (children 1)\n " , childIndent )
27- fmt .Fprintf (sb , "%s ExpressionList (children 1)\n " , childIndent )
28- Node (sb , sel , depth + 3 )
32+ // But if it's already a SelectWithUnionQuery, don't double-wrap
33+ if _ , isUnion := sel .(* ast.SelectWithUnionQuery ); isUnion {
34+ Node (sb , sel , depth + 1 )
35+ } else {
36+ fmt .Fprintf (sb , "%sSelectWithUnionQuery (children 1)\n " , childIndent )
37+ fmt .Fprintf (sb , "%s ExpressionList (children 1)\n " , childIndent )
38+ Node (sb , sel , depth + 3 )
39+ }
40+ } else if i > 0 && len (inheritedWith ) > 0 {
41+ // Subsequent operands inherit the WITH clause from the first operand
42+ explainSelectQueryWithInheritedWith (sb , sel , inheritedWith , depth + 1 )
2943 } else {
3044 Node (sb , sel , depth + 1 )
3145 }
3246 }
3347}
3448
49+ // extractWithClause extracts the WITH clause from a statement (if it's a SelectQuery)
50+ func extractWithClause (stmt ast.Statement ) []ast.Expression {
51+ switch s := stmt .(type ) {
52+ case * ast.SelectQuery :
53+ return s .With
54+ case * ast.SelectWithUnionQuery :
55+ // Check the first select in the union
56+ if len (s .Selects ) > 0 {
57+ return extractWithClause (s .Selects [0 ])
58+ }
59+ }
60+ return nil
61+ }
62+
63+ // explainSelectQueryWithInheritedWith outputs a SELECT with an inherited WITH clause
64+ // The inherited WITH clause is output AFTER the columns (not before, like a regular WITH)
65+ func explainSelectQueryWithInheritedWith (sb * strings.Builder , stmt ast.Statement , inheritedWith []ast.Expression , depth int ) {
66+ sq , ok := stmt .(* ast.SelectQuery )
67+ if ! ok {
68+ // Not a SelectQuery, output normally
69+ Node (sb , stmt , depth )
70+ return
71+ }
72+
73+ // If the SelectQuery already has a WITH clause, output normally
74+ if len (sq .With ) > 0 {
75+ Node (sb , stmt , depth )
76+ return
77+ }
78+
79+ // Output SelectQuery with inherited WITH clause after columns
80+ indent := strings .Repeat (" " , depth )
81+ children := countSelectQueryChildren (sq ) + 1 // +1 for inherited WITH clause
82+ fmt .Fprintf (sb , "%sSelectQuery (children %d)\n " , indent , children )
83+
84+ // Columns (ExpressionList) - output BEFORE inherited WITH
85+ fmt .Fprintf (sb , "%s ExpressionList (children %d)\n " , indent , len (sq .Columns ))
86+ for _ , col := range sq .Columns {
87+ Node (sb , col , depth + 2 )
88+ }
89+
90+ // Inherited WITH clause (ExpressionList) - output AFTER columns
91+ fmt .Fprintf (sb , "%s ExpressionList (children %d)\n " , indent , len (inheritedWith ))
92+ for _ , w := range inheritedWith {
93+ Node (sb , w , depth + 2 )
94+ }
95+
96+ // FROM (including ARRAY JOIN as part of TablesInSelectQuery)
97+ if sq .From != nil || sq .ArrayJoin != nil {
98+ TablesWithArrayJoin (sb , sq .From , sq .ArrayJoin , depth + 1 )
99+ }
100+ // PREWHERE
101+ if sq .PreWhere != nil {
102+ Node (sb , sq .PreWhere , depth + 1 )
103+ }
104+ // WHERE
105+ if sq .Where != nil {
106+ Node (sb , sq .Where , depth + 1 )
107+ }
108+ // GROUP BY (skip for GROUP BY ALL which doesn't output an expression list)
109+ if len (sq .GroupBy ) > 0 && ! sq .GroupByAll {
110+ fmt .Fprintf (sb , "%s ExpressionList (children %d)\n " , indent , len (sq .GroupBy ))
111+ for _ , g := range sq .GroupBy {
112+ Node (sb , g , depth + 2 )
113+ }
114+ }
115+ // HAVING
116+ if sq .Having != nil {
117+ Node (sb , sq .Having , depth + 1 )
118+ }
119+ // QUALIFY
120+ if sq .Qualify != nil {
121+ Node (sb , sq .Qualify , depth + 1 )
122+ }
123+ // WINDOW clause
124+ if len (sq .Window ) > 0 {
125+ fmt .Fprintf (sb , "%s ExpressionList (children %d)\n " , indent , len (sq .Window ))
126+ for range sq .Window {
127+ fmt .Fprintf (sb , "%s WindowListElement\n " , indent )
128+ }
129+ }
130+ // ORDER BY
131+ if len (sq .OrderBy ) > 0 {
132+ fmt .Fprintf (sb , "%s ExpressionList (children %d)\n " , indent , len (sq .OrderBy ))
133+ for _ , o := range sq .OrderBy {
134+ Node (sb , o , depth + 2 )
135+ }
136+ }
137+ // INTERPOLATE
138+ if len (sq .Interpolate ) > 0 {
139+ fmt .Fprintf (sb , "%s ExpressionList (children %d)\n " , indent , len (sq .Interpolate ))
140+ for _ , i := range sq .Interpolate {
141+ Node (sb , i , depth + 2 )
142+ }
143+ }
144+ // OFFSET
145+ if sq .Offset != nil {
146+ Node (sb , sq .Offset , depth + 1 )
147+ }
148+ // LIMIT BY handling
149+ if sq .LimitByLimit != nil {
150+ Node (sb , sq .LimitByLimit , depth + 1 )
151+ if len (sq .LimitBy ) > 0 {
152+ fmt .Fprintf (sb , "%s ExpressionList (children %d)\n " , indent , len (sq .LimitBy ))
153+ for _ , expr := range sq .LimitBy {
154+ Node (sb , expr , depth + 2 )
155+ }
156+ }
157+ if sq .Limit != nil {
158+ Node (sb , sq .Limit , depth + 1 )
159+ }
160+ } else if len (sq .LimitBy ) > 0 {
161+ if sq .Limit != nil {
162+ Node (sb , sq .Limit , depth + 1 )
163+ }
164+ fmt .Fprintf (sb , "%s ExpressionList (children %d)\n " , indent , len (sq .LimitBy ))
165+ for _ , expr := range sq .LimitBy {
166+ Node (sb , expr , depth + 2 )
167+ }
168+ } else if sq .Limit != nil {
169+ Node (sb , sq .Limit , depth + 1 )
170+ }
171+ // SETTINGS
172+ if len (sq .Settings ) > 0 && ! sq .SettingsAfterFormat {
173+ fmt .Fprintf (sb , "%s Set\n " , indent )
174+ }
175+ // TOP clause
176+ if sq .Top != nil {
177+ Node (sb , sq .Top , depth + 1 )
178+ }
179+ }
180+
35181func explainSelectWithUnionQuery (sb * strings.Builder , n * ast.SelectWithUnionQuery , indent string , depth int ) {
36182 if n == nil {
37183 return
0 commit comments