@@ -249,41 +249,59 @@ func validateGroupBy(ctx *sql.Context, a *Analyzer, n sql.Node, scope *plan.Scop
249
249
}
250
250
251
251
var err error
252
- // var parent sql.Node
252
+ var parent sql.Node
253
253
transform .Inspect (n , func (n sql.Node ) bool {
254
- // defer func() {
255
- // parent = n
256
- // }()
254
+ defer func () {
255
+ parent = n
256
+ }()
257
257
258
258
gb , ok := n .(* plan.GroupBy )
259
259
if ! ok {
260
260
return true
261
261
}
262
262
263
- // switch parent.(type) {
264
- // case *plan.Having, *plan.Project, *plan.Sort:
265
- // // TODO: these shouldn't be skipped; you can group by primary key without problem b/c only one value
266
- // // https://dev.mysql.com/doc/refman/8.0/en/group-by-handling.html#:~:text=The%20query%20is%20valid%20if%20name%20is%20a%20primary%20key
267
- // return true
268
- // }
263
+ switch parent .(type ) {
264
+ case * plan.Having , * plan.Project , * plan.Sort :
265
+ // TODO: these shouldn't be skipped; you can group by primary key without problem b/c only one value
266
+ // https://dev.mysql.com/doc/refman/8.0/en/group-by-handling.html#:~:text=The%20query%20is%20valid%20if%20name%20is%20a%20primary%20key
267
+ return true
268
+ }
269
269
270
270
// Allow the parser use the GroupBy node to eval the aggregation functions
271
271
// for sql statements that don't make use of the GROUP BY expression.
272
272
if len (gb .GroupByExprs ) == 0 {
273
273
return true
274
274
}
275
275
276
- var groupBys []string
276
+ primaryKeys := make (map [string ]bool )
277
+ for _ , col := range gb .Child .Schema () {
278
+ if col .PrimaryKey {
279
+ primaryKeys [strings .ToLower (col .Name )] = true
280
+ }
281
+ }
282
+
283
+ groupBys := make (map [string ]bool )
284
+ groupByAliases := make (map [string ]bool )
285
+ groupByPrimaryKeys := 0
277
286
for _ , expr := range gb .GroupByExprs {
278
- groupBys = append (groupBys , expr .String ())
287
+ exprStr := strings .ToLower (expr .String ())
288
+ groupBys [exprStr ] = true
289
+ if primaryKeys [exprStr ] {
290
+ groupByPrimaryKeys ++
291
+ }
292
+ if _ , ok := expr .(sql.Aggregation ); ok {
293
+ groupByAliases [exprStr ] = true
294
+ }
295
+ }
296
+
297
+ if len (primaryKeys ) != 0 && groupByPrimaryKeys == len (primaryKeys ) {
298
+ return true
279
299
}
280
300
281
301
for _ , expr := range gb .SelectedExprs {
282
- if _ , ok := expr .(sql.Aggregation ); ! ok {
283
- if ! expressionReferencesOnlyGroupBys (groupBys , expr ) {
284
- err = analyzererrors .ErrValidationGroupBy .New (expr .String ())
285
- return false
286
- }
302
+ if ! expressionReferencesOnlyGroupBys (groupBys , groupByAliases , expr ) {
303
+ err = analyzererrors .ErrValidationGroupBy .New (expr .String ())
304
+ return false
287
305
}
288
306
}
289
307
return true
@@ -292,22 +310,15 @@ func validateGroupBy(ctx *sql.Context, a *Analyzer, n sql.Node, scope *plan.Scop
292
310
return n , transform .SameTree , err
293
311
}
294
312
295
- func expressionReferencesOnlyGroupBys (groupBys [] string , expr sql.Expression ) bool {
313
+ func expressionReferencesOnlyGroupBys (groupBys , groupByAliases map [ string ] bool , expr sql.Expression ) bool {
296
314
valid := true
297
315
sql .Inspect (expr , func (expr sql.Expression ) bool {
316
+ exprStr := strings .ToLower (expr .String ())
298
317
switch expr := expr .(type ) {
299
318
case nil , sql.Aggregation , * expression.Literal :
300
319
return false
301
- case * expression.Alias , sql.FunctionExpression :
302
- if stringContains (groupBys , expr .String ()) {
303
- return false
304
- }
305
- return true
306
- // cc: https://dev.mysql.com/doc/refman/8.0/en/group-by-handling.html
307
- // Each part of the SelectExpr must refer to the aggregated columns in some way
308
- // TODO: this isn't complete, it's overly restrictive. Dependant columns are fine to reference.
309
320
default :
310
- if stringContains ( groupBys , expr . String ()) {
321
+ if groupBys [ exprStr ] {
311
322
return false
312
323
}
313
324
0 commit comments