From e47426ff6e670fc87820f4f3ea25c8e6f5837fcc Mon Sep 17 00:00:00 2001 From: "Jonathan A. Sternberg" Date: Thu, 18 Feb 2016 23:06:41 -0500 Subject: [PATCH] Support integer literals in the query language Numbers in the query without any decimal will now be emitted as integers instead and be parsed as an IntegerLiteral. This ensures we keep the original context that a query was issued with and allows us to act more similar to how programming languages are typically structured when it comes to floats and ints. This adds functionality for dealing with integers promoting to floats in the various different places where math are used. Fixes #5744 and #5629. --- CHANGELOG.md | 1 + cmd/influxd/run/server_suite_test.go | 2 +- influxql/ast.go | 230 ++++++++++++++++++++---- influxql/ast_test.go | 27 +-- influxql/call_iterator.go | 6 +- influxql/parser.go | 43 +++-- influxql/parser_test.go | 95 +++++----- influxql/scanner.go | 9 +- influxql/scanner_test.go | 5 +- influxql/select.go | 260 +++++++++++++++++++++++++-- influxql/select_test.go | 192 +++++++++++++++++--- influxql/token.go | 1 + 12 files changed, 700 insertions(+), 171 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e91f2df1f3c..e1eb8153093 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - [#6012](https://github.com/influxdata/influxdb/pull/6012): Add DROP SHARD support. - [#6025](https://github.com/influxdata/influxdb/pull/6025): Remove deprecated JSON write path. +- [#5744](https://github.com/influxdata/influxdb/issues/5744): Add integer literal support to the query language. ### Bugfixes diff --git a/cmd/influxd/run/server_suite_test.go b/cmd/influxd/run/server_suite_test.go index 8e54f253969..cc198e77fc3 100644 --- a/cmd/influxd/run/server_suite_test.go +++ b/cmd/influxd/run/server_suite_test.go @@ -41,7 +41,7 @@ func init() { &Query{ name: "create database with retention replication should error with bad retention replication number", command: `CREATE DATABASE db0 WITH REPLICATION xyz`, - exp: `{"error":"error parsing query: found xyz, expected number at line 1, char 38"}`, + exp: `{"error":"error parsing query: found xyz, expected integer at line 1, char 38"}`, }, &Query{ name: "create database with retention name should error with missing retention name", diff --git a/influxql/ast.go b/influxql/ast.go index de6fd5b8a13..f48d12800e1 100644 --- a/influxql/ast.go +++ b/influxql/ast.go @@ -135,6 +135,7 @@ func (*Call) node() {} func (*Dimension) node() {} func (Dimensions) node() {} func (*DurationLiteral) node() {} +func (*IntegerLiteral) node() {} func (*Field) node() {} func (Fields) node() {} func (*Measurement) node() {} @@ -252,6 +253,7 @@ func (*BooleanLiteral) expr() {} func (*Call) expr() {} func (*Distinct) expr() {} func (*DurationLiteral) expr() {} +func (*IntegerLiteral) expr() {} func (*nilLiteral) expr() {} func (*NumberLiteral) expr() {} func (*ParenExpr) expr() {} @@ -269,6 +271,7 @@ type Literal interface { func (*BooleanLiteral) literal() {} func (*DurationLiteral) literal() {} +func (*IntegerLiteral) literal() {} func (*nilLiteral) literal() {} func (*NumberLiteral) literal() {} func (*RegexLiteral) literal() {} @@ -1395,7 +1398,7 @@ func (s *SelectStatement) validTopBottomAggr(expr *Call) error { return fmt.Errorf("invalid number of arguments for %s, expected at least %d, got %d", expr.Name, exp, got) } if len(expr.Args) > 1 { - callLimit, ok := expr.Args[len(expr.Args)-1].(*NumberLiteral) + callLimit, ok := expr.Args[len(expr.Args)-1].(*IntegerLiteral) if !ok { return fmt.Errorf("expected integer as last argument in %s(), found %s", expr.Name, expr.Args[len(expr.Args)-1]) } @@ -1421,8 +1424,11 @@ func (s *SelectStatement) validPercentileAggr(expr *Call) error { if exp, got := 2, len(expr.Args); got != exp { return fmt.Errorf("invalid number of arguments for %s, expected %d, got %d", expr.Name, exp, got) } - _, ok := expr.Args[1].(*NumberLiteral) - if !ok { + + switch expr.Args[1].(type) { + case *IntegerLiteral, *NumberLiteral: + return nil + default: return fmt.Errorf("expected float argument in percentile()") } return nil @@ -3034,6 +3040,14 @@ type NumberLiteral struct { // String returns a string representation of the literal. func (l *NumberLiteral) String() string { return strconv.FormatFloat(l.Val, 'f', 3, 64) } +// IntegerLiteral represents an integer literal. +type IntegerLiteral struct { + Val int64 +} + +// String returns a string representation of the literal. +func (l *IntegerLiteral) String() string { return fmt.Sprintf("%d", l.Val) } + // BooleanLiteral represents a boolean literal. type BooleanLiteral struct { Val bool @@ -3236,6 +3250,8 @@ func CloneExpr(expr Expr) Expr { return &Distinct{Val: expr.Val} case *DurationLiteral: return &DurationLiteral{Val: expr.Val} + case *IntegerLiteral: + return &IntegerLiteral{Val: expr.Val} case *NumberLiteral: return &NumberLiteral{Val: expr.Val} case *ParenExpr: @@ -3379,6 +3395,8 @@ func timeExprValue(ref Expr, lit Expr) time.Time { return time.Unix(0, int64(lit.Val)).UTC() case *NumberLiteral: return time.Unix(0, int64(lit.Val)).UTC() + case *IntegerLiteral: + return time.Unix(0, lit.Val).UTC() } } return time.Time{} @@ -3602,6 +3620,8 @@ func Eval(expr Expr, m map[string]interface{}) interface{} { return evalBinaryExpr(expr, m) case *BooleanLiteral: return expr.Val + case *IntegerLiteral: + return expr.Val case *NumberLiteral: return expr.Val case *ParenExpr: @@ -3636,61 +3656,121 @@ func evalBinaryExpr(expr *BinaryExpr, m map[string]interface{}) interface{} { return lhs != rhs } case float64: - rhs, _ := rhs.(float64) + // Try the rhs as a float64 or int64 + rhsf, ok := rhs.(float64) + if !ok { + var rhsi int64 + if rhsi, ok = rhs.(int64); ok { + rhsf = float64(rhsi) + } + } + + rhs := rhsf switch expr.Op { case EQ: - return lhs == rhs + return ok && (lhs == rhs) case NEQ: - return lhs != rhs + return ok && (lhs != rhs) case LT: - return lhs < rhs + return ok && (lhs < rhs) case LTE: - return lhs <= rhs + return ok && (lhs <= rhs) case GT: - return lhs > rhs + return ok && (lhs > rhs) case GTE: - return lhs >= rhs + return ok && (lhs >= rhs) case ADD: + if !ok { + return nil + } return lhs + rhs case SUB: + if !ok { + return nil + } return lhs - rhs case MUL: + if !ok { + return nil + } return lhs * rhs case DIV: - if rhs == 0 { + if !ok { + return nil + } else if rhs == 0 { return float64(0) } return lhs / rhs } case int64: - // we parse all number literals as float 64, so we have to convert from - // an interface to the float64, then cast to an int64 for comparison - rhsf, _ := rhs.(float64) - rhs := int64(rhsf) - switch expr.Op { - case EQ: - return lhs == rhs - case NEQ: - return lhs != rhs - case LT: - return lhs < rhs - case LTE: - return lhs <= rhs - case GT: - return lhs > rhs - case GTE: - return lhs >= rhs - case ADD: - return lhs + rhs - case SUB: - return lhs - rhs - case MUL: - return lhs * rhs - case DIV: - if rhs == 0 { - return int64(0) + // Try as a float64 to see if a float cast is required. + rhsf, ok := rhs.(float64) + if ok { + lhs := float64(lhs) + rhs := rhsf + switch expr.Op { + case EQ: + return lhs == rhs + case NEQ: + return lhs != rhs + case LT: + return lhs < rhs + case LTE: + return lhs <= rhs + case GT: + return lhs > rhs + case GTE: + return lhs >= rhs + case ADD: + return lhs + rhs + case SUB: + return lhs - rhs + case MUL: + return lhs * rhs + case DIV: + if rhs == 0 { + return float64(0) + } + return lhs / rhs + } + } else { + rhs, ok := rhs.(int64) + switch expr.Op { + case EQ: + return ok && (lhs == rhs) + case NEQ: + return ok && (lhs != rhs) + case LT: + return ok && (lhs < rhs) + case LTE: + return ok && (lhs <= rhs) + case GT: + return ok && (lhs > rhs) + case GTE: + return ok && (lhs >= rhs) + case ADD: + if !ok { + return nil + } + return lhs + rhs + case SUB: + if !ok { + return nil + } + return lhs - rhs + case MUL: + if !ok { + return nil + } + return lhs * rhs + case DIV: + if !ok { + return nil + } else if rhs == 0 { + return float64(0) + } + return lhs / rhs } - return lhs / rhs } case string: switch expr.Op { @@ -3786,6 +3866,8 @@ func reduceBinaryExpr(expr *BinaryExpr, valuer Valuer) Expr { return reduceBinaryExprBooleanLHS(op, lhs, rhs) case *DurationLiteral: return reduceBinaryExprDurationLHS(op, lhs, rhs) + case *IntegerLiteral: + return reduceBinaryExprIntegerLHS(op, lhs, rhs) case *nilLiteral: return reduceBinaryExprNilLHS(op, lhs, rhs) case *NumberLiteral: @@ -3849,6 +3931,16 @@ func reduceBinaryExprDurationLHS(op Token, lhs *DurationLiteral, rhs Expr) Expr } return &DurationLiteral{Val: lhs.Val / time.Duration(rhs.Val)} } + case *IntegerLiteral: + switch op { + case MUL: + return &DurationLiteral{Val: lhs.Val * time.Duration(rhs.Val)} + case DIV: + if rhs.Val == 0 { + return &DurationLiteral{Val: 0} + } + return &DurationLiteral{Val: lhs.Val / time.Duration(rhs.Val)} + } case *TimeLiteral: switch op { case ADD: @@ -3860,6 +3952,42 @@ func reduceBinaryExprDurationLHS(op Token, lhs *DurationLiteral, rhs Expr) Expr return &BinaryExpr{Op: op, LHS: lhs, RHS: rhs} } +func reduceBinaryExprIntegerLHS(op Token, lhs *IntegerLiteral, rhs Expr) Expr { + switch rhs := rhs.(type) { + case *NumberLiteral: + return reduceBinaryExprNumberLHS(op, &NumberLiteral{Val: float64(lhs.Val)}, rhs) + case *IntegerLiteral: + switch op { + case ADD: + return &IntegerLiteral{Val: lhs.Val + rhs.Val} + case SUB: + return &IntegerLiteral{Val: lhs.Val - rhs.Val} + case MUL: + return &IntegerLiteral{Val: lhs.Val * rhs.Val} + case DIV: + if rhs.Val == 0 { + return &NumberLiteral{Val: 0} + } + return &NumberLiteral{Val: float64(lhs.Val) / float64(rhs.Val)} + case EQ: + return &BooleanLiteral{Val: lhs.Val == rhs.Val} + case NEQ: + return &BooleanLiteral{Val: lhs.Val != rhs.Val} + case GT: + return &BooleanLiteral{Val: lhs.Val > rhs.Val} + case GTE: + return &BooleanLiteral{Val: lhs.Val >= rhs.Val} + case LT: + return &BooleanLiteral{Val: lhs.Val < rhs.Val} + case LTE: + return &BooleanLiteral{Val: lhs.Val <= rhs.Val} + } + case *nilLiteral: + return &BooleanLiteral{Val: false} + } + return &BinaryExpr{Op: op, LHS: lhs, RHS: rhs} +} + func reduceBinaryExprNilLHS(op Token, lhs *nilLiteral, rhs Expr) Expr { switch op { case EQ, NEQ: @@ -3896,6 +4024,32 @@ func reduceBinaryExprNumberLHS(op Token, lhs *NumberLiteral, rhs Expr) Expr { case LTE: return &BooleanLiteral{Val: lhs.Val <= rhs.Val} } + case *IntegerLiteral: + switch op { + case ADD: + return &NumberLiteral{Val: lhs.Val + float64(rhs.Val)} + case SUB: + return &NumberLiteral{Val: lhs.Val - float64(rhs.Val)} + case MUL: + return &NumberLiteral{Val: lhs.Val * float64(rhs.Val)} + case DIV: + if float64(rhs.Val) == 0 { + return &NumberLiteral{Val: 0} + } + return &NumberLiteral{Val: lhs.Val / float64(rhs.Val)} + case EQ: + return &BooleanLiteral{Val: lhs.Val == float64(rhs.Val)} + case NEQ: + return &BooleanLiteral{Val: lhs.Val != float64(rhs.Val)} + case GT: + return &BooleanLiteral{Val: lhs.Val > float64(rhs.Val)} + case GTE: + return &BooleanLiteral{Val: lhs.Val >= float64(rhs.Val)} + case LT: + return &BooleanLiteral{Val: lhs.Val < float64(rhs.Val)} + case LTE: + return &BooleanLiteral{Val: lhs.Val <= float64(rhs.Val)} + } case *nilLiteral: return &BooleanLiteral{Val: false} } diff --git a/influxql/ast_test.go b/influxql/ast_test.go index c8a999d8636..9c685d45332 100644 --- a/influxql/ast_test.go +++ b/influxql/ast_test.go @@ -64,7 +64,7 @@ func TestSelectStatement_Substatement(t *testing.T) { { stmt: `SELECT value FROM myseries WHERE value > 1`, expr: &influxql.VarRef{Val: "value"}, - sub: `SELECT value FROM myseries WHERE value > 1.000`, + sub: `SELECT value FROM myseries WHERE value > 1`, }, // 1. Simple join @@ -92,14 +92,14 @@ func TestSelectStatement_Substatement(t *testing.T) { { stmt: `SELECT sum(aa.value) + sum(bb.value) FROM aa, bb WHERE aa.host = 'servera' AND (bb.host = 'serverb' OR bb.host = 'serverc') AND 1 = 2`, expr: &influxql.VarRef{Val: "bb.value"}, - sub: `SELECT "bb.value" FROM bb WHERE ("bb.host" = 'serverb' OR "bb.host" = 'serverc') AND 1.000 = 2.000`, + sub: `SELECT "bb.value" FROM bb WHERE ("bb.host" = 'serverb' OR "bb.host" = 'serverc') AND 1 = 2`, }, // 5. 4 with different condition order { stmt: `SELECT sum(aa.value) + sum(bb.value) FROM aa, bb WHERE ((bb.host = 'serverb' OR bb.host = 'serverc') AND aa.host = 'servera') AND 1 = 2`, expr: &influxql.VarRef{Val: "bb.value"}, - sub: `SELECT "bb.value" FROM bb WHERE (("bb.host" = 'serverb' OR "bb.host" = 'serverc')) AND 1.000 = 2.000`, + sub: `SELECT "bb.value" FROM bb WHERE (("bb.host" = 'serverb' OR "bb.host" = 'serverc')) AND 1 = 2`, }, } @@ -766,6 +766,7 @@ func TestTimeRange(t *testing.T) { // number literal {expr: `time < 10`, min: `0001-01-01T00:00:00Z`, max: `1970-01-01T00:00:00.000000009Z`}, + {expr: `time < 10i`, min: `0001-01-01T00:00:00Z`, max: `1970-01-01T00:00:00.000000009Z`}, // Equality {expr: `time = '2000-01-01 00:00:00'`, min: `2000-01-01T00:00:00Z`, max: `2000-01-01T00:00:00.000000001Z`}, @@ -856,7 +857,7 @@ func TestRewrite(t *testing.T) { }) // Verify that everything is flipped. - if act := act.String(); act != `2.000 = foo OR 1.000 > time` { + if act := act.String(); act != `2 = foo OR 1 > time` { t.Fatalf("unexpected result: %s", act) } } @@ -877,7 +878,7 @@ func TestRewriteExpr(t *testing.T) { }) // Verify that everything is flipped. - if act := act.String(); act != `foo = 2.000` { + if act := act.String(); act != `foo = 2` { t.Fatalf("unexpected result: %s", act) } } @@ -995,7 +996,7 @@ func TestEval(t *testing.T) { data map[string]interface{} }{ // Number literals. - {in: `1 + 2`, out: float64(3)}, + {in: `1 + 2`, out: int64(3)}, {in: `(foo*2) + ( (4/2) + (3 * 5) - 0.5 )`, out: float64(26.5), data: map[string]interface{}{"foo": float64(5)}}, {in: `foo / 2`, out: float64(2), data: map[string]interface{}{"foo": float64(4)}}, {in: `4 = 4`, out: true}, @@ -1005,6 +1006,9 @@ func TestEval(t *testing.T) { {in: `4 < 6`, out: true}, {in: `4 <= 4`, out: true}, {in: `4 AND 5`, out: nil}, + {in: `0 = 'test'`, out: false}, + {in: `1.0 = 1`, out: true}, + {in: `1.2 = 1`, out: false}, // Boolean literals. {in: `true AND false`, out: false}, @@ -1049,17 +1053,18 @@ func TestReduce(t *testing.T) { data Valuer }{ // Number literals. - {in: `1 + 2`, out: `3.000`}, - {in: `(foo*2) + ( (4/2) + (3 * 5) - 0.5 )`, out: `(foo * 2.000) + 16.500`}, - {in: `foo(bar(2 + 3), 4)`, out: `foo(bar(5.000), 4.000)`}, + {in: `1 + 2`, out: `3`}, + {in: `(foo*2) + ( (4/2) + (3 * 5) - 0.5 )`, out: `(foo * 2) + 16.500`}, + {in: `foo(bar(2 + 3), 4)`, out: `foo(bar(5), 4)`}, {in: `4 / 0`, out: `0.000`}, + {in: `1 / 2`, out: `0.500`}, {in: `4 = 4`, out: `true`}, {in: `4 <> 4`, out: `false`}, {in: `6 > 4`, out: `true`}, {in: `4 >= 4`, out: `true`}, {in: `4 < 6`, out: `true`}, {in: `4 <= 4`, out: `true`}, - {in: `4 AND 5`, out: `4.000 AND 5.000`}, + {in: `4 AND 5`, out: `4 AND 5`}, // Boolean literals. {in: `true AND false`, out: `false`}, @@ -1097,7 +1102,7 @@ func TestReduce(t *testing.T) { {in: `60s >= 1m`, out: `true`}, {in: `60s AND 1m`, out: `1m AND 1m`}, {in: `60m / 0`, out: `0s`}, - {in: `60m + 50`, out: `1h + 50.000`}, + {in: `60m + 50`, out: `1h + 50`}, // String literals. {in: `'foo' + 'bar'`, out: `'foobar'`}, diff --git a/influxql/call_iterator.go b/influxql/call_iterator.go index 19f9d172060..ebf033db6d4 100644 --- a/influxql/call_iterator.go +++ b/influxql/call_iterator.go @@ -646,8 +646,7 @@ func IntegerSpreadReduceSlice(a []IntegerPoint) []IntegerPoint { return []IntegerPoint{{Time: ZeroTime, Value: max - min}} } -// newTopIterator returns an iterator for operating on a top() call. -func newTopIterator(input Iterator, opt IteratorOptions, n *NumberLiteral, tags []int) (Iterator, error) { +func newTopIterator(input Iterator, opt IteratorOptions, n *IntegerLiteral, tags []int) (Iterator, error) { switch input := input.(type) { case FloatIterator: aggregateFn := NewFloatTopReduceSliceFunc(int(n.Val), tags, opt.Interval) @@ -760,8 +759,7 @@ func NewIntegerTopReduceSliceFunc(n int, tags []int, interval Interval) IntegerR } } -// newBottomIterator returns an iterator for operating on a bottom() call. -func newBottomIterator(input Iterator, opt IteratorOptions, n *NumberLiteral, tags []int) (Iterator, error) { +func newBottomIterator(input Iterator, opt IteratorOptions, n *IntegerLiteral, tags []int) (Iterator, error) { switch input := input.(type) { case FloatIterator: aggregateFn := NewFloatBottomReduceSliceFunc(int(n.Val), tags, opt.Interval) diff --git a/influxql/parser.go b/influxql/parser.go index 0e5bf34e334..d96eb4c2386 100644 --- a/influxql/parser.go +++ b/influxql/parser.go @@ -463,13 +463,8 @@ Loop: // parseInt parses a string and returns an integer literal. func (p *Parser) parseInt(min, max int) (int, error) { tok, pos, lit := p.scanIgnoreWhitespace() - if tok != NUMBER { - return 0, newParseError(tokstr(tok, lit), []string{"number"}, pos) - } - - // Return an error if the number has a fractional part. - if strings.Contains(lit, ".") { - return 0, &ParseError{Message: "number must be an integer", Pos: pos} + if tok != INTEGER { + return 0, newParseError(tokstr(tok, lit), []string{"integer"}, pos) } // Convert string to int. @@ -489,8 +484,8 @@ func (p *Parser) parseInt(min, max int) (int, error) { // parseUInt32 parses a string and returns a 32-bit unsigned integer literal. func (p *Parser) parseUInt32() (uint32, error) { tok, pos, lit := p.scanIgnoreWhitespace() - if tok != NUMBER { - return 0, newParseError(tokstr(tok, lit), []string{"number"}, pos) + if tok != INTEGER { + return 0, newParseError(tokstr(tok, lit), []string{"integer"}, pos) } // Convert string to unsigned 32-bit integer @@ -505,8 +500,8 @@ func (p *Parser) parseUInt32() (uint32, error) { // parseUInt64 parses a string and returns a 64-bit unsigned integer literal. func (p *Parser) parseUInt64() (uint64, error) { tok, pos, lit := p.scanIgnoreWhitespace() - if tok != NUMBER { - return 0, newParseError(tokstr(tok, lit), []string{"number"}, pos) + if tok != INTEGER { + return 0, newParseError(tokstr(tok, lit), []string{"integer"}, pos) } // Convert string to unsigned 64-bit integer @@ -2065,11 +2060,14 @@ func (p *Parser) parseFill() (FillOption, interface{}, error) { case "previous": return PreviousFill, nil, nil default: - num, ok := lit.Args[0].(*NumberLiteral) - if !ok { + switch num := lit.Args[0].(type) { + case *IntegerLiteral: + return NumberFill, num.Val, nil + case *NumberLiteral: + return NumberFill, num.Val, nil + default: return NullFill, nil, fmt.Errorf("expected number argument in fill()") } - return NumberFill, num.Val, nil } } @@ -2084,19 +2082,12 @@ func (p *Parser) parseOptionalTokenAndInt(t Token) (int, error) { // Scan the number. tok, pos, lit := p.scanIgnoreWhitespace() - if tok != NUMBER { - return 0, newParseError(tokstr(tok, lit), []string{"number"}, pos) - } - - // Return an error if the number has a fractional part. - if strings.Contains(lit, ".") { - msg := fmt.Sprintf("fractional parts not allowed in %s", t.String()) - return 0, &ParseError{Message: msg, Pos: pos} + if tok != INTEGER { + return 0, newParseError(tokstr(tok, lit), []string{"integer"}, pos) } // Parse number. n, _ := strconv.ParseInt(lit, 10, 64) - if n < 0 { msg := fmt.Sprintf("%s must be >= 0", t.String()) return 0, &ParseError{Message: msg, Pos: pos} @@ -2346,6 +2337,12 @@ func (p *Parser) parseUnaryExpr() (Expr, error) { return nil, &ParseError{Message: "unable to parse number", Pos: pos} } return &NumberLiteral{Val: v}, nil + case INTEGER: + v, err := strconv.ParseInt(lit, 10, 64) + if err != nil { + return nil, &ParseError{Message: "unable to parse integer", Pos: pos} + } + return &IntegerLiteral{Val: v}, nil case TRUE, FALSE: return &BooleanLiteral{Val: (tok == TRUE)}, nil case DURATIONVAL: diff --git a/influxql/parser_test.go b/influxql/parser_test.go index 9bbd1ba5c74..ef3716991b1 100644 --- a/influxql/parser_test.go +++ b/influxql/parser_test.go @@ -280,7 +280,7 @@ func TestParser_ParseStatement(t *testing.T) { stmt: &influxql.SelectStatement{ IsRawQuery: false, Fields: []*influxql.Field{ - {Expr: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}}}, + {Expr: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.IntegerLiteral{Val: 2}}}}, }, Sources: []influxql.Source{&influxql.Measurement{Name: "cpu"}}, }, @@ -291,7 +291,7 @@ func TestParser_ParseStatement(t *testing.T) { stmt: &influxql.SelectStatement{ IsRawQuery: false, Fields: []*influxql.Field{ - {Expr: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}}}, + {Expr: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.IntegerLiteral{Val: 2}}}}, }, Sources: []influxql.Source{&influxql.Measurement{Name: "cpu"}}, }, @@ -302,7 +302,7 @@ func TestParser_ParseStatement(t *testing.T) { stmt: &influxql.SelectStatement{ IsRawQuery: false, Fields: []*influxql.Field{ - {Expr: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.NumberLiteral{Val: 2}}}}, + {Expr: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.IntegerLiteral{Val: 2}}}}, {Expr: &influxql.VarRef{Val: "tag1"}}, }, Sources: []influxql.Source{&influxql.Measurement{Name: "cpu"}}, @@ -314,7 +314,7 @@ func TestParser_ParseStatement(t *testing.T) { stmt: &influxql.SelectStatement{ IsRawQuery: false, Fields: []*influxql.Field{ - {Expr: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.VarRef{Val: "tag1"}, &influxql.NumberLiteral{Val: 2}}}}, + {Expr: &influxql.Call{Name: "top", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}, &influxql.VarRef{Val: "tag1"}, &influxql.IntegerLiteral{Val: 2}}}}, {Expr: &influxql.VarRef{Val: "tag1"}}, }, Sources: []influxql.Source{&influxql.Measurement{Name: "cpu"}}, @@ -404,7 +404,7 @@ func TestParser_ParseStatement(t *testing.T) { Condition: &influxql.BinaryExpr{ Op: influxql.GT, LHS: &influxql.VarRef{Val: "load"}, - RHS: &influxql.NumberLiteral{Val: 100}, + RHS: &influxql.IntegerLiteral{Val: 100}, }, }, }, @@ -417,7 +417,7 @@ func TestParser_ParseStatement(t *testing.T) { Condition: &influxql.BinaryExpr{ Op: influxql.GTE, LHS: &influxql.VarRef{Val: "load"}, - RHS: &influxql.NumberLiteral{Val: 100}, + RHS: &influxql.IntegerLiteral{Val: 100}, }, }, }, @@ -430,7 +430,7 @@ func TestParser_ParseStatement(t *testing.T) { Condition: &influxql.BinaryExpr{ Op: influxql.EQ, LHS: &influxql.VarRef{Val: "load"}, - RHS: &influxql.NumberLiteral{Val: 100}, + RHS: &influxql.IntegerLiteral{Val: 100}, }, }, }, @@ -443,7 +443,7 @@ func TestParser_ParseStatement(t *testing.T) { Condition: &influxql.BinaryExpr{ Op: influxql.LTE, LHS: &influxql.VarRef{Val: "load"}, - RHS: &influxql.NumberLiteral{Val: 100}, + RHS: &influxql.IntegerLiteral{Val: 100}, }, }, }, @@ -456,7 +456,7 @@ func TestParser_ParseStatement(t *testing.T) { Condition: &influxql.BinaryExpr{ Op: influxql.LT, LHS: &influxql.VarRef{Val: "load"}, - RHS: &influxql.NumberLiteral{Val: 100}, + RHS: &influxql.IntegerLiteral{Val: 100}, }, }, }, @@ -469,7 +469,7 @@ func TestParser_ParseStatement(t *testing.T) { Condition: &influxql.BinaryExpr{ Op: influxql.NEQ, LHS: &influxql.VarRef{Val: "load"}, - RHS: &influxql.NumberLiteral{Val: 100}, + RHS: &influxql.IntegerLiteral{Val: 100}, }, }, }, @@ -580,7 +580,7 @@ func TestParser_ParseStatement(t *testing.T) { }, Dimensions: []*influxql.Dimension{{Expr: &influxql.Call{Name: "time", Args: []influxql.Expr{&influxql.DurationLiteral{Val: 5 * time.Minute}}}}}, Fill: influxql.NumberFill, - FillValue: float64(1), + FillValue: int64(1), }, }, @@ -1640,27 +1640,27 @@ func TestParser_ParseStatement(t *testing.T) { {s: `SELECT field1 X`, err: `found X, expected FROM at line 1, char 15`}, {s: `SELECT field1 FROM "series" WHERE X +;`, err: `found ;, expected identifier, string, number, bool at line 1, char 38`}, {s: `SELECT field1 FROM myseries GROUP`, err: `found EOF, expected BY at line 1, char 35`}, - {s: `SELECT field1 FROM myseries LIMIT`, err: `found EOF, expected number at line 1, char 35`}, - {s: `SELECT field1 FROM myseries LIMIT 10.5`, err: `fractional parts not allowed in LIMIT at line 1, char 35`}, + {s: `SELECT field1 FROM myseries LIMIT`, err: `found EOF, expected integer at line 1, char 35`}, + {s: `SELECT field1 FROM myseries LIMIT 10.5`, err: `found 10.5, expected integer at line 1, char 35`}, {s: `SELECT top() FROM myseries`, err: `invalid number of arguments for top, expected at least 2, got 0`}, {s: `SELECT top(field1) FROM myseries`, err: `invalid number of arguments for top, expected at least 2, got 1`}, {s: `SELECT top(field1,foo) FROM myseries`, err: `expected integer as last argument in top(), found foo`}, {s: `SELECT top(field1,host,'server',foo) FROM myseries`, err: `expected integer as last argument in top(), found foo`}, - {s: `SELECT top(field1,5,'server',2) FROM myseries`, err: `only fields or tags are allowed in top(), found 5.000`}, + {s: `SELECT top(field1,5,'server',2) FROM myseries`, err: `only fields or tags are allowed in top(), found 5`}, {s: `SELECT top(field1,max(foo),'server',2) FROM myseries`, err: `only fields or tags are allowed in top(), found max(foo)`}, {s: `SELECT top(value, 10) + count(value) FROM myseries`, err: `cannot use top() inside of a binary expression`}, {s: `SELECT bottom() FROM myseries`, err: `invalid number of arguments for bottom, expected at least 2, got 0`}, {s: `SELECT bottom(field1) FROM myseries`, err: `invalid number of arguments for bottom, expected at least 2, got 1`}, {s: `SELECT bottom(field1,foo) FROM myseries`, err: `expected integer as last argument in bottom(), found foo`}, {s: `SELECT bottom(field1,host,'server',foo) FROM myseries`, err: `expected integer as last argument in bottom(), found foo`}, - {s: `SELECT bottom(field1,5,'server',2) FROM myseries`, err: `only fields or tags are allowed in bottom(), found 5.000`}, + {s: `SELECT bottom(field1,5,'server',2) FROM myseries`, err: `only fields or tags are allowed in bottom(), found 5`}, {s: `SELECT bottom(field1,max(foo),'server',2) FROM myseries`, err: `only fields or tags are allowed in bottom(), found max(foo)`}, {s: `SELECT bottom(value, 10) + count(value) FROM myseries`, err: `cannot use bottom() inside of a binary expression`}, {s: `SELECT percentile() FROM myseries`, err: `invalid number of arguments for percentile, expected 2, got 0`}, {s: `SELECT percentile(field1) FROM myseries`, err: `invalid number of arguments for percentile, expected 2, got 1`}, {s: `SELECT percentile(field1, foo) FROM myseries`, err: `expected float argument in percentile()`}, - {s: `SELECT field1 FROM myseries OFFSET`, err: `found EOF, expected number at line 1, char 36`}, - {s: `SELECT field1 FROM myseries OFFSET 10.5`, err: `fractional parts not allowed in OFFSET at line 1, char 36`}, + {s: `SELECT field1 FROM myseries OFFSET`, err: `found EOF, expected integer at line 1, char 36`}, + {s: `SELECT field1 FROM myseries OFFSET 10.5`, err: `found 10.5, expected integer at line 1, char 36`}, {s: `SELECT field1 FROM myseries ORDER`, err: `found EOF, expected BY at line 1, char 35`}, {s: `SELECT field1 FROM myseries ORDER BY`, err: `found EOF, expected identifier, ASC, DESC at line 1, char 38`}, {s: `SELECT field1 FROM myseries ORDER BY /`, err: `found /, expected identifier, ASC, DESC at line 1, char 38`}, @@ -1679,7 +1679,7 @@ func TestParser_ParseStatement(t *testing.T) { {s: `SELECT count(value) FROM foo where time > now() and time < now() group by time(b)`, err: `time dimension must have one duration argument`}, {s: `SELECT count(value) FROM foo where time > now() and time < now() group by time(1s), time(2s)`, err: `multiple time dimensions not allowed`}, {s: `SELECT field1 FROM 12`, err: `found 12, expected identifier at line 1, char 20`}, - {s: `SELECT 1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 FROM myseries`, err: `unable to parse number at line 1, char 8`}, + {s: `SELECT 1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 FROM myseries`, err: `unable to parse integer at line 1, char 8`}, {s: `SELECT 10.5h FROM myseries`, err: `found h, expected FROM at line 1, char 12`}, {s: `SELECT distinct(field1), sum(field1) FROM myseries`, err: `aggregate function distinct() can not be combined with other functions or fields`}, {s: `SELECT distinct(field1), field2 FROM myseries`, err: `aggregate function distinct() can not be combined with other functions or fields`}, @@ -1733,8 +1733,8 @@ func TestParser_ParseStatement(t *testing.T) { {s: `DROP SERIES`, err: `found EOF, expected FROM, WHERE at line 1, char 13`}, {s: `DROP SERIES FROM`, err: `found EOF, expected identifier at line 1, char 18`}, {s: `DROP SERIES FROM src WHERE`, err: `found EOF, expected identifier, string, number, bool at line 1, char 28`}, - {s: `DROP META SERVER`, err: `found EOF, expected number at line 1, char 18`}, - {s: `DROP DATA SERVER abc`, err: `found abc, expected number at line 1, char 18`}, + {s: `DROP META SERVER`, err: `found EOF, expected integer at line 1, char 18`}, + {s: `DROP DATA SERVER abc`, err: `found abc, expected integer at line 1, char 18`}, {s: `SHOW CONTINUOUS`, err: `found EOF, expected QUERIES at line 1, char 17`}, {s: `SHOW RETENTION`, err: `found EOF, expected POLICIES at line 1, char 16`}, {s: `SHOW RETENTION ON`, err: `found ON, expected POLICIES at line 1, char 16`}, @@ -1760,14 +1760,14 @@ func TestParser_ParseStatement(t *testing.T) { {s: `CREATE DATABASE`, err: `found EOF, expected identifier at line 1, char 17`}, {s: `CREATE DATABASE "testdb" WITH`, err: `found EOF, expected DURATION, REPLICATION, NAME at line 1, char 31`}, {s: `CREATE DATABASE "testdb" WITH DURATION`, err: `found EOF, expected duration at line 1, char 40`}, - {s: `CREATE DATABASE "testdb" WITH REPLICATION`, err: `found EOF, expected number at line 1, char 43`}, + {s: `CREATE DATABASE "testdb" WITH REPLICATION`, err: `found EOF, expected integer at line 1, char 43`}, {s: `CREATE DATABASE "testdb" WITH NAME`, err: `found EOF, expected identifier at line 1, char 36`}, {s: `CREATE DATABASE IF`, err: `found EOF, expected NOT at line 1, char 20`}, {s: `CREATE DATABASE IF NOT`, err: `found EOF, expected EXISTS at line 1, char 24`}, {s: `CREATE DATABASE IF NOT EXISTS`, err: `found EOF, expected identifier at line 1, char 31`}, {s: `CREATE DATABASE IF NOT EXISTS "testdb" WITH`, err: `found EOF, expected DURATION, REPLICATION, NAME at line 1, char 45`}, {s: `CREATE DATABASE IF NOT EXISTS "testdb" WITH DURATION`, err: `found EOF, expected duration at line 1, char 54`}, - {s: `CREATE DATABASE IF NOT EXISTS "testdb" WITH REPLICATION`, err: `found EOF, expected number at line 1, char 57`}, + {s: `CREATE DATABASE IF NOT EXISTS "testdb" WITH REPLICATION`, err: `found EOF, expected integer at line 1, char 57`}, {s: `CREATE DATABASE IF NOT EXISTS "testdb" WITH NAME`, err: `found EOF, expected identifier at line 1, char 50`}, {s: `DROP DATABASE`, err: `found EOF, expected identifier at line 1, char 15`}, {s: `DROP DATABASE IF`, err: `found EOF, expected EXISTS at line 1, char 18`}, @@ -1867,10 +1867,10 @@ func TestParser_ParseStatement(t *testing.T) { {s: `CREATE RETENTION POLICY policy1 ON testdb DURATION`, err: `found EOF, expected duration at line 1, char 52`}, {s: `CREATE RETENTION POLICY policy1 ON testdb DURATION bad`, err: `found bad, expected duration at line 1, char 52`}, {s: `CREATE RETENTION POLICY policy1 ON testdb DURATION 1h`, err: `found EOF, expected REPLICATION at line 1, char 54`}, - {s: `CREATE RETENTION POLICY policy1 ON testdb DURATION 1h REPLICATION`, err: `found EOF, expected number at line 1, char 67`}, - {s: `CREATE RETENTION POLICY policy1 ON testdb DURATION 1h REPLICATION 3.14`, err: `number must be an integer at line 1, char 67`}, + {s: `CREATE RETENTION POLICY policy1 ON testdb DURATION 1h REPLICATION`, err: `found EOF, expected integer at line 1, char 67`}, + {s: `CREATE RETENTION POLICY policy1 ON testdb DURATION 1h REPLICATION 3.14`, err: `found 3.14, expected integer at line 1, char 67`}, {s: `CREATE RETENTION POLICY policy1 ON testdb DURATION 1h REPLICATION 0`, err: `invalid value 0: must be 1 <= n <= 2147483647 at line 1, char 67`}, - {s: `CREATE RETENTION POLICY policy1 ON testdb DURATION 1h REPLICATION bad`, err: `found bad, expected number at line 1, char 67`}, + {s: `CREATE RETENTION POLICY policy1 ON testdb DURATION 1h REPLICATION bad`, err: `found bad, expected integer at line 1, char 67`}, {s: `CREATE RETENTION POLICY policy1 ON testdb DURATION 1h REPLICATION 1 foo`, err: `found foo, expected DEFAULT at line 1, char 69`}, {s: `ALTER`, err: `found EOF, expected RETENTION at line 1, char 7`}, {s: `ALTER RETENTION`, err: `found EOF, expected POLICY at line 1, char 17`}, @@ -1919,7 +1919,8 @@ func TestParser_ParseExpr(t *testing.T) { err string }{ // Primitives - {s: `100`, expr: &influxql.NumberLiteral{Val: 100}}, + {s: `100.0`, expr: &influxql.NumberLiteral{Val: 100}}, + {s: `100`, expr: &influxql.IntegerLiteral{Val: 100}}, {s: `'foo bar'`, expr: &influxql.StringLiteral{Val: "foo bar"}}, {s: `true`, expr: &influxql.BooleanLiteral{Val: true}}, {s: `false`, expr: &influxql.BooleanLiteral{Val: false}}, @@ -1935,8 +1936,8 @@ func TestParser_ParseExpr(t *testing.T) { s: `1 + 2`, expr: &influxql.BinaryExpr{ Op: influxql.ADD, - LHS: &influxql.NumberLiteral{Val: 1}, - RHS: &influxql.NumberLiteral{Val: 2}, + LHS: &influxql.IntegerLiteral{Val: 1}, + RHS: &influxql.IntegerLiteral{Val: 2}, }, }, @@ -1947,10 +1948,10 @@ func TestParser_ParseExpr(t *testing.T) { Op: influxql.ADD, LHS: &influxql.BinaryExpr{ Op: influxql.MUL, - LHS: &influxql.NumberLiteral{Val: 1}, - RHS: &influxql.NumberLiteral{Val: 2}, + LHS: &influxql.IntegerLiteral{Val: 1}, + RHS: &influxql.IntegerLiteral{Val: 2}, }, - RHS: &influxql.NumberLiteral{Val: 3}, + RHS: &influxql.IntegerLiteral{Val: 3}, }, }, @@ -1959,11 +1960,11 @@ func TestParser_ParseExpr(t *testing.T) { s: `1 + 2 * 3`, expr: &influxql.BinaryExpr{ Op: influxql.ADD, - LHS: &influxql.NumberLiteral{Val: 1}, + LHS: &influxql.IntegerLiteral{Val: 1}, RHS: &influxql.BinaryExpr{ Op: influxql.MUL, - LHS: &influxql.NumberLiteral{Val: 2}, - RHS: &influxql.NumberLiteral{Val: 3}, + LHS: &influxql.IntegerLiteral{Val: 2}, + RHS: &influxql.IntegerLiteral{Val: 3}, }, }, }, @@ -1976,11 +1977,11 @@ func TestParser_ParseExpr(t *testing.T) { LHS: &influxql.ParenExpr{ Expr: &influxql.BinaryExpr{ Op: influxql.ADD, - LHS: &influxql.NumberLiteral{Val: 1}, - RHS: &influxql.NumberLiteral{Val: 2}, + LHS: &influxql.IntegerLiteral{Val: 1}, + RHS: &influxql.IntegerLiteral{Val: 2}, }, }, - RHS: &influxql.NumberLiteral{Val: 3}, + RHS: &influxql.IntegerLiteral{Val: 3}, }, }, @@ -1991,10 +1992,10 @@ func TestParser_ParseExpr(t *testing.T) { Op: influxql.MUL, LHS: &influxql.BinaryExpr{ Op: influxql.MUL, - LHS: &influxql.NumberLiteral{Val: 1}, - RHS: &influxql.NumberLiteral{Val: 2}, + LHS: &influxql.IntegerLiteral{Val: 1}, + RHS: &influxql.IntegerLiteral{Val: 2}, }, - RHS: &influxql.NumberLiteral{Val: 3}, + RHS: &influxql.IntegerLiteral{Val: 3}, }, }, @@ -2030,14 +2031,14 @@ func TestParser_ParseExpr(t *testing.T) { LHS: &influxql.BinaryExpr{ Op: influxql.ADD, LHS: &influxql.VarRef{Val: "value"}, - RHS: &influxql.NumberLiteral{Val: 3}, + RHS: &influxql.IntegerLiteral{Val: 3}, }, - RHS: &influxql.NumberLiteral{Val: 30}, + RHS: &influxql.IntegerLiteral{Val: 30}, }, RHS: &influxql.BinaryExpr{ Op: influxql.ADD, - LHS: &influxql.NumberLiteral{Val: 1}, - RHS: &influxql.NumberLiteral{Val: 2}, + LHS: &influxql.IntegerLiteral{Val: 1}, + RHS: &influxql.IntegerLiteral{Val: 2}, }, }, RHS: &influxql.BooleanLiteral{Val: true}, @@ -2084,11 +2085,11 @@ func TestParser_ParseExpr(t *testing.T) { expr: &influxql.Call{ Name: "my_func", Args: []influxql.Expr{ - &influxql.NumberLiteral{Val: 1}, + &influxql.IntegerLiteral{Val: 1}, &influxql.BinaryExpr{ Op: influxql.ADD, - LHS: &influxql.NumberLiteral{Val: 2}, - RHS: &influxql.NumberLiteral{Val: 3}, + LHS: &influxql.IntegerLiteral{Val: 2}, + RHS: &influxql.IntegerLiteral{Val: 3}, }, }, }, diff --git a/influxql/scanner.go b/influxql/scanner.go index aa562e224f2..3c78d6cdd24 100644 --- a/influxql/scanner.go +++ b/influxql/scanner.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "io" - "strings" ) // Scanner represents a lexical scanner for InfluxQL. @@ -238,21 +237,22 @@ func (s *Scanner) scanNumber() (tok Token, pos Pos, lit string) { _, _ = buf.WriteString(s.scanDigits()) // If next code points are a full stop and digit then consume them. + isDecimal := false if ch0, _ := s.r.read(); ch0 == '.' { + isDecimal = true if ch1, _ := s.r.read(); isDigit(ch1) { _, _ = buf.WriteRune(ch0) _, _ = buf.WriteRune(ch1) _, _ = buf.WriteString(s.scanDigits()) } else { s.r.unread() - s.r.unread() } } else { s.r.unread() } - // Attempt to read as a duration if it doesn't have a fractional part. - if !strings.Contains(buf.String(), ".") { + // Read as a duration or integer if it doesn't have a fractional part. + if !isDecimal { // If the next rune is a duration unit (u,µ,ms,s) then return a duration token if ch0, _ := s.r.read(); ch0 == 'u' || ch0 == 'µ' || ch0 == 's' || ch0 == 'h' || ch0 == 'd' || ch0 == 'w' { _, _ = buf.WriteRune(ch0) @@ -267,6 +267,7 @@ func (s *Scanner) scanNumber() (tok Token, pos Pos, lit string) { return DURATIONVAL, pos, buf.String() } s.r.unread() + return INTEGER, pos, buf.String() } return NUMBER, pos, buf.String() } diff --git a/influxql/scanner_test.go b/influxql/scanner_test.go index f437928e35a..ddade778b89 100644 --- a/influxql/scanner_test.go +++ b/influxql/scanner_test.go @@ -81,7 +81,8 @@ func TestScanner_Scan(t *testing.T) { {s: `'test\g'`, tok: influxql.BADESCAPE, lit: `\g`, pos: influxql.Pos{Line: 0, Char: 6}}, // Numbers - {s: `100`, tok: influxql.NUMBER, lit: `100`}, + {s: `100`, tok: influxql.INTEGER, lit: `100`}, + {s: `-100`, tok: influxql.INTEGER, lit: `-100`}, {s: `100.23`, tok: influxql.NUMBER, lit: `100.23`}, {s: `+100.23`, tok: influxql.NUMBER, lit: `+100.23`}, {s: `-100.23`, tok: influxql.NUMBER, lit: `-100.23`}, @@ -103,7 +104,7 @@ func TestScanner_Scan(t *testing.T) { {s: `10h`, tok: influxql.DURATIONVAL, lit: `10h`}, {s: `10d`, tok: influxql.DURATIONVAL, lit: `10d`}, {s: `10w`, tok: influxql.DURATIONVAL, lit: `10w`}, - {s: `10x`, tok: influxql.NUMBER, lit: `10`}, // non-duration unit + {s: `10x`, tok: influxql.INTEGER, lit: `10`}, // non-duration unit // Keywords {s: `ALL`, tok: influxql.ALL}, diff --git a/influxql/select.go b/influxql/select.go index 78ff48a4f4e..8af327c980d 100644 --- a/influxql/select.go +++ b/influxql/select.go @@ -286,7 +286,7 @@ func buildExprIterator(expr Expr, ic IteratorCreator, opt IteratorOptions) (Iter if err != nil { return nil, err } - n := expr.Args[len(expr.Args)-1].(*NumberLiteral) + n := expr.Args[len(expr.Args)-1].(*IntegerLiteral) return newTopIterator(input, opt, n, tags) case "bottom": var tags []int @@ -310,14 +310,20 @@ func buildExprIterator(expr Expr, ic IteratorCreator, opt IteratorOptions) (Iter if err != nil { return nil, err } - n := expr.Args[len(expr.Args)-1].(*NumberLiteral) + n := expr.Args[len(expr.Args)-1].(*IntegerLiteral) return newBottomIterator(input, opt, n, tags) case "percentile": input, err := buildExprIterator(expr.Args[0].(*VarRef), ic, opt) if err != nil { return nil, err } - percentile := expr.Args[1].(*NumberLiteral).Val + var percentile float64 + switch arg := expr.Args[1].(type) { + case *NumberLiteral: + percentile = arg.Val + case *IntegerLiteral: + percentile = float64(arg.Val) + } return newPercentileIterator(input, opt, percentile) default: return nil, fmt.Errorf("unsupported call: %s", expr.Name) @@ -389,9 +395,14 @@ func buildRHSTransformIterator(lhs Iterator, rhs Literal, op Token, ic IteratorC return nil, fmt.Errorf("type mismatch on LHS, unable to use %T as a FloatIterator", lhs) } - lit, ok := rhs.(*NumberLiteral) - if !ok { - return nil, fmt.Errorf("type mismatch on RHS, unable to use %T as a NumberLiteral", lhs) + var val float64 + switch rhs := rhs.(type) { + case *NumberLiteral: + val = rhs.Val + case *IntegerLiteral: + val = float64(rhs.Val) + default: + return nil, fmt.Errorf("type mismatch on RHS, unable to use %T as a NumberLiteral", rhs) } return &floatTransformIterator{ input: input, @@ -401,10 +412,44 @@ func buildRHSTransformIterator(lhs Iterator, rhs Literal, op Token, ic IteratorC } else if p.Nil { return p } - p.Value = fn(p.Value, lit.Val) + p.Value = fn(p.Value, val) return p }, }, nil + case func(int64, int64) float64: + input, ok := lhs.(IntegerIterator) + if !ok { + return nil, fmt.Errorf("type mismatch on LHS, unable to use %T as a IntegerIterator", lhs) + } + + var val int64 + switch rhs := rhs.(type) { + case *IntegerLiteral: + val = rhs.Val + default: + return nil, fmt.Errorf("type mismatch on RHS, unable to use %T as a IntegerLiteral", rhs) + } + return &integerFloatTransformIterator{ + input: input, + fn: func(p *IntegerPoint) *FloatPoint { + if p == nil { + return nil + } + + fp := &FloatPoint{ + Name: p.Name, + Tags: p.Tags, + Time: p.Time, + Aux: p.Aux, + } + if p.Nil { + fp.Nil = true + } else { + fp.Value = fn(p.Value, val) + } + return fp + }, + }, nil case func(float64, float64) bool: var input FloatIterator switch lhs := lhs.(type) { @@ -416,9 +461,14 @@ func buildRHSTransformIterator(lhs Iterator, rhs Literal, op Token, ic IteratorC return nil, fmt.Errorf("type mismatch on LHS, unable to use %T as a FloatIterator", lhs) } - lit, ok := rhs.(*NumberLiteral) - if !ok { - return nil, fmt.Errorf("type mismatch on RHS, unable to use %T as a NumberLiteral", lhs) + var val float64 + switch rhs := rhs.(type) { + case *NumberLiteral: + val = rhs.Val + case *IntegerLiteral: + val = float64(rhs.Val) + default: + return nil, fmt.Errorf("type mismatch on RHS, unable to use %T as a NumberLiteral", rhs) } return &floatBoolTransformIterator{ input: input, @@ -436,7 +486,72 @@ func buildRHSTransformIterator(lhs Iterator, rhs Literal, op Token, ic IteratorC if p.Nil { bp.Nil = true } else { - bp.Value = fn(p.Value, lit.Val) + bp.Value = fn(p.Value, val) + } + return bp + }, + }, nil + case func(int64, int64) int64: + var input IntegerIterator + switch lhs := lhs.(type) { + case IntegerIterator: + input = lhs + default: + return nil, fmt.Errorf("type mismatch on LHS, unable to use %T as an IntegerIterator", lhs) + } + + var val int64 + switch rhs := rhs.(type) { + case *IntegerLiteral: + val = rhs.Val + default: + return nil, fmt.Errorf("type mismatch on RHS, unable to use %T as an IntegerLiteral", rhs) + } + return &integerTransformIterator{ + input: input, + fn: func(p *IntegerPoint) *IntegerPoint { + if p == nil { + return nil + } else if p.Nil { + return p + } + p.Value = fn(p.Value, val) + return p + }, + }, nil + case func(int64, int64) bool: + var input IntegerIterator + switch lhs := lhs.(type) { + case IntegerIterator: + input = lhs + default: + return nil, fmt.Errorf("type mismatch on LHS, unable to use %T as an IntegerIterator", lhs) + } + + var val int64 + switch rhs := rhs.(type) { + case *IntegerLiteral: + val = rhs.Val + default: + return nil, fmt.Errorf("type mismatch on RHS, unable to use %T as an IntegerLiteral", rhs) + } + return &integerBoolTransformIterator{ + input: input, + fn: func(p *IntegerPoint) *BooleanPoint { + if p == nil { + return nil + } + + bp := &BooleanPoint{ + Name: p.Name, + Tags: p.Tags, + Time: p.Time, + Aux: p.Aux, + } + if p.Nil { + bp.Nil = true + } else { + bp.Value = fn(p.Value, val) } return bp }, @@ -459,8 +574,13 @@ func buildLHSTransformIterator(lhs Literal, rhs Iterator, op Token, ic IteratorC return nil, fmt.Errorf("type mismatch on RHS, unable to use %T as a FloatIterator", rhs) } - lit, ok := lhs.(*NumberLiteral) - if !ok { + var val float64 + switch lhs := lhs.(type) { + case *NumberLiteral: + val = lhs.Val + case *IntegerLiteral: + val = float64(lhs.Val) + default: return nil, fmt.Errorf("type mismatch on LHS, unable to use %T as a NumberLiteral", lhs) } return &floatTransformIterator{ @@ -471,10 +591,44 @@ func buildLHSTransformIterator(lhs Literal, rhs Iterator, op Token, ic IteratorC } else if p.Nil { return p } - p.Value = fn(lit.Val, p.Value) + p.Value = fn(val, p.Value) return p }, }, nil + case func(int64, int64) float64: + input, ok := rhs.(IntegerIterator) + if !ok { + return nil, fmt.Errorf("type mismatch on RHS, unable to use %T as a IntegerIterator", lhs) + } + + var val int64 + switch lhs := lhs.(type) { + case *IntegerLiteral: + val = lhs.Val + default: + return nil, fmt.Errorf("type mismatch on LHS, unable to use %T as a IntegerLiteral", rhs) + } + return &integerFloatTransformIterator{ + input: input, + fn: func(p *IntegerPoint) *FloatPoint { + if p == nil { + return nil + } + + fp := &FloatPoint{ + Name: p.Name, + Tags: p.Tags, + Time: p.Time, + Aux: p.Aux, + } + if p.Nil { + fp.Nil = true + } else { + fp.Value = fn(val, p.Value) + } + return fp + }, + }, nil case func(float64, float64) bool: var input FloatIterator switch rhs := rhs.(type) { @@ -486,8 +640,13 @@ func buildLHSTransformIterator(lhs Literal, rhs Iterator, op Token, ic IteratorC return nil, fmt.Errorf("type mismatch on RHS, unable to use %T as a FloatIterator", rhs) } - lit, ok := lhs.(*NumberLiteral) - if !ok { + var val float64 + switch lhs := lhs.(type) { + case *NumberLiteral: + val = lhs.Val + case *IntegerLiteral: + val = float64(lhs.Val) + default: return nil, fmt.Errorf("type mismatch on LHS, unable to use %T as a NumberLiteral", lhs) } return &floatBoolTransformIterator{ @@ -506,7 +665,72 @@ func buildLHSTransformIterator(lhs Literal, rhs Iterator, op Token, ic IteratorC if p.Nil { bp.Nil = true } else { - bp.Value = fn(lit.Val, p.Value) + bp.Value = fn(val, p.Value) + } + return bp + }, + }, nil + case func(int64, int64) int64: + var input IntegerIterator + switch rhs := rhs.(type) { + case IntegerIterator: + input = rhs + default: + return nil, fmt.Errorf("type mismatch on RHS, unable to use %T as an IntegerIterator", rhs) + } + + var val int64 + switch lhs := lhs.(type) { + case *IntegerLiteral: + val = lhs.Val + default: + return nil, fmt.Errorf("type mismatch on LHS, unable to use %T as an IntegerLiteral", lhs) + } + return &integerTransformIterator{ + input: input, + fn: func(p *IntegerPoint) *IntegerPoint { + if p == nil { + return nil + } else if p.Nil { + return p + } + p.Value = fn(val, p.Value) + return p + }, + }, nil + case func(int64, int64) bool: + var input IntegerIterator + switch rhs := rhs.(type) { + case IntegerIterator: + input = rhs + default: + return nil, fmt.Errorf("type mismatch on RHS, unable to use %T as an IntegerIterator", rhs) + } + + var val int64 + switch lhs := lhs.(type) { + case *IntegerLiteral: + val = lhs.Val + default: + return nil, fmt.Errorf("type mismatch on LHS, unable to use %T as an IntegerLiteral", lhs) + } + return &integerBoolTransformIterator{ + input: input, + fn: func(p *IntegerPoint) *BooleanPoint { + if p == nil { + return nil + } + + bp := &BooleanPoint{ + Name: p.Name, + Tags: p.Tags, + Time: p.Time, + Aux: p.Aux, + } + if p.Nil { + bp.Nil = true + } else { + bp.Value = fn(val, p.Value) } return bp }, @@ -710,6 +934,8 @@ func literalDataType(lit Literal) DataType { switch lit.(type) { case *NumberLiteral: return Float + case *IntegerLiteral: + return Integer case *StringLiteral: return String case *BooleanLiteral: diff --git a/influxql/select_test.go b/influxql/select_test.go index 62a12143388..d59ac05f2d6 100644 --- a/influxql/select_test.go +++ b/influxql/select_test.go @@ -1250,7 +1250,16 @@ func TestSelect_BinaryExpr_Float(t *testing.T) { Points [][]influxql.Point }{ { - Name: "rhs binary add", + Name: "rhs binary add number", + Statement: `SELECT value + 2.0 FROM cpu`, + Points: [][]influxql.Point{ + {&influxql.FloatPoint{Name: "cpu", Time: 0 * Second, Value: 22}}, + {&influxql.FloatPoint{Name: "cpu", Time: 5 * Second, Value: 12}}, + {&influxql.FloatPoint{Name: "cpu", Time: 9 * Second, Value: 21}}, + }, + }, + { + Name: "rhs binary add integer", Statement: `SELECT value + 2 FROM cpu`, Points: [][]influxql.Point{ {&influxql.FloatPoint{Name: "cpu", Time: 0 * Second, Value: 22}}, @@ -1259,7 +1268,16 @@ func TestSelect_BinaryExpr_Float(t *testing.T) { }, }, { - Name: "lhs binary add", + Name: "lhs binary add number", + Statement: `SELECT 2.0 + value FROM cpu`, + Points: [][]influxql.Point{ + {&influxql.FloatPoint{Name: "cpu", Time: 0 * Second, Value: 22}}, + {&influxql.FloatPoint{Name: "cpu", Time: 5 * Second, Value: 12}}, + {&influxql.FloatPoint{Name: "cpu", Time: 9 * Second, Value: 21}}, + }, + }, + { + Name: "lhs binary add integer", Statement: `SELECT 2 + value FROM cpu`, Points: [][]influxql.Point{ {&influxql.FloatPoint{Name: "cpu", Time: 0 * Second, Value: 22}}, @@ -1277,7 +1295,16 @@ func TestSelect_BinaryExpr_Float(t *testing.T) { }, }, { - Name: "rhs binary multiply", + Name: "rhs binary multiply number", + Statement: `SELECT value * 2.0 FROM cpu`, + Points: [][]influxql.Point{ + {&influxql.FloatPoint{Name: "cpu", Time: 0 * Second, Value: 40}}, + {&influxql.FloatPoint{Name: "cpu", Time: 5 * Second, Value: 20}}, + {&influxql.FloatPoint{Name: "cpu", Time: 9 * Second, Value: 38}}, + }, + }, + { + Name: "rhs binary multiply integer", Statement: `SELECT value * 2 FROM cpu`, Points: [][]influxql.Point{ {&influxql.FloatPoint{Name: "cpu", Time: 0 * Second, Value: 40}}, @@ -1286,7 +1313,16 @@ func TestSelect_BinaryExpr_Float(t *testing.T) { }, }, { - Name: "lhs binary multiply", + Name: "lhs binary multiply number", + Statement: `SELECT 2.0 * value FROM cpu`, + Points: [][]influxql.Point{ + {&influxql.FloatPoint{Name: "cpu", Time: 0 * Second, Value: 40}}, + {&influxql.FloatPoint{Name: "cpu", Time: 5 * Second, Value: 20}}, + {&influxql.FloatPoint{Name: "cpu", Time: 9 * Second, Value: 38}}, + }, + }, + { + Name: "lhs binary multiply integer", Statement: `SELECT 2 * value FROM cpu`, Points: [][]influxql.Point{ {&influxql.FloatPoint{Name: "cpu", Time: 0 * Second, Value: 40}}, @@ -1304,7 +1340,16 @@ func TestSelect_BinaryExpr_Float(t *testing.T) { }, }, { - Name: "rhs binary subtract", + Name: "rhs binary subtract number", + Statement: `SELECT value - 2.0 FROM cpu`, + Points: [][]influxql.Point{ + {&influxql.FloatPoint{Name: "cpu", Time: 0 * Second, Value: 18}}, + {&influxql.FloatPoint{Name: "cpu", Time: 5 * Second, Value: 8}}, + {&influxql.FloatPoint{Name: "cpu", Time: 9 * Second, Value: 17}}, + }, + }, + { + Name: "rhs binary subtract integer", Statement: `SELECT value - 2 FROM cpu`, Points: [][]influxql.Point{ {&influxql.FloatPoint{Name: "cpu", Time: 0 * Second, Value: 18}}, @@ -1313,7 +1358,16 @@ func TestSelect_BinaryExpr_Float(t *testing.T) { }, }, { - Name: "lhs binary subtract", + Name: "lhs binary subtract number", + Statement: `SELECT 2.0 - value FROM cpu`, + Points: [][]influxql.Point{ + {&influxql.FloatPoint{Name: "cpu", Time: 0 * Second, Value: -18}}, + {&influxql.FloatPoint{Name: "cpu", Time: 5 * Second, Value: -8}}, + {&influxql.FloatPoint{Name: "cpu", Time: 9 * Second, Value: -17}}, + }, + }, + { + Name: "lhs binary subtract integer", Statement: `SELECT 2 - value FROM cpu`, Points: [][]influxql.Point{ {&influxql.FloatPoint{Name: "cpu", Time: 0 * Second, Value: -18}}, @@ -1331,7 +1385,16 @@ func TestSelect_BinaryExpr_Float(t *testing.T) { }, }, { - Name: "rhs binary division", + Name: "rhs binary division number", + Statement: `SELECT value / 2.0 FROM cpu`, + Points: [][]influxql.Point{ + {&influxql.FloatPoint{Name: "cpu", Time: 0 * Second, Value: 10}}, + {&influxql.FloatPoint{Name: "cpu", Time: 5 * Second, Value: 5}}, + {&influxql.FloatPoint{Name: "cpu", Time: 9 * Second, Value: float64(19) / 2}}, + }, + }, + { + Name: "rhs binary division integer", Statement: `SELECT value / 2 FROM cpu`, Points: [][]influxql.Point{ {&influxql.FloatPoint{Name: "cpu", Time: 0 * Second, Value: 10}}, @@ -1340,7 +1403,16 @@ func TestSelect_BinaryExpr_Float(t *testing.T) { }, }, { - Name: "lhs binary division", + Name: "lhs binary division number", + Statement: `SELECT 38.0 / value FROM cpu`, + Points: [][]influxql.Point{ + {&influxql.FloatPoint{Name: "cpu", Time: 0 * Second, Value: 1.9}}, + {&influxql.FloatPoint{Name: "cpu", Time: 5 * Second, Value: 3.8}}, + {&influxql.FloatPoint{Name: "cpu", Time: 9 * Second, Value: 2}}, + }, + }, + { + Name: "lhs binary division integer", Statement: `SELECT 38 / value FROM cpu`, Points: [][]influxql.Point{ {&influxql.FloatPoint{Name: "cpu", Time: 0 * Second, Value: 1.9}}, @@ -1391,8 +1463,8 @@ func TestSelect_BinaryExpr_Integer(t *testing.T) { Points [][]influxql.Point }{ { - Name: "rhs binary add", - Statement: `SELECT value + 2 FROM cpu`, + Name: "rhs binary add number", + Statement: `SELECT value + 2.0 FROM cpu`, Points: [][]influxql.Point{ {&influxql.FloatPoint{Name: "cpu", Time: 0 * Second, Value: 22}}, {&influxql.FloatPoint{Name: "cpu", Time: 5 * Second, Value: 12}}, @@ -1400,14 +1472,32 @@ func TestSelect_BinaryExpr_Integer(t *testing.T) { }, }, { - Name: "lhs binary add", - Statement: `SELECT 2 + value FROM cpu`, + Name: "rhs binary add integer", + Statement: `SELECT value + 2 FROM cpu`, + Points: [][]influxql.Point{ + {&influxql.IntegerPoint{Name: "cpu", Time: 0 * Second, Value: 22}}, + {&influxql.IntegerPoint{Name: "cpu", Time: 5 * Second, Value: 12}}, + {&influxql.IntegerPoint{Name: "cpu", Time: 9 * Second, Value: 21}}, + }, + }, + { + Name: "lhs binary add number", + Statement: `SELECT 2.0 + value FROM cpu`, Points: [][]influxql.Point{ {&influxql.FloatPoint{Name: "cpu", Time: 0 * Second, Value: 22}}, {&influxql.FloatPoint{Name: "cpu", Time: 5 * Second, Value: 12}}, {&influxql.FloatPoint{Name: "cpu", Time: 9 * Second, Value: 21}}, }, }, + { + Name: "lhs binary add integer", + Statement: `SELECT 2 + value FROM cpu`, + Points: [][]influxql.Point{ + {&influxql.IntegerPoint{Name: "cpu", Time: 0 * Second, Value: 22}}, + {&influxql.IntegerPoint{Name: "cpu", Time: 5 * Second, Value: 12}}, + {&influxql.IntegerPoint{Name: "cpu", Time: 9 * Second, Value: 21}}, + }, + }, { Name: "two variable binary add", Statement: `SELECT value + value FROM cpu`, @@ -1418,8 +1508,8 @@ func TestSelect_BinaryExpr_Integer(t *testing.T) { }, }, { - Name: "rhs binary multiply", - Statement: `SELECT value * 2 FROM cpu`, + Name: "rhs binary multiply number", + Statement: `SELECT value * 2.0 FROM cpu`, Points: [][]influxql.Point{ {&influxql.FloatPoint{Name: "cpu", Time: 0 * Second, Value: 40}}, {&influxql.FloatPoint{Name: "cpu", Time: 5 * Second, Value: 20}}, @@ -1427,14 +1517,32 @@ func TestSelect_BinaryExpr_Integer(t *testing.T) { }, }, { - Name: "lhs binary multiply", - Statement: `SELECT 2 * value FROM cpu`, + Name: "rhs binary multiply integer", + Statement: `SELECT value * 2 FROM cpu`, + Points: [][]influxql.Point{ + {&influxql.IntegerPoint{Name: "cpu", Time: 0 * Second, Value: 40}}, + {&influxql.IntegerPoint{Name: "cpu", Time: 5 * Second, Value: 20}}, + {&influxql.IntegerPoint{Name: "cpu", Time: 9 * Second, Value: 38}}, + }, + }, + { + Name: "lhs binary multiply number", + Statement: `SELECT 2.0 * value FROM cpu`, Points: [][]influxql.Point{ {&influxql.FloatPoint{Name: "cpu", Time: 0 * Second, Value: 40}}, {&influxql.FloatPoint{Name: "cpu", Time: 5 * Second, Value: 20}}, {&influxql.FloatPoint{Name: "cpu", Time: 9 * Second, Value: 38}}, }, }, + { + Name: "lhs binary multiply integer", + Statement: `SELECT 2 * value FROM cpu`, + Points: [][]influxql.Point{ + {&influxql.IntegerPoint{Name: "cpu", Time: 0 * Second, Value: 40}}, + {&influxql.IntegerPoint{Name: "cpu", Time: 5 * Second, Value: 20}}, + {&influxql.IntegerPoint{Name: "cpu", Time: 9 * Second, Value: 38}}, + }, + }, { Name: "two variable binary multiply", Statement: `SELECT value * value FROM cpu`, @@ -1445,8 +1553,8 @@ func TestSelect_BinaryExpr_Integer(t *testing.T) { }, }, { - Name: "rhs binary subtract", - Statement: `SELECT value - 2 FROM cpu`, + Name: "rhs binary subtract number", + Statement: `SELECT value - 2.0 FROM cpu`, Points: [][]influxql.Point{ {&influxql.FloatPoint{Name: "cpu", Time: 0 * Second, Value: 18}}, {&influxql.FloatPoint{Name: "cpu", Time: 5 * Second, Value: 8}}, @@ -1454,14 +1562,32 @@ func TestSelect_BinaryExpr_Integer(t *testing.T) { }, }, { - Name: "lhs binary subtract", - Statement: `SELECT 2 - value FROM cpu`, + Name: "rhs binary subtract integer", + Statement: `SELECT value - 2 FROM cpu`, + Points: [][]influxql.Point{ + {&influxql.IntegerPoint{Name: "cpu", Time: 0 * Second, Value: 18}}, + {&influxql.IntegerPoint{Name: "cpu", Time: 5 * Second, Value: 8}}, + {&influxql.IntegerPoint{Name: "cpu", Time: 9 * Second, Value: 17}}, + }, + }, + { + Name: "lhs binary subtract number", + Statement: `SELECT 2.0 - value FROM cpu`, Points: [][]influxql.Point{ {&influxql.FloatPoint{Name: "cpu", Time: 0 * Second, Value: -18}}, {&influxql.FloatPoint{Name: "cpu", Time: 5 * Second, Value: -8}}, {&influxql.FloatPoint{Name: "cpu", Time: 9 * Second, Value: -17}}, }, }, + { + Name: "lhs binary subtract integer", + Statement: `SELECT 2 - value FROM cpu`, + Points: [][]influxql.Point{ + {&influxql.IntegerPoint{Name: "cpu", Time: 0 * Second, Value: -18}}, + {&influxql.IntegerPoint{Name: "cpu", Time: 5 * Second, Value: -8}}, + {&influxql.IntegerPoint{Name: "cpu", Time: 9 * Second, Value: -17}}, + }, + }, { Name: "two variable binary subtract", Statement: `SELECT value - value FROM cpu`, @@ -1472,8 +1598,8 @@ func TestSelect_BinaryExpr_Integer(t *testing.T) { }, }, { - Name: "rhs binary division", - Statement: `SELECT value / 2 FROM cpu`, + Name: "rhs binary division number", + Statement: `SELECT value / 2.0 FROM cpu`, Points: [][]influxql.Point{ {&influxql.FloatPoint{Name: "cpu", Time: 0 * Second, Value: 10}}, {&influxql.FloatPoint{Name: "cpu", Time: 5 * Second, Value: 5}}, @@ -1481,14 +1607,32 @@ func TestSelect_BinaryExpr_Integer(t *testing.T) { }, }, { - Name: "lhs binary division", - Statement: `SELECT 38 / value FROM cpu`, + Name: "rhs binary division integer", + Statement: `SELECT value / 2 FROM cpu`, + Points: [][]influxql.Point{ + {&influxql.FloatPoint{Name: "cpu", Time: 0 * Second, Value: 10}}, + {&influxql.FloatPoint{Name: "cpu", Time: 5 * Second, Value: 5}}, + {&influxql.FloatPoint{Name: "cpu", Time: 9 * Second, Value: float64(19) / 2}}, + }, + }, + { + Name: "lhs binary division number", + Statement: `SELECT 38.0 / value FROM cpu`, Points: [][]influxql.Point{ {&influxql.FloatPoint{Name: "cpu", Time: 0 * Second, Value: 1.9}}, {&influxql.FloatPoint{Name: "cpu", Time: 5 * Second, Value: 3.8}}, {&influxql.FloatPoint{Name: "cpu", Time: 9 * Second, Value: 2.0}}, }, }, + { + Name: "lhs binary division integer", + Statement: `SELECT 38 / value FROM cpu`, + Points: [][]influxql.Point{ + {&influxql.FloatPoint{Name: "cpu", Time: 0 * Second, Value: 1.9}}, + {&influxql.FloatPoint{Name: "cpu", Time: 5 * Second, Value: 3.8}}, + {&influxql.FloatPoint{Name: "cpu", Time: 9 * Second, Value: 2}}, + }, + }, { Name: "two variable binary division", Statement: `SELECT value / value FROM cpu`, diff --git a/influxql/token.go b/influxql/token.go index f26a565690b..bde1a0dae3d 100644 --- a/influxql/token.go +++ b/influxql/token.go @@ -18,6 +18,7 @@ const ( // IDENT and the following are InfluxQL literal tokens. IDENT // main NUMBER // 12345.67 + INTEGER // 12345 DURATIONVAL // 13h STRING // "abc" BADSTRING // "abc