fix #80. Support using durations to specify start and end time
parent
9018b327be
commit
95fb546595
|
@ -127,9 +127,11 @@
|
|||
- [Issue #57](https://github.com/influxdb/influxdb/issues/57). Don't panic when type of time != float
|
||||
- [Issue #63](https://github.com/influxdb/influxdb/issues/63). Aggregate queries should not have a sequence_number column
|
||||
|
||||
## v0.3.1 (unreleased)
|
||||
## v0.4.0 (unreleased)
|
||||
|
||||
## Features
|
||||
|
||||
- [Issue #80](https://github.com/influxdb/influxdb/issues/80). Support durations when specifying start and end time
|
||||
|
||||
## Bugfixes
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package parser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
. "launchpad.net/gocheck"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -417,6 +418,19 @@ func (self *QueryParserSuite) TestParseSelectWithComplexArithmeticOperations(c *
|
|||
c.Assert(value.Name, Equals, "3")
|
||||
}
|
||||
|
||||
func (self *QueryParserSuite) TestTimeConditionWithFloats(c *C) {
|
||||
var startTime int64 = 13853965
|
||||
for _, query := range []string{
|
||||
fmt.Sprintf("select * from foo where time > %d000000000.0", startTime),
|
||||
fmt.Sprintf("select * from foo where time > %ds", startTime),
|
||||
fmt.Sprintf("select * from foo where time > %d.0s", startTime),
|
||||
} {
|
||||
q, err := ParseQuery(query)
|
||||
c.Assert(err, IsNil)
|
||||
c.Assert(q.GetStartTime(), Equals, time.Unix(startTime, 0))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// insert into user.events.count.per_day select count(*) from user.events where time<forever group by time(1d)
|
||||
// insert into :series_name.percentiles.95 select percentile(95,value) from stats.* where time<forever group by time(1d)
|
||||
|
|
|
@ -55,9 +55,9 @@ static int yycolumn = 1;
|
|||
|
||||
[0-9]+ { yylval->string = strdup(yytext); return INT_VALUE; }
|
||||
|
||||
[0-9]*\.[0-9]+|[0-9]+\.[0-9]* { yylval->string = strdup(yytext); return FLOAT_VALUE; }
|
||||
([0-9]+|[0-9]*\.[0-9]+|[0-9]+\.[0-9]*)[smhdw] { yylval->string = strdup(yytext); return DURATION; }
|
||||
|
||||
[0-9]+[smhdw] { yylval->string = strdup(yytext); return DURATION; }
|
||||
[0-9]*\.[0-9]+|[0-9]+\.[0-9]* { yylval->string = strdup(yytext); return FLOAT_VALUE; }
|
||||
|
||||
\/.+\/ { yytext[strlen(yytext)-1]='\0';yylval->string=strdup(yytext+1);return REGEX_STRING; }
|
||||
\/.+\/i { yytext[strlen(yytext)-2]='\0';yylval->string=strdup(yytext+1);return INSENSITIVE_REGEX_STRING; }
|
||||
|
|
|
@ -179,34 +179,36 @@ func parseTime(expr *Expression) (int64, error) {
|
|||
|
||||
name := value.Name
|
||||
|
||||
parsedInt, err := strconv.ParseInt(name[:len(name)-1], 10, 64)
|
||||
parsedFloat, err := strconv.ParseFloat(name[:len(name)-1], 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
switch name[len(name)-1] {
|
||||
case 'u':
|
||||
return parsedInt * int64(time.Microsecond), nil
|
||||
return int64(parsedFloat * float64(time.Microsecond)), nil
|
||||
case 's':
|
||||
return parsedInt * int64(time.Second), nil
|
||||
return int64(parsedFloat * float64(time.Second)), nil
|
||||
case 'm':
|
||||
return parsedInt * int64(time.Minute), nil
|
||||
return int64(parsedFloat * float64(time.Minute)), nil
|
||||
case 'h':
|
||||
return parsedInt * int64(time.Hour), nil
|
||||
return int64(parsedFloat * float64(time.Hour)), nil
|
||||
case 'd':
|
||||
return parsedInt * 24 * int64(time.Hour), nil
|
||||
return int64(parsedFloat * 24 * float64(time.Hour)), nil
|
||||
case 'w':
|
||||
return parsedInt * 7 * 24 * int64(time.Hour), nil
|
||||
return int64(parsedFloat * 7 * 24 * float64(time.Hour)), nil
|
||||
}
|
||||
|
||||
lastChar := name[len(name)-1]
|
||||
if !unicode.IsDigit(rune(lastChar)) {
|
||||
if !unicode.IsDigit(rune(lastChar)) && lastChar != '.' {
|
||||
return 0, fmt.Errorf("Invalid character '%c'", lastChar)
|
||||
}
|
||||
|
||||
extraDigit := int64(lastChar - '0')
|
||||
parsedInt = parsedInt*10 + extraDigit
|
||||
return int64(parsedInt), err
|
||||
if name[len(name)-2] != '.' {
|
||||
extraDigit := float64(lastChar - '0')
|
||||
parsedFloat = parsedFloat*10 + extraDigit
|
||||
}
|
||||
return int64(parsedFloat), nil
|
||||
}
|
||||
|
||||
leftExpression, _ := expr.GetLeftExpression()
|
||||
|
@ -283,6 +285,15 @@ func getReferencedColumnsFromCondition(condition *WhereCondition, mapping map[st
|
|||
return
|
||||
}
|
||||
|
||||
func isNumericValue(value *Value) bool {
|
||||
switch value.Type {
|
||||
case ValueDuration, ValueFloat, ValueInt:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// parse the start time or end time from the where conditions and return the new condition
|
||||
// without the time clauses, or nil if there are no where conditions left
|
||||
func getTime(condition *WhereCondition, isParsingStartTime bool) (*WhereCondition, time.Time, error) {
|
||||
|
@ -294,6 +305,17 @@ func getTime(condition *WhereCondition, isParsingStartTime bool) (*WhereConditio
|
|||
leftValue, isLeftValue := expr.Left.GetLeftValue()
|
||||
rightValue, isRightValue := expr.Right.GetLeftValue()
|
||||
|
||||
// this can only be the case if the where condition
|
||||
// is of the form `"time" > 123456789`, so let's see
|
||||
// which side is a float value
|
||||
if isLeftValue && isRightValue {
|
||||
if isNumericValue(rightValue) {
|
||||
isRightValue = false
|
||||
} else {
|
||||
isLeftValue = false
|
||||
}
|
||||
}
|
||||
|
||||
// if this expression isn't "time > xxx" or "xxx < time" then return
|
||||
// TODO: we should do a check to make sure "time" doesn't show up in
|
||||
// either expressions
|
||||
|
@ -307,11 +329,13 @@ func getTime(condition *WhereCondition, isParsingStartTime bool) (*WhereConditio
|
|||
return condition, ZERO_TIME, nil
|
||||
}
|
||||
timeExpression = expr.Right
|
||||
} else {
|
||||
} else if !isLeftValue {
|
||||
if rightValue.Name != "time" {
|
||||
return condition, ZERO_TIME, nil
|
||||
}
|
||||
timeExpression = expr.Left
|
||||
} else {
|
||||
return nil, ZERO_TIME, fmt.Errorf("Invalid time condition %v", condition)
|
||||
}
|
||||
|
||||
switch expr.Operation {
|
||||
|
|
|
@ -214,7 +214,7 @@ func (self *QueryApiSuite) TestGetStartTimeWithOr(c *C) {
|
|||
func (self *QueryApiSuite) TestErrorInStartTime(c *C) {
|
||||
queriesAndErrors := map[string]string{
|
||||
"select * from t where time > now() * 1d and time < now() - 1h;": ".*'\\*'.*",
|
||||
"select * from t where time > blah * 1d and time < now() - 1h;": ".*strconv.ParseInt.*",
|
||||
"select * from t where time > blah * 1d and time < now() - 1h;": ".*strconv.ParseFloat.*",
|
||||
"select * from t where time == now() * 1d and time < now() - 1h;": ".*Cannot use time with '=='.*",
|
||||
"select * from t where time > now() - 1d or time > now() - 1h;": ".*Invalid where.*",
|
||||
"select * from t where time > foo() - 1d or time > now() - 1h;": ".*Invalid use of function foo.*",
|
||||
|
|
Loading…
Reference in New Issue