diff --git a/CHANGELOG.md b/CHANGELOG.md index d4b84f62b58..2395a665754 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ - [#3580](https://github.com/influxdb/influxdb/issues/3580): Do not allow wildcards with fields in select statements - [#3530](https://github.com/influxdb/influxdb/pull/3530): Aliasing a column no longer works - [#3436](https://github.com/influxdb/influxdb/issues/3436): Fix panic in hinted handoff queue processor +- [#3401](https://github.com/influxdb/influxdb/issues/3401): Derivative on non-numeric fields panics db ## v0.9.2 [2015-07-24] diff --git a/tsdb/executor.go b/tsdb/executor.go index f7b9d7bf8ed..bc1b84a9f78 100644 --- a/tsdb/executor.go +++ b/tsdb/executor.go @@ -757,14 +757,31 @@ type RawQueryDerivativeProcessor struct { DerivativeInterval time.Duration } +func (rqdp *RawQueryDerivativeProcessor) canProcess(input []*MapperValue) bool { + // If we only have 1 value, then the value did not change, so return + // a single row with 0.0 + if len(input) == 1 { + return false + } + + // See if the field value is numeric, if it's not, we can't process the derivative + validType := false + switch input[0].Value.(type) { + case int64: + validType = true + case float64: + validType = true + } + + return validType +} + func (rqdp *RawQueryDerivativeProcessor) Process(input []*MapperValue) []*MapperValue { if len(input) == 0 { return input } - // If we only have 1 value, then the value did not change, so return - // a single row with 0.0 - if len(input) == 1 { + if !rqdp.canProcess(input) { return []*MapperValue{ &MapperValue{ Time: input[0].Time, @@ -858,6 +875,22 @@ func ProcessAggregateDerivative(results [][]interface{}, isNonNegative bool, int } } + // Check the value's type to ensure it's an numeric, if not, return a 0 result. We only check the first value + // because derivatives cannot be combined with other aggregates currently. + validType := false + switch results[0][1].(type) { + case int64: + validType = true + case float64: + validType = true + } + + if !validType { + return [][]interface{}{ + []interface{}{results[0][0], 0.0}, + } + } + // Otherwise calculate the derivatives as the difference between consecutive // points divided by the elapsed time. Then normalize to the requested // interval. diff --git a/tsdb/executor_test.go b/tsdb/executor_test.go index d1d01faffed..3ada0cf6478 100644 --- a/tsdb/executor_test.go +++ b/tsdb/executor_test.go @@ -579,7 +579,7 @@ func TestProcessAggregateDerivative(t *testing.T) { }, }, { - name: "float derivatives", + name: "integer derivatives", fn: "derivative", interval: 24 * time.Hour, in: [][]interface{}{ @@ -608,6 +608,30 @@ func TestProcessAggregateDerivative(t *testing.T) { }, }, }, + { + name: "string derivatives", + fn: "derivative", + interval: 24 * time.Hour, + in: [][]interface{}{ + []interface{}{ + time.Unix(0, 0), "1.0", + }, + []interface{}{ + time.Unix(0, 0).Add(24 * time.Hour), "2.0", + }, + []interface{}{ + time.Unix(0, 0).Add(48 * time.Hour), "3.0", + }, + []interface{}{ + time.Unix(0, 0).Add(72 * time.Hour), "4.0", + }, + }, + exp: [][]interface{}{ + []interface{}{ + time.Unix(0, 0), 0.0, + }, + }, + }, } for _, test := range tests { @@ -697,6 +721,43 @@ func TestProcessRawQueryDerivative(t *testing.T) { }, }, }, + { + name: "integer derivative", + fn: "derivative", + interval: 24 * time.Hour, + in: []*tsdb.MapperValue{ + { + Time: time.Unix(0, 0).Unix(), + Value: int64(0), + }, + { + Time: time.Unix(0, 0).Add(24 * time.Hour).UnixNano(), + Value: int64(3), + }, + { + Time: time.Unix(0, 0).Add(48 * time.Hour).UnixNano(), + Value: int64(5), + }, + { + Time: time.Unix(0, 0).Add(72 * time.Hour).UnixNano(), + Value: int64(9), + }, + }, + exp: []*tsdb.MapperValue{ + { + Time: time.Unix(0, 0).Add(24 * time.Hour).UnixNano(), + Value: 3.0, + }, + { + Time: time.Unix(0, 0).Add(48 * time.Hour).UnixNano(), + Value: 2.0, + }, + { + Time: time.Unix(0, 0).Add(72 * time.Hour).UnixNano(), + Value: 4.0, + }, + }, + }, { name: "12h interval", fn: "derivative", @@ -806,6 +867,35 @@ func TestProcessRawQueryDerivative(t *testing.T) { }, }, }, + { + name: "string derivatives", + fn: "derivative", + interval: 24 * time.Hour, + in: []*tsdb.MapperValue{ + { + Time: time.Unix(0, 0).Unix(), + Value: "1.0", + }, + { + Time: time.Unix(0, 0).Add(24 * time.Hour).UnixNano(), + Value: "2.0", + }, + { + Time: time.Unix(0, 0).Add(48 * time.Hour).UnixNano(), + Value: "3.0", + }, + { + Time: time.Unix(0, 0).Add(72 * time.Hour).UnixNano(), + Value: "4.0", + }, + }, + exp: []*tsdb.MapperValue{ + { + Time: time.Unix(0, 0).Unix(), + Value: 0.0, + }, + }, + }, } for _, test := range tests {