Support unsigned binary math in fields

Field math works similar to condition evaluation, but not the exact same
because we have more information to work with in field expressions than
we do in conditional math because fields retain the information about
their source while conditions do not.

The main difference is that you cannot add an unsigned literal to the
output of an integer iterator while you can inside of a condition. You
can perform math on a positive integer literal to an unsigned iterator.
Inside of the condition, we aren't sure if an integer is because of a
literal or because of an iterator so we can't make that distinction.
pull/8902/head
Jonathan A. Sternberg 2017-10-02 12:31:07 -05:00
parent 8328bc1e04
commit 79092610c8
3 changed files with 1028 additions and 367 deletions

View File

@ -1356,6 +1356,7 @@ type integerFloatTransformFunc func(p *IntegerPoint) *FloatPoint
type integerFloatCastIterator struct {
input IntegerIterator
point FloatPoint
}
func (itr *integerFloatCastIterator) Stats() IteratorStats { return itr.input.Stats() }
@ -1366,14 +1367,35 @@ func (itr *integerFloatCastIterator) Next() (*FloatPoint, error) {
return nil, err
}
return &FloatPoint{
Name: p.Name,
Tags: p.Tags,
Time: p.Time,
Nil: p.Nil,
Value: float64(p.Value),
Aux: p.Aux,
}, nil
itr.point.Name = p.Name
itr.point.Tags = p.Tags
itr.point.Time = p.Time
itr.point.Nil = p.Nil
itr.point.Value = float64(p.Value)
itr.point.Aux = p.Aux
return &itr.point, nil
}
type unsignedFloatCastIterator struct {
input UnsignedIterator
point FloatPoint
}
func (itr *unsignedFloatCastIterator) Stats() IteratorStats { return itr.input.Stats() }
func (itr *unsignedFloatCastIterator) Close() error { return itr.input.Close() }
func (itr *unsignedFloatCastIterator) Next() (*FloatPoint, error) {
p, err := itr.input.Next()
if p == nil || err != nil {
return nil, err
}
itr.point.Name = p.Name
itr.point.Tags = p.Tags
itr.point.Time = p.Time
itr.point.Nil = p.Nil
itr.point.Value = float64(p.Value)
itr.point.Aux = p.Aux
return &itr.point, nil
}
// IteratorStats represents statistics about an iterator.

View File

@ -810,6 +810,13 @@ func (b *exprIteratorBuilder) callIterator(expr *influxql.Call, opt IteratorOpti
}
func buildRHSTransformIterator(lhs Iterator, rhs influxql.Literal, op influxql.Token, opt IteratorOptions) (Iterator, error) {
itrType, litType := iteratorDataType(lhs), literalDataType(rhs)
if litType == influxql.Unsigned && itrType == influxql.Integer {
// If the literal is unsigned but the iterator is an integer, return
// an error since we cannot add an unsigned to an integer.
return nil, fmt.Errorf("cannot use %s with an integer and unsigned", op)
}
fn := binaryExprFunc(iteratorDataType(lhs), literalDataType(rhs), op)
switch fn := fn.(type) {
case func(float64, float64) float64:
@ -819,6 +826,8 @@ func buildRHSTransformIterator(lhs Iterator, rhs influxql.Literal, op influxql.T
input = lhs
case IntegerIterator:
input = &integerFloatCastIterator{input: lhs}
case UnsignedIterator:
input = &unsignedFloatCastIterator{input: lhs}
default:
return nil, fmt.Errorf("type mismatch on LHS, unable to use %T as a FloatIterator", lhs)
}
@ -829,6 +838,8 @@ func buildRHSTransformIterator(lhs Iterator, rhs influxql.Literal, op influxql.T
val = rhs.Val
case *influxql.IntegerLiteral:
val = float64(rhs.Val)
case *influxql.UnsignedLiteral:
val = float64(rhs.Val)
default:
return nil, fmt.Errorf("type mismatch on RHS, unable to use %T as a NumberLiteral", rhs)
}
@ -885,6 +896,8 @@ func buildRHSTransformIterator(lhs Iterator, rhs influxql.Literal, op influxql.T
input = lhs
case IntegerIterator:
input = &integerFloatCastIterator{input: lhs}
case UnsignedIterator:
input = &unsignedFloatCastIterator{input: lhs}
default:
return nil, fmt.Errorf("type mismatch on LHS, unable to use %T as a FloatIterator", lhs)
}
@ -895,6 +908,8 @@ func buildRHSTransformIterator(lhs Iterator, rhs influxql.Literal, op influxql.T
val = rhs.Val
case *influxql.IntegerLiteral:
val = float64(rhs.Val)
case *influxql.UnsignedLiteral:
val = float64(rhs.Val)
default:
return nil, fmt.Errorf("type mismatch on RHS, unable to use %T as a NumberLiteral", rhs)
}
@ -970,6 +985,81 @@ func buildRHSTransformIterator(lhs Iterator, rhs influxql.Literal, op influxql.T
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
},
}, nil
case func(uint64, uint64) uint64:
var input UnsignedIterator
switch lhs := lhs.(type) {
case UnsignedIterator:
input = lhs
default:
return nil, fmt.Errorf("type mismatch on LHS, unable to use %T as a FloatIterator", lhs)
}
var val uint64
switch rhs := rhs.(type) {
case *influxql.IntegerLiteral:
if rhs.Val < 0 {
return nil, fmt.Errorf("cannot use negative integer '%s' in math with unsigned", rhs)
}
val = uint64(rhs.Val)
case *influxql.UnsignedLiteral:
val = rhs.Val
default:
return nil, fmt.Errorf("type mismatch on RHS, unable to use %T as a NumberLiteral", rhs)
}
return &unsignedTransformIterator{
input: input,
fn: func(p *UnsignedPoint) *UnsignedPoint {
if p == nil {
return nil
} else if p.Nil {
return p
}
p.Value = fn(p.Value, val)
return p
},
}, nil
case func(uint64, uint64) bool:
var input UnsignedIterator
switch lhs := lhs.(type) {
case UnsignedIterator:
input = lhs
default:
return nil, fmt.Errorf("type mismatch on LHS, unable to use %T as a FloatIterator", lhs)
}
var val uint64
switch rhs := rhs.(type) {
case *influxql.IntegerLiteral:
if rhs.Val < 0 {
return nil, fmt.Errorf("cannot use negative integer '%s' in math with unsigned", rhs)
}
val = uint64(rhs.Val)
case *influxql.UnsignedLiteral:
val = rhs.Val
default:
return nil, fmt.Errorf("type mismatch on RHS, unable to use %T as a NumberLiteral", rhs)
}
return &unsignedBoolTransformIterator{
input: input,
fn: func(p *UnsignedPoint) *BooleanPoint {
if p == nil {
return nil
}
bp := &BooleanPoint{
Name: p.Name,
Tags: p.Tags,
@ -1026,7 +1116,14 @@ func buildRHSTransformIterator(lhs Iterator, rhs influxql.Literal, op influxql.T
}
func buildLHSTransformIterator(lhs influxql.Literal, rhs Iterator, op influxql.Token, opt IteratorOptions) (Iterator, error) {
fn := binaryExprFunc(literalDataType(lhs), iteratorDataType(rhs), op)
litType, itrType := literalDataType(lhs), iteratorDataType(rhs)
if litType == influxql.Unsigned && itrType == influxql.Integer {
// If the literal is unsigned but the iterator is an integer, return
// an error since we cannot add an unsigned to an integer.
return nil, fmt.Errorf("cannot use %s with unsigned and an integer", op)
}
fn := binaryExprFunc(litType, itrType, op)
switch fn := fn.(type) {
case func(float64, float64) float64:
var input FloatIterator
@ -1035,6 +1132,8 @@ func buildLHSTransformIterator(lhs influxql.Literal, rhs Iterator, op influxql.T
input = rhs
case IntegerIterator:
input = &integerFloatCastIterator{input: rhs}
case UnsignedIterator:
input = &unsignedFloatCastIterator{input: rhs}
default:
return nil, fmt.Errorf("type mismatch on RHS, unable to use %T as a FloatIterator", rhs)
}
@ -1045,6 +1144,8 @@ func buildLHSTransformIterator(lhs influxql.Literal, rhs Iterator, op influxql.T
val = lhs.Val
case *influxql.IntegerLiteral:
val = float64(lhs.Val)
case *influxql.UnsignedLiteral:
val = float64(lhs.Val)
default:
return nil, fmt.Errorf("type mismatch on LHS, unable to use %T as a NumberLiteral", lhs)
}
@ -1101,6 +1202,8 @@ func buildLHSTransformIterator(lhs influxql.Literal, rhs Iterator, op influxql.T
input = rhs
case IntegerIterator:
input = &integerFloatCastIterator{input: rhs}
case UnsignedIterator:
input = &unsignedFloatCastIterator{input: rhs}
default:
return nil, fmt.Errorf("type mismatch on RHS, unable to use %T as a FloatIterator", rhs)
}
@ -1111,6 +1214,8 @@ func buildLHSTransformIterator(lhs influxql.Literal, rhs Iterator, op influxql.T
val = lhs.Val
case *influxql.IntegerLiteral:
val = float64(lhs.Val)
case *influxql.UnsignedLiteral:
val = float64(lhs.Val)
default:
return nil, fmt.Errorf("type mismatch on LHS, unable to use %T as a NumberLiteral", lhs)
}
@ -1186,6 +1291,81 @@ func buildLHSTransformIterator(lhs influxql.Literal, rhs Iterator, op influxql.T
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
},
}, nil
case func(uint64, uint64) uint64:
var input UnsignedIterator
switch rhs := rhs.(type) {
case UnsignedIterator:
input = rhs
default:
return nil, fmt.Errorf("type mismatch on LHS, unable to use %T as a FloatIterator", lhs)
}
var val uint64
switch lhs := lhs.(type) {
case *influxql.IntegerLiteral:
if lhs.Val < 0 {
return nil, fmt.Errorf("cannot use negative integer '%s' in math with unsigned", rhs)
}
val = uint64(lhs.Val)
case *influxql.UnsignedLiteral:
val = lhs.Val
default:
return nil, fmt.Errorf("type mismatch on RHS, unable to use %T as a NumberLiteral", rhs)
}
return &unsignedTransformIterator{
input: input,
fn: func(p *UnsignedPoint) *UnsignedPoint {
if p == nil {
return nil
} else if p.Nil {
return p
}
p.Value = fn(val, p.Value)
return p
},
}, nil
case func(uint64, uint64) bool:
var input UnsignedIterator
switch rhs := rhs.(type) {
case UnsignedIterator:
input = rhs
default:
return nil, fmt.Errorf("type mismatch on LHS, unable to use %T as a FloatIterator", lhs)
}
var val uint64
switch lhs := lhs.(type) {
case *influxql.IntegerLiteral:
if lhs.Val < 0 {
return nil, fmt.Errorf("cannot use negative integer '%s' in math with unsigned", rhs)
}
val = uint64(lhs.Val)
case *influxql.UnsignedLiteral:
val = lhs.Val
default:
return nil, fmt.Errorf("type mismatch on RHS, unable to use %T as a NumberLiteral", rhs)
}
return &unsignedBoolTransformIterator{
input: input,
fn: func(p *UnsignedPoint) *BooleanPoint {
if p == nil {
return nil
}
bp := &BooleanPoint{
Name: p.Name,
Tags: p.Tags,
@ -1242,7 +1422,14 @@ func buildLHSTransformIterator(lhs influxql.Literal, rhs Iterator, op influxql.T
}
func buildTransformIterator(lhs Iterator, rhs Iterator, op influxql.Token, opt IteratorOptions) (Iterator, error) {
fn := binaryExprFunc(iteratorDataType(lhs), iteratorDataType(rhs), op)
lhsType, rhsType := iteratorDataType(lhs), iteratorDataType(rhs)
if lhsType == influxql.Integer && rhsType == influxql.Unsigned {
return nil, fmt.Errorf("cannot use %s between an integer and unsigned, an explicit cast is required", op)
} else if lhsType == influxql.Unsigned && rhsType == influxql.Integer {
return nil, fmt.Errorf("cannot use %s between unsigned and an integer, an explicit cast is required", op)
}
fn := binaryExprFunc(lhsType, rhsType, op)
switch fn := fn.(type) {
case func(float64, float64) float64:
var left FloatIterator
@ -1251,6 +1438,8 @@ func buildTransformIterator(lhs Iterator, rhs Iterator, op influxql.Token, opt I
left = lhs
case IntegerIterator:
left = &integerFloatCastIterator{input: lhs}
case UnsignedIterator:
left = &unsignedFloatCastIterator{input: lhs}
default:
return nil, fmt.Errorf("type mismatch on LHS, unable to use %T as a FloatIterator", lhs)
}
@ -1261,6 +1450,8 @@ func buildTransformIterator(lhs Iterator, rhs Iterator, op influxql.Token, opt I
right = rhs
case IntegerIterator:
right = &integerFloatCastIterator{input: rhs}
case UnsignedIterator:
right = &unsignedFloatCastIterator{input: rhs}
default:
return nil, fmt.Errorf("type mismatch on RHS, unable to use %T as a FloatIterator", rhs)
}
@ -1285,6 +1476,16 @@ func buildTransformIterator(lhs Iterator, rhs Iterator, op influxql.Token, opt I
return nil, fmt.Errorf("type mismatch on RHS, unable to use %T as a IntegerIterator", rhs)
}
return newIntegerExprIterator(left, right, opt, fn), nil
case func(uint64, uint64) uint64:
left, ok := lhs.(UnsignedIterator)
if !ok {
return nil, fmt.Errorf("type mismatch on LHS, unable to use %T as an UnsignedIterator", lhs)
}
right, ok := rhs.(UnsignedIterator)
if !ok {
return nil, fmt.Errorf("type mismatch on RHS, unable to use %T as an UnsignedIterator", lhs)
}
return newUnsignedExprIterator(left, right, opt, fn), nil
case func(float64, float64) bool:
var left FloatIterator
switch lhs := lhs.(type) {
@ -1292,6 +1493,8 @@ func buildTransformIterator(lhs Iterator, rhs Iterator, op influxql.Token, opt I
left = lhs
case IntegerIterator:
left = &integerFloatCastIterator{input: lhs}
case UnsignedIterator:
left = &unsignedFloatCastIterator{input: lhs}
default:
return nil, fmt.Errorf("type mismatch on LHS, unable to use %T as a FloatIterator", lhs)
}
@ -1302,6 +1505,8 @@ func buildTransformIterator(lhs Iterator, rhs Iterator, op influxql.Token, opt I
right = rhs
case IntegerIterator:
right = &integerFloatCastIterator{input: rhs}
case UnsignedIterator:
right = &unsignedFloatCastIterator{input: rhs}
default:
return nil, fmt.Errorf("type mismatch on RHS, unable to use %T as a FloatIterator", rhs)
}
@ -1316,6 +1521,16 @@ func buildTransformIterator(lhs Iterator, rhs Iterator, op influxql.Token, opt I
return nil, fmt.Errorf("type mismatch on RHS, unable to use %T as a IntegerIterator", rhs)
}
return newIntegerBooleanExprIterator(left, right, opt, fn), nil
case func(uint64, uint64) bool:
left, ok := lhs.(UnsignedIterator)
if !ok {
return nil, fmt.Errorf("type mismatch on LHS, unable to use %T as an UnsignedIterator", lhs)
}
right, ok := rhs.(UnsignedIterator)
if !ok {
return nil, fmt.Errorf("type mismatch on RHS, unable to use %T as an UnsignedIterator", lhs)
}
return newUnsignedBooleanExprIterator(left, right, opt, fn), nil
case func(bool, bool) bool:
left, ok := lhs.(BooleanIterator)
if !ok {
@ -1336,6 +1551,8 @@ func iteratorDataType(itr Iterator) influxql.DataType {
return influxql.Float
case IntegerIterator:
return influxql.Integer
case UnsignedIterator:
return influxql.Unsigned
case StringIterator:
return influxql.String
case BooleanIterator:
@ -1351,6 +1568,8 @@ func literalDataType(lit influxql.Literal) influxql.DataType {
return influxql.Float
case *influxql.IntegerLiteral:
return influxql.Integer
case *influxql.UnsignedLiteral:
return influxql.Unsigned
case *influxql.StringLiteral:
return influxql.String
case *influxql.BooleanLiteral:
@ -1369,9 +1588,54 @@ func binaryExprFunc(typ1 influxql.DataType, typ2 influxql.DataType, op influxql.
switch typ2 {
case influxql.Float:
fn = floatBinaryExprFunc(op)
case influxql.Unsigned:
// Special case for LT, LTE, GT, and GTE.
fn = unsignedBinaryExprFunc(op)
default:
fn = integerBinaryExprFunc(op)
}
case influxql.Unsigned:
switch typ2 {
case influxql.Float:
fn = floatBinaryExprFunc(op)
case influxql.Integer:
// Special case for LT, LTE, GT, and GTE.
// Since the RHS is an integer, we need to check if it is less than
// zero for the comparison operators to not be subject to overflow.
switch op {
case influxql.LT:
return func(lhs, rhs uint64) bool {
if int64(rhs) < 0 {
return false
}
return lhs < rhs
}
case influxql.LTE:
return func(lhs, rhs uint64) bool {
if int64(rhs) < 0 {
return false
}
return lhs <= rhs
}
case influxql.GT:
return func(lhs, rhs uint64) bool {
if int64(rhs) < 0 {
return true
}
return lhs > rhs
}
case influxql.GTE:
return func(lhs, rhs uint64) bool {
if int64(rhs) < 0 {
return true
}
return lhs >= rhs
}
}
fallthrough
default:
fn = unsignedBinaryExprFunc(op)
}
case influxql.Boolean:
fn = booleanBinaryExprFunc(op)
}
@ -1455,6 +1719,50 @@ func integerBinaryExprFunc(op influxql.Token) interface{} {
return nil
}
func unsignedBinaryExprFunc(op influxql.Token) interface{} {
switch op {
case influxql.ADD:
return func(lhs, rhs uint64) uint64 { return lhs + rhs }
case influxql.SUB:
return func(lhs, rhs uint64) uint64 { return lhs - rhs }
case influxql.MUL:
return func(lhs, rhs uint64) uint64 { return lhs * rhs }
case influxql.DIV:
return func(lhs, rhs uint64) uint64 {
if rhs == 0 {
return uint64(0)
}
return lhs / rhs
}
case influxql.MOD:
return func(lhs, rhs uint64) uint64 {
if rhs == 0 {
return uint64(0)
}
return lhs % rhs
}
case influxql.BITWISE_AND:
return func(lhs, rhs uint64) uint64 { return lhs & rhs }
case influxql.BITWISE_OR:
return func(lhs, rhs uint64) uint64 { return lhs | rhs }
case influxql.BITWISE_XOR:
return func(lhs, rhs uint64) uint64 { return lhs ^ rhs }
case influxql.EQ:
return func(lhs, rhs uint64) bool { return lhs == rhs }
case influxql.NEQ:
return func(lhs, rhs uint64) bool { return lhs != rhs }
case influxql.LT:
return func(lhs, rhs uint64) bool { return lhs < rhs }
case influxql.LTE:
return func(lhs, rhs uint64) bool { return lhs <= rhs }
case influxql.GT:
return func(lhs, rhs uint64) bool { return lhs > rhs }
case influxql.GTE:
return func(lhs, rhs uint64) bool { return lhs >= rhs }
}
return nil
}
func booleanBinaryExprFunc(op influxql.Token) interface{} {
switch op {
case influxql.BITWISE_AND:

File diff suppressed because it is too large Load Diff