From 680e6d96bc449bbcf395c5ec8c229f1d6d491f6c Mon Sep 17 00:00:00 2001 From: John Shahid Date: Wed, 6 Aug 2014 15:37:29 -0400 Subject: [PATCH] Always fill empty groups if the start time is specified. Prior to this change, empty group filling worked between t1 and t2, where t1 is the timestamp of the earliest point and t2 is the timestamp of the last point. This patch change the behavior of the fill() to use the query start and end time as t1 and t2, respectively. This only happens if the user specified the start time of the query. Otherwise, there's a potential of filling millions of millions of groups, since the default start time of the query is really really early. --- engine/engine.go | 17 +++++++++++++++++ integration/data_test.go | 27 +++++++++++++++++++++++++++ parser/parser.go | 6 ++++-- parser/parser_test.go | 7 +++++++ parser/query_api.go | 4 ++++ 5 files changed, 59 insertions(+), 2 deletions(-) diff --git a/engine/engine.go b/engine/engine.go index 0d05fe62903..281142c1eac 100644 --- a/engine/engine.go +++ b/engine/engine.go @@ -32,6 +32,11 @@ type QueryEngine struct { where *parser.WhereCondition fillWithZero bool + // was start time set in the query, e.g. time > now() - 1d + startTimeSpecified bool + startTime int64 + endTime int64 + // output fields responseChan chan *protocol.Response limiter *Limiter @@ -356,6 +361,15 @@ func (self *QueryEngine) executeCountQueryWithGroupBy(query *parser.SelectQuery, self.fillWithZero = query.GetGroupByClause().FillWithZero + // This is a special case for issue #426. If the start time is + // specified and there's a group by clause and fill with zero, then + // we need to fill the entire range from start time to end time + if query.IsStartTimeSpecified() && self.duration != nil && self.fillWithZero { + self.startTimeSpecified = true + self.startTime = query.GetStartTime().Truncate(*self.duration).UnixNano() / 1000 + self.endTime = query.GetEndTime().Truncate(*self.duration).UnixNano() / 1000 + } + self.initializeFields() err = self.distributeQuery(query, func(series *protocol.Series) error { @@ -520,6 +534,9 @@ func (self *QueryEngine) runAggregatesForTable(table string) { var err error if self.duration != nil && self.fillWithZero { timestampRange := state.pointsRange + if self.startTimeSpecified { + timestampRange = &PointRange{startTime: self.startTime, endTime: self.endTime} + } // TODO: DRY this if self.query.Ascending { diff --git a/integration/data_test.go b/integration/data_test.go index 58e08544120..bda0355ad09 100644 --- a/integration/data_test.go +++ b/integration/data_test.go @@ -204,6 +204,33 @@ func (self *DataTestSuite) DifferenceValues(c *C) (Fun, Fun) { } } +// issue #426 +func (self *DataTestSuite) FilllingEntireRange(c *C) (Fun, Fun) { + return func(client Client) { + data := ` +[ + { + "name": "test_filling_range", + "columns": ["value"], + "points": [ + [1] + ] + } + ]` + client.WriteJsonData(data, c, influxdb.Millisecond) + }, func(client Client) { + serieses := client.RunQuery("select sum(value) from test_filling_range where time > now() - 1d group by time(1h) fill(0)", c, "m") + c.Assert(serieses, HasLen, 1) + maps := ToMap(serieses[0]) + fmt.Printf("lenght: %d\n", len(maps)) + c.Assert(maps, HasLen, 25) + c.Assert(maps[0]["sum"], Equals, 1.0) + for i := 1; i < len(maps); i++ { + c.Assert(maps[i]["sum"], Equals, 0.0) + } + } +} + func (self *DataTestSuite) ModeWithInt(c *C) (Fun, Fun) { return func(client Client) { data := ` diff --git a/parser/parser.go b/parser/parser.go index 657c7a807d2..39e8cc51712 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -29,8 +29,9 @@ type IntoClause struct { } type BasicQuery struct { - startTime time.Time - endTime time.Time + startTime time.Time + endTime time.Time + startTimeSpecified bool } type SelectDeleteCommonQuery struct { @@ -681,6 +682,7 @@ func parseSelectDeleteCommonQuery(fromClause *C.from_clause, whereCondition *C.c if startTime != nil { goQuery.startTime = *startTime + goQuery.startTimeSpecified = true } return goQuery, nil diff --git a/parser/parser_test.go b/parser/parser_test.go index d823a801b07..dc7dc3439e7 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -101,6 +101,13 @@ func (self *QueryParserSuite) TestGetQueryString(c *C) { c.Assert(actualQuery, HasLen, 1) expectedQuery[0].QueryString = "" actualQuery[0].QueryString = "" + if expectedQuery[0].DeleteQuery != nil { + expectedQuery[0].DeleteQuery.startTimeSpecified = false + actualQuery[0].DeleteQuery.startTimeSpecified = false + } else if expectedQuery[0].SelectQuery != nil { + expectedQuery[0].SelectQuery.startTimeSpecified = false + actualQuery[0].SelectQuery.startTimeSpecified = false + } c.Assert(actualQuery[0], DeepEquals, expectedQuery[0]) } diff --git a/parser/query_api.go b/parser/query_api.go index e0b5e4ab6c0..d7c434047f0 100644 --- a/parser/query_api.go +++ b/parser/query_api.go @@ -200,6 +200,10 @@ func (self *BasicQuery) GetStartTime() time.Time { return self.startTime } +func (self *BasicQuery) IsStartTimeSpecified() bool { + return self.startTimeSpecified +} + // Returns the start time of the query. Queries can only have // one condition of the form time > start_time func (self *BasicQuery) GetEndTime() time.Time {