Merge pull request #9853 from influxdata/js-fix-log2-function

Fix the log2 function to return values
pull/9881/head
Jonathan A. Sternberg 2018-05-19 11:35:31 -05:00 committed by GitHub
commit 531dc49717
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 227 additions and 7 deletions

View File

@ -23,14 +23,60 @@ func (MathTypeMapper) MapType(measurement *influxql.Measurement, field string) i
func (MathTypeMapper) CallType(name string, args []influxql.DataType) (influxql.DataType, error) {
switch name {
case "abs", "sin", "cos", "tan", "asin", "acos", "atan", "atan2", "exp", "log", "ln", "log2", "log10", "sqrt", "pow":
return influxql.Float, nil
case "floor", "ceil", "round":
switch args[0] {
case "sin", "cos", "tan", "atan", "exp", "log", "ln", "log2", "log10", "sqrt":
var arg0 influxql.DataType
if len(args) > 0 {
arg0 = args[0]
}
switch arg0 {
case influxql.Float, influxql.Integer, influxql.Unsigned, influxql.Unknown:
return influxql.Float, nil
default:
return influxql.Unknown, fmt.Errorf("invalid argument type for the first argument in %s(): %s", name, arg0)
}
case "asin", "acos":
var arg0 influxql.DataType
if len(args) > 0 {
arg0 = args[0]
}
switch arg0 {
case influxql.Float, influxql.Unknown:
return influxql.Float, nil
default:
return influxql.Unknown, fmt.Errorf("invalid argument type for the first argument in %s(): %s", name, arg0)
}
case "atan2", "pow":
var arg0, arg1 influxql.DataType
if len(args) > 0 {
arg0 = args[0]
}
if len(args) > 1 {
arg1 = args[1]
}
switch arg0 {
case influxql.Float, influxql.Integer, influxql.Unsigned, influxql.Unknown:
// Pass through to verify the second argument.
default:
return influxql.Unknown, fmt.Errorf("invalid argument type for the first argument in %s(): %s", name, arg0)
}
switch arg1 {
case influxql.Float, influxql.Integer, influxql.Unsigned, influxql.Unknown:
return influxql.Float, nil
default:
return influxql.Unknown, fmt.Errorf("invalid argument type for the second argument in %s(): %s", name, arg1)
}
case "abs", "floor", "ceil", "round":
var arg0 influxql.DataType
if len(args) > 0 {
arg0 = args[0]
}
switch arg0 {
case influxql.Float, influxql.Integer, influxql.Unsigned, influxql.Unknown:
return args[0], nil
default:
return influxql.Unknown, fmt.Errorf("invalid argument type for first argument in %s(): %s", name, args[0])
return influxql.Unknown, fmt.Errorf("invalid argument type for the first argument in %s(): %s", name, arg0)
}
}
return influxql.Unknown, nil
@ -125,7 +171,7 @@ func (v MathValuer) Call(name string, args []interface{}) (interface{}, bool) {
}
return nil, true
case "log2":
if arg0, ok := asFloat(args); ok {
if arg0, ok := asFloat(arg0); ok {
return math.Log2(arg0), true
}
return nil, true

View File

@ -1,6 +1,7 @@
package query_test
import (
"math"
"testing"
"github.com/influxdata/influxdb/query"
@ -11,16 +12,108 @@ func TestMath_TypeMapper(t *testing.T) {
for _, tt := range []struct {
s string
typ influxql.DataType
err bool
}{
{s: `abs(f::float)`, typ: influxql.Float},
{s: `abs(i::integer)`, typ: influxql.Integer},
{s: `abs(u::unsigned)`, typ: influxql.Unsigned},
{s: `abs(s::string)`, err: true},
{s: `abs(b::boolean)`, err: true},
{s: `sin(f::float)`, typ: influxql.Float},
{s: `sin(i::integer)`, typ: influxql.Float},
{s: `sin(u::unsigned)`, typ: influxql.Float},
{s: `sin(s::string)`, err: true},
{s: `sin(b::boolean)`, err: true},
{s: `cos(f::float)`, typ: influxql.Float},
{s: `cos(i::integer)`, typ: influxql.Float},
{s: `cos(u::unsigned)`, typ: influxql.Float},
{s: `cos(s::string)`, err: true},
{s: `cos(b::boolean)`, err: true},
{s: `tan(f::float)`, typ: influxql.Float},
{s: `tan(i::integer)`, typ: influxql.Float},
{s: `tan(u::unsigned)`, typ: influxql.Float},
{s: `tan(s::string)`, err: true},
{s: `tan(b::boolean)`, err: true},
{s: `asin(f::float)`, typ: influxql.Float},
{s: `asin(i::integer)`, err: true},
{s: `asin(u::unsigned)`, err: true},
{s: `asin(s::string)`, err: true},
{s: `asin(b::boolean)`, err: true},
{s: `acos(f::float)`, typ: influxql.Float},
{s: `acos(i::integer)`, err: true},
{s: `acos(u::unsigned)`, err: true},
{s: `acos(s::string)`, err: true},
{s: `acos(b::boolean)`, err: true},
{s: `atan(f::float)`, typ: influxql.Float},
{s: `atan(i::integer)`, typ: influxql.Float},
{s: `atan(u::unsigned)`, typ: influxql.Float},
{s: `atan(s::string)`, err: true},
{s: `atan(b::boolean)`, err: true},
{s: `atan2(y::float, x::float)`, typ: influxql.Float},
{s: `atan2(y::integer, x::float)`, typ: influxql.Float},
{s: `atan2(y::unsigned, x::float)`, typ: influxql.Float},
{s: `atan2(y::string, x::float)`, err: true},
{s: `atan2(y::boolean, x::float)`, err: true},
{s: `atan2(y::float, x::float)`, typ: influxql.Float},
{s: `atan2(y::float, x::integer)`, typ: influxql.Float},
{s: `atan2(y::float, x::unsigned)`, typ: influxql.Float},
{s: `atan2(y::float, x::string)`, err: true},
{s: `atan2(y::float, x::boolean)`, err: true},
{s: `exp(f::float)`, typ: influxql.Float},
{s: `exp(i::integer)`, typ: influxql.Float},
{s: `exp(u::unsigned)`, typ: influxql.Float},
{s: `exp(s::string)`, err: true},
{s: `exp(b::boolean)`, err: true},
{s: `log(f::float)`, typ: influxql.Float},
{s: `log(i::integer)`, typ: influxql.Float},
{s: `log(u::unsigned)`, typ: influxql.Float},
{s: `log(s::string)`, err: true},
{s: `log(b::boolean)`, err: true},
{s: `ln(f::float)`, typ: influxql.Float},
{s: `ln(i::integer)`, typ: influxql.Float},
{s: `ln(u::unsigned)`, typ: influxql.Float},
{s: `ln(s::string)`, err: true},
{s: `ln(b::boolean)`, err: true},
{s: `log2(f::float)`, typ: influxql.Float},
{s: `log2(i::integer)`, typ: influxql.Float},
{s: `log2(u::unsigned)`, typ: influxql.Float},
{s: `log2(s::string)`, err: true},
{s: `log2(b::boolean)`, err: true},
{s: `log10(f::float)`, typ: influxql.Float},
{s: `log10(i::integer)`, typ: influxql.Float},
{s: `log10(u::unsigned)`, typ: influxql.Float},
{s: `log10(s::string)`, err: true},
{s: `log10(b::boolean)`, err: true},
{s: `sqrt(f::float)`, typ: influxql.Float},
{s: `sqrt(i::integer)`, typ: influxql.Float},
{s: `sqrt(u::unsigned)`, typ: influxql.Float},
{s: `sqrt(s::string)`, err: true},
{s: `sqrt(b::boolean)`, err: true},
{s: `pow(y::float, x::float)`, typ: influxql.Float},
{s: `pow(y::integer, x::float)`, typ: influxql.Float},
{s: `pow(y::unsigned, x::float)`, typ: influxql.Float},
{s: `pow(y::string, x::string)`, err: true},
{s: `pow(y::boolean, x::boolean)`, err: true},
{s: `pow(y::float, x::float)`, typ: influxql.Float},
{s: `pow(y::float, x::integer)`, typ: influxql.Float},
{s: `pow(y::float, x::unsigned)`, typ: influxql.Float},
{s: `pow(y::float, x::string)`, err: true},
{s: `pow(y::float, x::boolean)`, err: true},
{s: `floor(f::float)`, typ: influxql.Float},
{s: `floor(i::integer)`, typ: influxql.Integer},
{s: `floor(u::unsigned)`, typ: influxql.Unsigned},
{s: `floor(s::string)`, err: true},
{s: `floor(b::boolean)`, err: true},
{s: `ceil(f::float)`, typ: influxql.Float},
{s: `ceil(i::integer)`, typ: influxql.Integer},
{s: `ceil(u::unsigned)`, typ: influxql.Unsigned},
{s: `ceil(s::string)`, err: true},
{s: `ceil(b::boolean)`, err: true},
{s: `round(f::float)`, typ: influxql.Float},
{s: `round(i::integer)`, typ: influxql.Integer},
{s: `round(u::unsigned)`, typ: influxql.Unsigned},
{s: `round(s::string)`, err: true},
{s: `round(b::boolean)`, err: true},
} {
t.Run(tt.s, func(t *testing.T) {
expr := MustParseExpr(tt.s)
@ -29,10 +122,91 @@ func TestMath_TypeMapper(t *testing.T) {
TypeMapper: query.MathTypeMapper{},
}
if got, err := typmap.EvalType(expr); err != nil {
t.Errorf("unexpected error: %s", err)
if !tt.err {
t.Errorf("unexpected error: %s", err)
}
} else if tt.err {
t.Error("expected error")
} else if want := tt.typ; got != want {
t.Errorf("unexpected type:\n\t-: \"%s\"\n\t+: \"%s\"", want, got)
}
})
}
}
func TestMathValuer_Call(t *testing.T) {
type values map[string]interface{}
for _, tt := range []struct {
s string
values values
exp interface{}
}{
{s: `abs(f)`, values: values{"f": float64(2)}, exp: float64(2)},
{s: `abs(f)`, values: values{"f": float64(-2)}, exp: float64(2)},
{s: `abs(i)`, values: values{"i": int64(2)}, exp: int64(2)},
{s: `abs(i)`, values: values{"i": int64(-2)}, exp: int64(-2)},
{s: `abs(u)`, values: values{"u": uint64(2)}, exp: uint64(2)},
{s: `sin(f)`, values: values{"f": math.Pi / 2}, exp: math.Sin(math.Pi / 2)},
{s: `sin(i)`, values: values{"i": int64(2)}, exp: math.Sin(2)},
{s: `sin(u)`, values: values{"u": uint64(2)}, exp: math.Sin(2)},
{s: `asin(f)`, values: values{"f": float64(0.5)}, exp: math.Asin(0.5)},
{s: `cos(f)`, values: values{"f": math.Pi / 2}, exp: math.Cos(math.Pi / 2)},
{s: `cos(i)`, values: values{"i": int64(2)}, exp: math.Cos(2)},
{s: `cos(u)`, values: values{"u": uint64(2)}, exp: math.Cos(2)},
{s: `acos(f)`, values: values{"f": float64(0.5)}, exp: math.Acos(0.5)},
{s: `tan(f)`, values: values{"f": math.Pi / 2}, exp: math.Tan(math.Pi / 2)},
{s: `tan(i)`, values: values{"i": int64(2)}, exp: math.Tan(2)},
{s: `tan(u)`, values: values{"u": uint64(2)}, exp: math.Tan(2)},
{s: `atan(f)`, values: values{"f": float64(2)}, exp: math.Atan(2)},
{s: `atan(i)`, values: values{"i": int64(2)}, exp: math.Atan(2)},
{s: `atan(u)`, values: values{"u": uint64(2)}, exp: math.Atan(2)},
{s: `atan2(y, x)`, values: values{"y": float64(2), "x": float64(3)}, exp: math.Atan2(2, 3)},
{s: `atan2(y, x)`, values: values{"y": int64(2), "x": int64(3)}, exp: math.Atan2(2, 3)},
{s: `atan2(y, x)`, values: values{"y": uint64(2), "x": uint64(3)}, exp: math.Atan2(2, 3)},
{s: `floor(f)`, values: values{"f": float64(2.5)}, exp: float64(2)},
{s: `floor(i)`, values: values{"i": int64(2)}, exp: int64(2)},
{s: `floor(u)`, values: values{"u": uint64(2)}, exp: uint64(2)},
{s: `ceil(f)`, values: values{"f": float64(2.5)}, exp: float64(3)},
{s: `ceil(i)`, values: values{"i": int64(2)}, exp: int64(2)},
{s: `ceil(u)`, values: values{"u": uint64(2)}, exp: uint64(2)},
{s: `round(f)`, values: values{"f": float64(2.4)}, exp: float64(2)},
{s: `round(f)`, values: values{"f": float64(2.6)}, exp: float64(3)},
{s: `round(i)`, values: values{"i": int64(2)}, exp: int64(2)},
{s: `round(u)`, values: values{"u": uint64(2)}, exp: uint64(2)},
{s: `exp(f)`, values: values{"f": float64(3)}, exp: math.Exp(3)},
{s: `exp(i)`, values: values{"i": int64(3)}, exp: math.Exp(3)},
{s: `exp(u)`, values: values{"u": uint64(3)}, exp: math.Exp(3)},
{s: `log(f, 8)`, values: values{"f": float64(3)}, exp: math.Log(3) / math.Log(8)},
{s: `log(i, 8)`, values: values{"i": int64(3)}, exp: math.Log(3) / math.Log(8)},
{s: `log(u, 8)`, values: values{"u": uint64(3)}, exp: math.Log(3) / math.Log(8)},
{s: `ln(f)`, values: values{"f": float64(3)}, exp: math.Log(3)},
{s: `ln(i)`, values: values{"i": int64(3)}, exp: math.Log(3)},
{s: `ln(u)`, values: values{"u": uint64(3)}, exp: math.Log(3)},
{s: `log2(f)`, values: values{"f": float64(3)}, exp: math.Log2(3)},
{s: `log2(i)`, values: values{"i": int64(3)}, exp: math.Log2(3)},
{s: `log2(u)`, values: values{"u": uint64(3)}, exp: math.Log2(3)},
{s: `log10(f)`, values: values{"f": float64(3)}, exp: math.Log10(3)},
{s: `log10(i)`, values: values{"i": int64(3)}, exp: math.Log10(3)},
{s: `log10(u)`, values: values{"u": uint64(3)}, exp: math.Log10(3)},
{s: `sqrt(f)`, values: values{"f": float64(3)}, exp: math.Sqrt(3)},
{s: `sqrt(i)`, values: values{"i": int64(3)}, exp: math.Sqrt(3)},
{s: `sqrt(u)`, values: values{"u": uint64(3)}, exp: math.Sqrt(3)},
{s: `pow(f, 2)`, values: values{"f": float64(4)}, exp: math.Pow(4, 2)},
{s: `pow(i, 2)`, values: values{"i": int64(4)}, exp: math.Pow(4, 2)},
{s: `pow(u, 2)`, values: values{"u": uint64(4)}, exp: math.Pow(4, 2)},
} {
t.Run(tt.s, func(t *testing.T) {
expr := MustParseExpr(tt.s)
valuer := influxql.ValuerEval{
Valuer: influxql.MultiValuer(
influxql.MapValuer(tt.values),
query.MathValuer{},
),
}
if got, want := valuer.Eval(expr), tt.exp; got != want {
t.Errorf("unexpected value: %v != %v", want, got)
}
})
}
}