From 0b248e225c332e434dc36c3179a187238ebb3082 Mon Sep 17 00:00:00 2001 From: David Norton Date: Wed, 29 Jul 2015 15:33:03 -0400 Subject: [PATCH] Fix aggregate queries and time precision on where clauses. --- cmd/influxd/run/server_test.go | 7 +++++ influxql/ast.go | 8 ++--- influxql/ast_test.go | 56 +++++++++++++++++----------------- tsdb/mapper.go | 2 +- tsdb/mapper_test.go | 4 +-- 5 files changed, 42 insertions(+), 35 deletions(-) diff --git a/cmd/influxd/run/server_test.go b/cmd/influxd/run/server_test.go index a6cba965c5..ea862553fc 100644 --- a/cmd/influxd/run/server_test.go +++ b/cmd/influxd/run/server_test.go @@ -710,12 +710,19 @@ func TestServer_Query_Count(t *testing.T) { test := NewTest("db0", "rp0") test.write = `cpu,host=server01 value=1.0 ` + strconv.FormatInt(now.UnixNano(), 10) + hour_ago := now.Add(-time.Hour).UTC() + test.addQueries([]*Query{ &Query{ name: "selecting count(value) should succeed", command: `SELECT count(value) FROM db0.rp0.cpu`, exp: `{"results":[{"series":[{"name":"cpu","columns":["time","count"],"values":[["1970-01-01T00:00:00Z",1]]}]}]}`, }, + &Query{ + name: "selecting count(value) with where time should return result", + command: fmt.Sprintf(`SELECT count(value) FROM db0.rp0.cpu WHERE time >= '%s'`, hour_ago.Format(time.RFC3339Nano)), + exp: fmt.Sprintf(`{"results":[{"series":[{"name":"cpu","columns":["time","count"],"values":[["%s",1]]}]}]}`, hour_ago.Format(time.RFC3339Nano)), + }, &Query{ name: "selecting count(*) should error", command: `SELECT count(*) FROM db0.rp0.cpu`, diff --git a/influxql/ast.go b/influxql/ast.go index 2f44399726..0942b77929 100644 --- a/influxql/ast.go +++ b/influxql/ast.go @@ -2225,7 +2225,7 @@ type TimeLiteral struct { // String returns a string representation of the literal. func (l *TimeLiteral) String() string { - return `'` + l.Val.UTC().Format(DateTimeFormat) + `'` + return `'` + l.Val.UTC().Format(time.RFC3339Nano) + `'` } // DurationLiteral represents a duration literal. @@ -2358,11 +2358,11 @@ func TimeRange(expr Expr) (min, max time.Time) { } // Update the min/max depending on the operator. - // The GT & LT update the value by +/- 1µs not make them "not equal". + // The GT & LT update the value by +/- 1ns not make them "not equal". switch op { case GT: if min.IsZero() || value.After(min) { - min = value.Add(time.Microsecond) + min = value.Add(time.Nanosecond) } case GTE: if min.IsZero() || value.After(min) { @@ -2370,7 +2370,7 @@ func TimeRange(expr Expr) (min, max time.Time) { } case LT: if max.IsZero() || value.Before(max) { - max = value.Add(-time.Microsecond) + max = value.Add(-time.Nanosecond) } case LTE: if max.IsZero() || value.Before(max) { diff --git a/influxql/ast_test.go b/influxql/ast_test.go index af1a90a0d8..3d074d5f1d 100644 --- a/influxql/ast_test.go +++ b/influxql/ast_test.go @@ -132,8 +132,8 @@ func TestSelectStatement_SetTimeRange(t *testing.T) { if min != start { t.Fatalf("start time wasn't set properly.\n exp: %s\n got: %s", start, min) } - // the end range is actually one microsecond before the given one since end is exclusive - end = end.Add(-time.Microsecond) + // the end range is actually one nanosecond before the given one since end is exclusive + end = end.Add(-time.Nanosecond) if max != end { t.Fatalf("end time wasn't set properly.\n exp: %s\n got: %s", end, max) } @@ -165,8 +165,8 @@ func TestSelectStatement_SetTimeRange(t *testing.T) { if min != start { t.Fatalf("start time wasn't set properly.\n exp: %s\n got: %s", start, min) } - // the end range is actually one microsecond before the given one since end is exclusive - end = end.Add(-time.Microsecond) + // the end range is actually one nanosecond before the given one since end is exclusive + end = end.Add(-time.Nanosecond) if max != end { t.Fatalf("end time wasn't set properly.\n exp: %s\n got: %s", end, max) } @@ -189,8 +189,8 @@ func TestSelectStatement_SetTimeRange(t *testing.T) { if min != start { t.Fatalf("start time wasn't set properly.\n exp: %s\n got: %s", start, min) } - // the end range is actually one microsecond before the given one since end is exclusive - end = end.Add(-time.Microsecond) + // the end range is actually one nanosecond before the given one since end is exclusive + end = end.Add(-time.Nanosecond) if max != end { t.Fatalf("end time wasn't set properly.\n exp: %s\n got: %s", end, max) } @@ -472,45 +472,45 @@ func TestTimeRange(t *testing.T) { min, max string }{ // LHS VarRef - {expr: `time > '2000-01-01 00:00:00'`, min: `2000-01-01 00:00:00.000001`, max: `0001-01-01 00:00:00`}, - {expr: `time >= '2000-01-01 00:00:00'`, min: `2000-01-01 00:00:00`, max: `0001-01-01 00:00:00`}, - {expr: `time < '2000-01-01 00:00:00'`, min: `0001-01-01 00:00:00`, max: `1999-12-31 23:59:59.999999`}, - {expr: `time <= '2000-01-01 00:00:00'`, min: `0001-01-01 00:00:00`, max: `2000-01-01 00:00:00`}, + {expr: `time > '2000-01-01 00:00:00'`, min: `2000-01-01T00:00:00.000000001Z`, max: `0001-01-01T00:00:00Z`}, + {expr: `time >= '2000-01-01 00:00:00'`, min: `2000-01-01T00:00:00Z`, max: `0001-01-01T00:00:00Z`}, + {expr: `time < '2000-01-01 00:00:00'`, min: `0001-01-01T00:00:00Z`, max: `1999-12-31T23:59:59.999999999Z`}, + {expr: `time <= '2000-01-01 00:00:00'`, min: `0001-01-01T00:00:00Z`, max: `2000-01-01T00:00:00Z`}, // RHS VarRef - {expr: `'2000-01-01 00:00:00' > time`, min: `0001-01-01 00:00:00`, max: `1999-12-31 23:59:59.999999`}, - {expr: `'2000-01-01 00:00:00' >= time`, min: `0001-01-01 00:00:00`, max: `2000-01-01 00:00:00`}, - {expr: `'2000-01-01 00:00:00' < time`, min: `2000-01-01 00:00:00.000001`, max: `0001-01-01 00:00:00`}, - {expr: `'2000-01-01 00:00:00' <= time`, min: `2000-01-01 00:00:00`, max: `0001-01-01 00:00:00`}, + {expr: `'2000-01-01 00:00:00' > time`, min: `0001-01-01T00:00:00Z`, max: `1999-12-31T23:59:59.999999999Z`}, + {expr: `'2000-01-01 00:00:00' >= time`, min: `0001-01-01T00:00:00Z`, max: `2000-01-01T00:00:00Z`}, + {expr: `'2000-01-01 00:00:00' < time`, min: `2000-01-01T00:00:00.000000001Z`, max: `0001-01-01T00:00:00Z`}, + {expr: `'2000-01-01 00:00:00' <= time`, min: `2000-01-01T00:00:00Z`, max: `0001-01-01T00:00:00Z`}, // Equality - {expr: `time = '2000-01-01 00:00:00'`, min: `2000-01-01 00:00:00`, max: `2000-01-01 00:00:00`}, + {expr: `time = '2000-01-01 00:00:00'`, min: `2000-01-01T00:00:00Z`, max: `2000-01-01T00:00:00Z`}, // Multiple time expressions. - {expr: `time >= '2000-01-01 00:00:00' AND time < '2000-01-02 00:00:00'`, min: `2000-01-01 00:00:00`, max: `2000-01-01 23:59:59.999999`}, + {expr: `time >= '2000-01-01 00:00:00' AND time < '2000-01-02 00:00:00'`, min: `2000-01-01T00:00:00Z`, max: `2000-01-01T23:59:59.999999999Z`}, // Min/max crossover - {expr: `time >= '2000-01-01 00:00:00' AND time <= '1999-01-01 00:00:00'`, min: `2000-01-01 00:00:00`, max: `1999-01-01 00:00:00`}, + {expr: `time >= '2000-01-01 00:00:00' AND time <= '1999-01-01 00:00:00'`, min: `2000-01-01T00:00:00Z`, max: `1999-01-01T00:00:00Z`}, // Absolute time - {expr: `time = 1388534400s`, min: `2014-01-01 00:00:00`, max: `2014-01-01 00:00:00`}, + {expr: `time = 1388534400s`, min: `2014-01-01T00:00:00Z`, max: `2014-01-01T00:00:00Z`}, // Non-comparative expressions. - {expr: `time`, min: `0001-01-01 00:00:00`, max: `0001-01-01 00:00:00`}, - {expr: `time + 2`, min: `0001-01-01 00:00:00`, max: `0001-01-01 00:00:00`}, - {expr: `time - '2000-01-01 00:00:00'`, min: `0001-01-01 00:00:00`, max: `0001-01-01 00:00:00`}, - {expr: `time AND '2000-01-01 00:00:00'`, min: `0001-01-01 00:00:00`, max: `0001-01-01 00:00:00`}, + {expr: `time`, min: `0001-01-01T00:00:00Z`, max: `0001-01-01T00:00:00Z`}, + {expr: `time + 2`, min: `0001-01-01T00:00:00Z`, max: `0001-01-01T00:00:00Z`}, + {expr: `time - '2000-01-01 00:00:00'`, min: `0001-01-01T00:00:00Z`, max: `0001-01-01T00:00:00Z`}, + {expr: `time AND '2000-01-01 00:00:00'`, min: `0001-01-01T00:00:00Z`, max: `0001-01-01T00:00:00Z`}, } { // Extract time range. expr := MustParseExpr(tt.expr) min, max := influxql.TimeRange(expr) // Compare with expected min/max. - if min := min.Format(influxql.DateTimeFormat); tt.min != min { + if min := min.Format(time.RFC3339Nano); tt.min != min { t.Errorf("%d. %s: unexpected min:\n\nexp=%s\n\ngot=%s\n\n", i, tt.expr, tt.min, min) continue } - if max := max.Format(influxql.DateTimeFormat); tt.max != max { + if max := max.Format(time.RFC3339Nano); tt.max != max { t.Errorf("%d. %s: unexpected max:\n\nexp=%s\n\ngot=%s\n\n", i, tt.expr, tt.max, max) continue } @@ -694,9 +694,9 @@ func TestReduce(t *testing.T) { {in: `true + false`, out: `true + false`}, // Time literals. - {in: `now() + 2h`, out: `'2000-01-01 02:00:00'`, data: map[string]interface{}{"now()": now}}, - {in: `now() / 2h`, out: `'2000-01-01 00:00:00' / 2h`, data: map[string]interface{}{"now()": now}}, - {in: `4µ + now()`, out: `'2000-01-01 00:00:00.000004'`, data: map[string]interface{}{"now()": now}}, + {in: `now() + 2h`, out: `'2000-01-01T02:00:00Z'`, data: map[string]interface{}{"now()": now}}, + {in: `now() / 2h`, out: `'2000-01-01T00:00:00Z' / 2h`, data: map[string]interface{}{"now()": now}}, + {in: `4µ + now()`, out: `'2000-01-01T00:00:00.000004Z'`, data: map[string]interface{}{"now()": now}}, {in: `now() = now()`, out: `true`, data: map[string]interface{}{"now()": now}}, {in: `now() <> now()`, out: `false`, data: map[string]interface{}{"now()": now}}, {in: `now() < now() + 1h`, out: `true`, data: map[string]interface{}{"now()": now}}, @@ -704,7 +704,7 @@ func TestReduce(t *testing.T) { {in: `now() >= now() - 1h`, out: `true`, data: map[string]interface{}{"now()": now}}, {in: `now() > now() - 1h`, out: `true`, data: map[string]interface{}{"now()": now}}, {in: `now() - (now() - 60s)`, out: `1m`, data: map[string]interface{}{"now()": now}}, - {in: `now() AND now()`, out: `'2000-01-01 00:00:00' AND '2000-01-01 00:00:00'`, data: map[string]interface{}{"now()": now}}, + {in: `now() AND now()`, out: `'2000-01-01T00:00:00Z' AND '2000-01-01T00:00:00Z'`, data: map[string]interface{}{"now()": now}}, {in: `now()`, out: `now()`}, // Duration literals. diff --git a/tsdb/mapper.go b/tsdb/mapper.go index 012b842f68..cdaaf8cdc8 100644 --- a/tsdb/mapper.go +++ b/tsdb/mapper.go @@ -141,7 +141,7 @@ func (lm *LocalMapper) Open() error { // Ensure that the start time for the results is on the start of the window. lm.queryTMinWindow = lm.queryTMin - if lm.intervalSize > 0 { + if lm.intervalSize > 0 && lm.numIntervals > 1 { lm.queryTMinWindow = lm.queryTMinWindow / lm.intervalSize * lm.intervalSize } } diff --git a/tsdb/mapper_test.go b/tsdb/mapper_test.go index 9b68d25e8a..2bc1b86a0b 100644 --- a/tsdb/mapper_test.go +++ b/tsdb/mapper_test.go @@ -372,13 +372,13 @@ func TestShardMapper_WriteAndSingleMapperAggregateQuery(t *testing.T) { { stmt: fmt.Sprintf(`SELECT sum(value) FROM cpu WHERE time > '%s'`, pt1time.Format(influxql.DateTimeFormat)), expected: []string{ - `{"name":"cpu","values":[{"value":[60]}]}`, + `{"name":"cpu","values":[{"time":10000000001,"value":[60]}]}`, `null`}, }, { stmt: fmt.Sprintf(`SELECT sum(value) FROM cpu WHERE time > '%s'`, pt2time.Format(influxql.DateTimeFormat)), expected: []string{ - `{"name":"cpu","values":[{"value":[null]}]}`, + `{"name":"cpu","values":[{"time":20000000001,"value":[null]}]}`, `null`}, }, }