diff --git a/influxql/functions.go b/influxql/functions.go index b7b4ecc516..7bf4396fe1 100644 --- a/influxql/functions.go +++ b/influxql/functions.go @@ -749,6 +749,16 @@ func ReducePercentile(percentile float64) ReduceFunc { } } +// IsNumeric returns whether a given aggregate can only be run on numeric fields. +func IsNumeric(c *Call) bool { + switch c.Name { + case "count", "first", "last": + return false + default: + return true + } +} + // MapRawQuery is for queries without aggregates func MapRawQuery(itr Iterator) interface{} { var values []*rawQueryMapOutput diff --git a/tx.go b/tx.go index c8b1cb5f1a..2b52f6dec1 100644 --- a/tx.go +++ b/tx.go @@ -93,6 +93,21 @@ func (tx *tx) CreateMapReduceJobs(stmt *influxql.SelectStatement, tagKeys []stri } } + // If a numerical aggregate is requested, ensure it is only performed on numeric data. + for _, a := range stmt.FunctionCalls() { + lit, ok := a.Args[0].(*influxql.VarRef) + if !ok { + return nil, fmt.Errorf("aggregate call didn't contain a field %s", a.String()) + } + if influxql.IsNumeric(a) { + f := m.FieldByName(lit.Val) + if f.Type != influxql.Float && f.Type != influxql.Integer { + return nil, fmt.Errorf("aggregate '%s' requires numerical field values. Field '%s' is of type %s", + a.Name, f.Name, f.Type) + } + } + } + // Grab time range from statement. tmin, tmax := influxql.TimeRange(stmt.Condition) if tmax.IsZero() {