Skip to content

Commit

Permalink
Support integer literals in the query language
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jsternberg committed Mar 3, 2016
1 parent 6e34679 commit d9f795a
Show file tree
Hide file tree
Showing 12 changed files with 677 additions and 186 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
- [#5691](https://github.com/influxdata/influxdb/pull/5691): Remove associated shard data when retention policies are dropped.
- [#5758](https://github.com/influxdata/influxdb/pull/5758): TSM engine stats for cache, WAL, and filestore. Thanks @jonseymour
- [#5844](https://github.com/influxdata/influxdb/pull/5844): Tag TSM engine stats with database and retention policy
- [#5744](https://github.com/influxdata/influxdb/issues/5744): Add integer literal support to the query language.

### Bugfixes

Expand Down
2 changes: 1 addition & 1 deletion cmd/influxd/run/server_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
255 changes: 201 additions & 54 deletions influxql/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,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() {}
Expand Down Expand Up @@ -250,6 +251,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() {}
Expand All @@ -267,6 +269,7 @@ type Literal interface {

func (*BooleanLiteral) literal() {}
func (*DurationLiteral) literal() {}
func (*IntegerLiteral) literal() {}
func (*nilLiteral) literal() {}
func (*NumberLiteral) literal() {}
func (*RegexLiteral) literal() {}
Expand Down Expand Up @@ -1360,7 +1363,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])
}
Expand All @@ -1386,8 +1389,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
Expand Down Expand Up @@ -2959,6 +2965,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
Expand Down Expand Up @@ -3115,6 +3129,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:
Expand Down Expand Up @@ -3258,6 +3274,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{}
Expand Down Expand Up @@ -3451,6 +3469,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:
Expand Down Expand Up @@ -3483,61 +3503,114 @@ func evalBinaryExpr(expr *BinaryExpr, m map[string]interface{}) interface{} {
return lhs != rhs
}
case float64:
rhs, _ := rhs.(float64)
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)
switch rhs := rhs.(type) {
case float64:
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
}
case int64:
switch expr.Op {
case EQ:
return lhs == float64(rhs)
case NEQ:
return lhs != float64(rhs)
case LT:
return lhs < float64(rhs)
case LTE:
return lhs <= float64(rhs)
case GT:
return lhs > float64(rhs)
case GTE:
return lhs >= float64(rhs)
case ADD:
return lhs + float64(rhs)
case SUB:
return lhs - float64(rhs)
case MUL:
return lhs * float64(rhs)
case DIV:
if rhs == 0 {
return float64(0)
}
return lhs / float64(rhs)
}
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)
switch rhs := rhs.(type) {
case float64:
switch expr.Op {
case EQ:
return float64(lhs) == rhs
case NEQ:
return float64(lhs) != rhs
case LT:
return float64(lhs) < rhs
case LTE:
return float64(lhs) <= rhs
case GT:
return float64(lhs) > rhs
case GTE:
return float64(lhs) >= rhs
case ADD:
return float64(lhs) + rhs
case SUB:
return float64(lhs) - rhs
case MUL:
return float64(lhs) * rhs
case DIV:
if rhs == 0 {
return float64(0)
}
return float64(lhs) / rhs
}
case int64:
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 float64(lhs) / float64(rhs)
}
return lhs / rhs
}
case string:
rhs, _ := rhs.(string)
Expand Down Expand Up @@ -3626,6 +3699,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:
Expand Down Expand Up @@ -3689,6 +3764,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:
Expand All @@ -3700,6 +3785,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:
Expand Down Expand Up @@ -3736,6 +3857,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}
}
Expand Down
Loading

0 comments on commit d9f795a

Please sign in to comment.