@@ -110,228 +110,156 @@ type column interface {
110
110
}
111
111
112
112
func qualifyColumns (ctx * sql.Context , a * Analyzer , n sql.Node ) (sql.Node , error ) {
113
- span , _ := ctx .Span ("qualify_columns" )
114
- defer span .Finish ()
115
-
116
- a .Log ("qualify columns" )
117
- tables := make (map [string ]sql.Node )
118
- tableAliases := make (map [string ]string )
119
- colIndex := make (map [string ][]string )
120
-
121
- indexCols := func (table string , schema sql.Schema ) {
122
- for _ , col := range schema {
123
- name := strings .ToLower (col .Name )
124
- colIndex [name ] = append (colIndex [name ], strings .ToLower (table ))
125
- }
126
- }
127
-
128
- var projects , seenProjects int
129
- plan .Inspect (n , func (n sql.Node ) bool {
130
- if _ , ok := n .(* plan.Project ); ok {
131
- projects ++
132
- }
133
- return true
134
- })
135
-
136
113
return n .TransformUp (func (n sql.Node ) (sql.Node , error ) {
137
- a .Log ("transforming node of type: %T" , n )
138
- switch n := n .(type ) {
139
- case * plan.TableAlias :
140
- switch t := n .Child .(type ) {
141
- case * plan.ResolvedTable , * plan.UnresolvedTable :
142
- name := strings .ToLower (t .(sql.Nameable ).Name ())
143
- tableAliases [strings .ToLower (n .Name ())] = name
144
- default :
145
- tables [strings .ToLower (n .Name ())] = n .Child
146
- indexCols (n .Name (), n .Schema ())
147
- }
148
- case * plan.ResolvedTable , * plan.SubqueryAlias :
149
- name := strings .ToLower (n .(sql.Nameable ).Name ())
150
- tables [name ] = n
151
- indexCols (name , n .Schema ())
152
- }
153
-
154
114
exp , ok := n .(sql.Expressioner )
155
- if ! ok {
115
+ if ! ok || n . Resolved () {
156
116
return n , nil
157
117
}
158
118
159
- result , err := exp .TransformExpressions (func (e sql.Expression ) (sql.Expression , error ) {
160
- a .Log ("transforming expression of type: %T" , e )
161
- switch col := e .(type ) {
162
- case * expression.UnresolvedColumn :
163
- // Skip this step for global and session variables
164
- if isGlobalOrSessionColumn (col ) {
165
- return col , nil
166
- }
119
+ columns := getNodeAvailableColumns (n )
120
+ tables := getNodeAvailableTables (n )
167
121
168
- col = expression .NewUnresolvedQualifiedColumn (col .Table (), col .Name ())
169
- name := strings .ToLower (col .Name ())
170
- table := strings .ToLower (col .Table ())
171
- if table == "" {
172
- // If a column has no table, it might be an alias
173
- // defined in a child projection, so check that instead
174
- // of incorrectly qualify it.
175
- if isDefinedInChildProject (n , col ) {
176
- return col , nil
177
- }
178
-
179
- tables := dedupStrings (colIndex [name ])
180
- switch len (tables ) {
181
- case 0 :
182
- // If there are no tables that have any column with the column
183
- // name let's just return it as it is. This may be an alias, so
184
- // we'll wait for the reorder of the projection.
185
- return col , nil
186
- case 1 :
187
- col = expression .NewUnresolvedQualifiedColumn (
188
- tables [0 ],
189
- col .Name (),
190
- )
191
- default :
192
- if _ , ok := n .(* plan.GroupBy ); ok {
193
- return expression .NewUnresolvedColumn (col .Name ()), nil
194
- }
195
- return nil , ErrAmbiguousColumnName .New (col .Name (), strings .Join (tables , ", " ))
196
- }
197
- } else {
198
- if real , ok := tableAliases [table ]; ok {
199
- col = expression .NewUnresolvedQualifiedColumn (
200
- real ,
201
- col .Name (),
202
- )
203
- }
122
+ return exp .TransformExpressions (func (e sql.Expression ) (sql.Expression , error ) {
123
+ return qualifyExpression (e , columns , tables )
124
+ })
125
+ })
126
+ }
204
127
205
- if _ , ok := tables [col .Table ()]; ! ok {
206
- if len (tables ) == 0 {
207
- return nil , sql .ErrTableNotFound .New (col .Table ())
208
- }
128
+ func qualifyExpression (
129
+ e sql.Expression ,
130
+ columns map [string ][]string ,
131
+ tables map [string ]string ,
132
+ ) (sql.Expression , error ) {
133
+ switch col := e .(type ) {
134
+ case column :
135
+ // Skip this step for global and session variables
136
+ if isGlobalOrSessionColumn (col ) {
137
+ return col , nil
138
+ }
209
139
210
- similar := similartext .FindFromMap (tables , col .Table ())
211
- return nil , sql .ErrTableNotFound .New (col .Table () + similar )
212
- }
140
+ name , table := strings .ToLower (col .Name ()), strings .ToLower (col .Table ())
141
+ availableTables := dedupStrings (columns [name ])
142
+ if table != "" {
143
+ table , ok := tables [table ]
144
+ if ! ok {
145
+ if len (tables ) == 0 {
146
+ return nil , sql .ErrTableNotFound .New (col .Table ())
213
147
}
214
148
215
- a .Log ("column %q was qualified with table %q" , col .Name (), col .Table ())
149
+ similar := similartext .FindFromMap (tables , col .Table ())
150
+ return nil , sql .ErrTableNotFound .New (col .Table () + similar )
151
+ }
152
+
153
+ // If the table exists but it's not available for this node it
154
+ // means some work is still needed, so just return the column
155
+ // and let it be resolved in the next pass.
156
+ if ! stringContains (availableTables , table ) {
216
157
return col , nil
217
- case * expression.Star :
218
- if col .Table != "" {
219
- if real , ok := tableAliases [strings .ToLower (col .Table )]; ok {
220
- col = expression .NewQualifiedStar (real )
221
- }
158
+ }
222
159
223
- if _ , ok := tables [strings .ToLower (col .Table )]; ! ok {
224
- return nil , sql .ErrTableNotFound .New (col .Table )
225
- }
160
+ return expression .NewUnresolvedQualifiedColumn (table , col .Name ()), nil
161
+ }
226
162
227
- return col , nil
228
- }
229
- default :
230
- // If any other kind of expression has a star, just replace it
231
- // with an unqualified star because it cannot be expanded.
232
- return e .TransformUp (func (e sql.Expression ) (sql.Expression , error ) {
233
- if _ , ok := e .(* expression.Star ); ok {
234
- return expression .NewStar (), nil
235
- }
236
- return e , nil
237
- })
163
+ switch len (availableTables ) {
164
+ case 0 :
165
+ // If there are no tables that have any column with the column
166
+ // name let's just return it as it is. This may be an alias, so
167
+ // we'll wait for the reorder of the projection.
168
+ return col , nil
169
+ case 1 :
170
+ return expression .NewUnresolvedQualifiedColumn (
171
+ availableTables [0 ],
172
+ col .Name (),
173
+ ), nil
174
+ default :
175
+ return nil , ErrAmbiguousColumnName .New (col .Name (), strings .Join (availableTables , ", " ))
176
+ }
177
+ case * expression.Star :
178
+ if col .Table != "" {
179
+ if real , ok := tables [strings .ToLower (col .Table )]; ok {
180
+ col = expression .NewQualifiedStar (real )
238
181
}
239
182
183
+ if _ , ok := tables [strings .ToLower (col .Table )]; ! ok {
184
+ return nil , sql .ErrTableNotFound .New (col .Table )
185
+ }
186
+ }
187
+ return col , nil
188
+ default :
189
+ // If any other kind of expression has a star, just replace it
190
+ // with an unqualified star because it cannot be expanded.
191
+ return e .TransformUp (func (e sql.Expression ) (sql.Expression , error ) {
192
+ if _ , ok := e .(* expression.Star ); ok {
193
+ return expression .NewStar (), nil
194
+ }
240
195
return e , nil
241
196
})
197
+ }
198
+ }
242
199
243
- if err != nil {
244
- return nil , err
245
- }
200
+ func getNodeAvailableColumns (n sql.Node ) map [string ][]string {
201
+ var columns = make (map [string ][]string )
202
+ getColumnsInNodes (n .Children (), columns )
203
+ return columns
204
+ }
246
205
247
- // We should ignore the topmost project, because some nodes are
248
- // reordered, such as Sort, and they would not be resolved well.
249
- if n , ok := result .(* plan.Project ); ok && projects - seenProjects > 1 {
250
- seenProjects ++
251
-
252
- // We need to modify the indexed columns to only contain what is
253
- // projected in this project. If the column is not qualified by any
254
- // table, just keep the ones that are currently in the index.
255
- // If it is, then just make those tables available for the column.
256
- // If we don't do this, columns that are not projected will be
257
- // available in this step and may cause false errors or unintended
258
- // results.
259
- var projected = make (map [string ][]string )
260
- for _ , p := range n .Projections {
261
- var table , col string
262
- switch p := p .(type ) {
263
- case column :
264
- table = p .Table ()
265
- col = p .Name ()
266
- default :
267
- continue
268
- }
206
+ func getColumnsInNodes (nodes []sql.Node , columns map [string ][]string ) {
207
+ indexCol := func (table , col string ) {
208
+ col = strings .ToLower (col )
209
+ columns [col ] = append (columns [col ], strings .ToLower (table ))
210
+ }
269
211
270
- col = strings .ToLower (col )
271
- table = strings .ToLower (table )
272
- if table != "" {
273
- projected [col ] = append (projected [col ], table )
274
- } else {
275
- projected [col ] = append (projected [col ], colIndex [col ]... )
276
- }
212
+ indexExpressions := func (exprs []sql.Expression ) {
213
+ for _ , e := range exprs {
214
+ switch e := e .(type ) {
215
+ case * expression.Alias :
216
+ indexCol ("" , e .Name ())
217
+ case * expression.GetField :
218
+ indexCol (e .Table (), e .Name ())
219
+ case * expression.UnresolvedColumn :
220
+ indexCol (e .Table (), e .Name ())
277
221
}
222
+ }
223
+ }
278
224
279
- colIndex = make (map [string ][]string )
280
- for col , tables := range projected {
281
- colIndex [col ] = dedupStrings (tables )
225
+ for _ , node := range nodes {
226
+ switch n := node .(type ) {
227
+ case * plan.ResolvedTable , * plan.SubqueryAlias :
228
+ for _ , col := range n .Schema () {
229
+ indexCol (col .Source , col .Name )
282
230
}
231
+ case * plan.Project :
232
+ indexExpressions (n .Projections )
233
+ case * plan.GroupBy :
234
+ indexExpressions (n .Aggregate )
235
+ default :
236
+ getColumnsInNodes (n .Children (), columns )
283
237
}
284
-
285
- return result , nil
286
- })
238
+ }
287
239
}
288
240
289
- func isDefinedInChildProject (n sql.Node , col * expression. UnresolvedColumn ) bool {
290
- var x sql. Node
291
- for _ , child := range n .Children () {
292
- plan .Inspect (child , func (n sql.Node ) bool {
241
+ func getNodeAvailableTables (n sql.Node ) map [ string ] string {
242
+ var tables = make ( map [ string ] string )
243
+ for _ , c := range n .Children () {
244
+ plan .Inspect (c , func (n sql.Node ) bool {
293
245
switch n := n .(type ) {
294
- case * plan.SubqueryAlias :
246
+ case * plan.SubqueryAlias , * plan.ResolvedTable :
247
+ name := strings .ToLower (n .(sql.Nameable ).Name ())
248
+ tables [name ] = name
295
249
return false
296
- case * plan.Project , * plan.GroupBy :
297
- if x == nil {
298
- x = n
250
+ case * plan.TableAlias :
251
+ switch t := n .Child .(type ) {
252
+ case * plan.ResolvedTable , * plan.UnresolvedTable :
253
+ name := strings .ToLower (t .(sql.Nameable ).Name ())
254
+ alias := strings .ToLower (n .Name ())
255
+ tables [alias ] = name
299
256
}
300
- return false
301
- default :
302
- return true
303
257
}
304
- })
305
-
306
- if x != nil {
307
- break
308
- }
309
- }
310
258
311
- if x == nil {
312
- return false
313
- }
314
-
315
- var found bool
316
- for _ , expr := range x .(sql.Expressioner ).Expressions () {
317
- switch expr := expr .(type ) {
318
- case * expression.Alias :
319
- if strings .ToLower (expr .Name ()) == strings .ToLower (col .Name ()) {
320
- found = true
321
- }
322
- case column :
323
- if strings .ToLower (expr .Name ()) == strings .ToLower (col .Name ()) &&
324
- strings .ToLower (expr .Table ()) == strings .ToLower (col .Table ()) {
325
- found = true
326
- }
327
- }
328
-
329
- if found {
330
- break
331
- }
259
+ return true
260
+ })
332
261
}
333
-
334
- return found
262
+ return tables
335
263
}
336
264
337
265
var errGlobalVariablesNotSupported = errors .NewKind ("can't resolve global variable, %s was requested" )
@@ -659,6 +587,6 @@ func dedupStrings(in []string) []string {
659
587
return result
660
588
}
661
589
662
- func isGlobalOrSessionColumn (col * expression. UnresolvedColumn ) bool {
590
+ func isGlobalOrSessionColumn (col column ) bool {
663
591
return strings .HasPrefix (col .Name (), "@@" ) || strings .HasPrefix (col .Table (), "@@" )
664
592
}
0 commit comments