Skip to content

Commit 7528777

Browse files
committed
abandoning this for now
1 parent b9d3599 commit 7528777

File tree

3 files changed

+61
-34
lines changed

3 files changed

+61
-34
lines changed

sql/analyzer/validation_rules.go

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -249,41 +249,59 @@ func validateGroupBy(ctx *sql.Context, a *Analyzer, n sql.Node, scope *plan.Scop
249249
}
250250

251251
var err error
252-
//var parent sql.Node
252+
var parent sql.Node
253253
transform.Inspect(n, func(n sql.Node) bool {
254-
//defer func() {
255-
// parent = n
256-
//}()
254+
defer func() {
255+
parent = n
256+
}()
257257

258258
gb, ok := n.(*plan.GroupBy)
259259
if !ok {
260260
return true
261261
}
262262

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+
}
269269

270270
// Allow the parser use the GroupBy node to eval the aggregation functions
271271
// for sql statements that don't make use of the GROUP BY expression.
272272
if len(gb.GroupByExprs) == 0 {
273273
return true
274274
}
275275

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
277286
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
279299
}
280300

281301
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
287305
}
288306
}
289307
return true
@@ -292,22 +310,15 @@ func validateGroupBy(ctx *sql.Context, a *Analyzer, n sql.Node, scope *plan.Scop
292310
return n, transform.SameTree, err
293311
}
294312

295-
func expressionReferencesOnlyGroupBys(groupBys []string, expr sql.Expression) bool {
313+
func expressionReferencesOnlyGroupBys(groupBys, groupByAliases map[string]bool, expr sql.Expression) bool {
296314
valid := true
297315
sql.Inspect(expr, func(expr sql.Expression) bool {
316+
exprStr := strings.ToLower(expr.String())
298317
switch expr := expr.(type) {
299318
case nil, sql.Aggregation, *expression.Literal:
300319
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.
309320
default:
310-
if stringContains(groupBys, expr.String()) {
321+
if groupBys[exprStr] {
311322
return false
312323
}
313324

sql/plan/group_by.go

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,17 @@ var ErrGroupBy = errors.NewKind("group by aggregation '%v' not supported")
3030
// GroupBy groups the rows by some expressions.
3131
type GroupBy struct {
3232
UnaryNode
33+
// SelectedExprs are projection dependencies. They are not the explicit select expressions. For example, given the
34+
// query "SELECT pk div 2 from one_pk group by 1," SelectedExprs would contain a GetField for one_pk.pk even though
35+
// the explicit select expression is "pk div 2".
3336
SelectedExprs []sql.Expression
3437
GroupByExprs []sql.Expression
38+
// SelectedAliasMap maps a projection dependency to an alias if it was part of one. For example, given the query
39+
// "SELECT pk div 2, pk + 3 from one_pk group by 2", SelectedAliasMap would contain a mapping from the GetField for
40+
// one_pk.pk to the Alias for "pk div 2" and to the Alias for "pk + 3". This allows for projection dependencies to
41+
// be validated for GroupBy dependencies. Since SelectedAliasMap is only used for validating GroupBys dependencies,
42+
// the expressions are stored as strings. A nested map is used for faster access during validation.
43+
SelectedAliasMap map[string][]string
3544
}
3645

3746
var _ sql.Expressioner = (*GroupBy)(nil)
@@ -43,11 +52,12 @@ var _ sql.CollationCoercible = (*GroupBy)(nil)
4352
// will appear in the output of the query. Some of these fields may be aggregate functions, some may be columns or
4453
// other expressions. Unlike a project, the GroupBy also has a list of group-by expressions, which usually also appear
4554
// in the list of selected expressions.
46-
func NewGroupBy(selectedExprs, groupByExprs []sql.Expression, child sql.Node) *GroupBy {
55+
func NewGroupBy(selectedExprs, groupByExprs []sql.Expression, selectedAliasMap map[string][]string, child sql.Node) *GroupBy {
4756
return &GroupBy{
48-
UnaryNode: UnaryNode{Child: child},
49-
SelectedExprs: selectedExprs,
50-
GroupByExprs: groupByExprs,
57+
UnaryNode: UnaryNode{Child: child},
58+
SelectedExprs: selectedExprs,
59+
GroupByExprs: groupByExprs,
60+
SelectedAliasMap: selectedAliasMap,
5161
}
5262
}
5363

@@ -101,7 +111,7 @@ func (g *GroupBy) WithChildren(children ...sql.Node) (sql.Node, error) {
101111
return nil, sql.ErrInvalidChildrenNumber.New(g, len(children), 1)
102112
}
103113

104-
return NewGroupBy(g.SelectedExprs, g.GroupByExprs, children[0]), nil
114+
return NewGroupBy(g.SelectedExprs, g.GroupByExprs, g.SelectedAliasMap, children[0]), nil
105115
}
106116

107117
// CollationCoercibility implements the interface sql.CollationCoercible.
@@ -122,7 +132,7 @@ func (g *GroupBy) WithExpressions(exprs ...sql.Expression) (sql.Node, error) {
122132
grouping := make([]sql.Expression, len(g.GroupByExprs))
123133
copy(grouping, exprs[len(g.SelectedExprs):])
124134

125-
return NewGroupBy(agg, grouping, g.Child), nil
135+
return NewGroupBy(agg, grouping, g.SelectedAliasMap, g.Child), nil
126136
}
127137

128138
func (g *GroupBy) String() string {

sql/planbuilder/aggregates.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ func (b *Builder) buildAggregation(fromScope, projScope *scope, groupingCols []s
203203
var selectExprs []sql.Expression
204204
var selectGfs []sql.Expression
205205
selectStr := make(map[string]bool)
206+
aliasMap := make(map[string][]string)
206207
for _, e := range group.aggregations() {
207208
if !selectStr[strings.ToLower(e.String())] {
208209
selectExprs = append(selectExprs, e.scalar)
@@ -212,12 +213,14 @@ func (b *Builder) buildAggregation(fromScope, projScope *scope, groupingCols []s
212213
}
213214
var aliases []sql.Expression
214215
for _, col := range projScope.cols {
216+
var isAlias bool
215217
// eval aliases in project scope
216218
switch e := col.scalar.(type) {
217219
case *expression.Alias:
218220
if !e.Unreferencable() {
219221
aliases = append(aliases, e.WithId(sql.ColumnId(col.id)).(*expression.Alias))
220222
}
223+
isAlias = true
221224
default:
222225
}
223226

@@ -231,6 +234,9 @@ func (b *Builder) buildAggregation(fromScope, projScope *scope, groupingCols []s
231234
selectGfs = append(selectGfs, e)
232235
selectStr[colName] = true
233236
}
237+
if isAlias {
238+
aliasMap[colName] = append(aliasMap[colName], strings.ToLower(col.scalar.String()))
239+
}
234240
default:
235241
}
236242
return false
@@ -245,7 +251,7 @@ func (b *Builder) buildAggregation(fromScope, projScope *scope, groupingCols []s
245251
selectStr[e.String()] = true
246252
}
247253
}
248-
gb := plan.NewGroupBy(selectExprs, groupingCols, fromScope.node)
254+
gb := plan.NewGroupBy(selectExprs, groupingCols, aliasMap, fromScope.node)
249255
outScope.node = gb
250256

251257
if len(aliases) > 0 {

0 commit comments

Comments
 (0)