func (v *extractAggregatesVisitor) Visit(expr parser.Expr, pre bool) (parser.Visitor, parser.Expr) { if !pre || v.err != nil { return nil, expr } // This expression is in the GROUP BY - switch to the copy that will accept // qvalues for this and any subtrees. if _, ok := v.groupStrs[expr.String()]; ok && v.groupedCopy != nil { v = v.groupedCopy } switch t := expr.(type) { case *parser.FuncExpr: if len(t.Name.Indirect) > 0 { break } if impl, ok := aggregates[strings.ToLower(string(t.Name.Base))]; ok { if len(t.Exprs) != 1 { // Type checking has already run on these expressions thus // if an aggregate function of the wrong arity gets here, // something has gone really wrong. panic(fmt.Sprintf("%s has %d arguments (expected 1)", t.Name.Base, len(t.Exprs))) } f := &aggregateFunc{ expr: t, arg: t.Exprs[0], create: impl, group: v.n, buckets: make(map[string]aggregateImpl), } if t.Type == parser.Distinct { f.seen = make(map[string]struct{}) } v.n.funcs = append(v.n.funcs, f) return nil, f } case *qvalue: if v.groupedCopy != nil { v.err = fmt.Errorf("column \"%s\" must appear in the GROUP BY clause or be used in an aggregate function", t.colRef.get().Name) return v, expr } f := &aggregateFunc{ expr: t, arg: t, create: newIdentAggregate, group: v.n, buckets: make(map[string]aggregateImpl), } v.n.funcs = append(v.n.funcs, f) return nil, f } return v, expr }
func (v *checkAggregateVisitor) Visit(expr parser.Expr, pre bool) (parser.Visitor, parser.Expr) { if !pre || v.aggrErr != nil { return nil, expr } switch t := expr.(type) { case *parser.FuncExpr: if _, ok := aggregates[strings.ToLower(string(t.Name.Base))]; ok { return nil, expr } case *qvalue: if _, ok := v.groupStrs[t.String()]; ok { return nil, expr } v.aggrErr = fmt.Errorf("column \"%s\" must appear in the GROUP BY clause or be used in an aggregate function", t.col.Name) return v, expr } if _, ok := v.groupStrs[expr.String()]; ok { return nil, expr } return v, expr }