github.com/influxdata/influxdb/v2@v2.7.6/influxql/query/math_test.go (about)

     1  package query_test
     2  
     3  import (
     4  	"math"
     5  	"testing"
     6  
     7  	"github.com/influxdata/influxdb/v2/influxql/query"
     8  	"github.com/influxdata/influxql"
     9  )
    10  
    11  func TestMath_TypeMapper(t *testing.T) {
    12  	for _, tt := range []struct {
    13  		s   string
    14  		typ influxql.DataType
    15  		err bool
    16  	}{
    17  		{s: `abs(f::float)`, typ: influxql.Float},
    18  		{s: `abs(i::integer)`, typ: influxql.Integer},
    19  		{s: `abs(u::unsigned)`, typ: influxql.Unsigned},
    20  		{s: `abs(s::string)`, err: true},
    21  		{s: `abs(b::boolean)`, err: true},
    22  		{s: `sin(f::float)`, typ: influxql.Float},
    23  		{s: `sin(i::integer)`, typ: influxql.Float},
    24  		{s: `sin(u::unsigned)`, typ: influxql.Float},
    25  		{s: `sin(s::string)`, err: true},
    26  		{s: `sin(b::boolean)`, err: true},
    27  		{s: `cos(f::float)`, typ: influxql.Float},
    28  		{s: `cos(i::integer)`, typ: influxql.Float},
    29  		{s: `cos(u::unsigned)`, typ: influxql.Float},
    30  		{s: `cos(s::string)`, err: true},
    31  		{s: `cos(b::boolean)`, err: true},
    32  		{s: `tan(f::float)`, typ: influxql.Float},
    33  		{s: `tan(i::integer)`, typ: influxql.Float},
    34  		{s: `tan(u::unsigned)`, typ: influxql.Float},
    35  		{s: `tan(s::string)`, err: true},
    36  		{s: `tan(b::boolean)`, err: true},
    37  		{s: `asin(f::float)`, typ: influxql.Float},
    38  		{s: `asin(i::integer)`, err: true},
    39  		{s: `asin(u::unsigned)`, err: true},
    40  		{s: `asin(s::string)`, err: true},
    41  		{s: `asin(b::boolean)`, err: true},
    42  		{s: `acos(f::float)`, typ: influxql.Float},
    43  		{s: `acos(i::integer)`, err: true},
    44  		{s: `acos(u::unsigned)`, err: true},
    45  		{s: `acos(s::string)`, err: true},
    46  		{s: `acos(b::boolean)`, err: true},
    47  		{s: `atan(f::float)`, typ: influxql.Float},
    48  		{s: `atan(i::integer)`, typ: influxql.Float},
    49  		{s: `atan(u::unsigned)`, typ: influxql.Float},
    50  		{s: `atan(s::string)`, err: true},
    51  		{s: `atan(b::boolean)`, err: true},
    52  		{s: `atan2(y::float, x::float)`, typ: influxql.Float},
    53  		{s: `atan2(y::integer, x::float)`, typ: influxql.Float},
    54  		{s: `atan2(y::unsigned, x::float)`, typ: influxql.Float},
    55  		{s: `atan2(y::string, x::float)`, err: true},
    56  		{s: `atan2(y::boolean, x::float)`, err: true},
    57  		{s: `atan2(y::float, x::float)`, typ: influxql.Float},
    58  		{s: `atan2(y::float, x::integer)`, typ: influxql.Float},
    59  		{s: `atan2(y::float, x::unsigned)`, typ: influxql.Float},
    60  		{s: `atan2(y::float, x::string)`, err: true},
    61  		{s: `atan2(y::float, x::boolean)`, err: true},
    62  		{s: `exp(f::float)`, typ: influxql.Float},
    63  		{s: `exp(i::integer)`, typ: influxql.Float},
    64  		{s: `exp(u::unsigned)`, typ: influxql.Float},
    65  		{s: `exp(s::string)`, err: true},
    66  		{s: `exp(b::boolean)`, err: true},
    67  		{s: `log(f::float)`, typ: influxql.Float},
    68  		{s: `log(i::integer)`, typ: influxql.Float},
    69  		{s: `log(u::unsigned)`, typ: influxql.Float},
    70  		{s: `log(s::string)`, err: true},
    71  		{s: `log(b::boolean)`, err: true},
    72  		{s: `ln(f::float)`, typ: influxql.Float},
    73  		{s: `ln(i::integer)`, typ: influxql.Float},
    74  		{s: `ln(u::unsigned)`, typ: influxql.Float},
    75  		{s: `ln(s::string)`, err: true},
    76  		{s: `ln(b::boolean)`, err: true},
    77  		{s: `log2(f::float)`, typ: influxql.Float},
    78  		{s: `log2(i::integer)`, typ: influxql.Float},
    79  		{s: `log2(u::unsigned)`, typ: influxql.Float},
    80  		{s: `log2(s::string)`, err: true},
    81  		{s: `log2(b::boolean)`, err: true},
    82  		{s: `log10(f::float)`, typ: influxql.Float},
    83  		{s: `log10(i::integer)`, typ: influxql.Float},
    84  		{s: `log10(u::unsigned)`, typ: influxql.Float},
    85  		{s: `log10(s::string)`, err: true},
    86  		{s: `log10(b::boolean)`, err: true},
    87  		{s: `sqrt(f::float)`, typ: influxql.Float},
    88  		{s: `sqrt(i::integer)`, typ: influxql.Float},
    89  		{s: `sqrt(u::unsigned)`, typ: influxql.Float},
    90  		{s: `sqrt(s::string)`, err: true},
    91  		{s: `sqrt(b::boolean)`, err: true},
    92  		{s: `pow(y::float, x::float)`, typ: influxql.Float},
    93  		{s: `pow(y::integer, x::float)`, typ: influxql.Float},
    94  		{s: `pow(y::unsigned, x::float)`, typ: influxql.Float},
    95  		{s: `pow(y::string, x::string)`, err: true},
    96  		{s: `pow(y::boolean, x::boolean)`, err: true},
    97  		{s: `pow(y::float, x::float)`, typ: influxql.Float},
    98  		{s: `pow(y::float, x::integer)`, typ: influxql.Float},
    99  		{s: `pow(y::float, x::unsigned)`, typ: influxql.Float},
   100  		{s: `pow(y::float, x::string)`, err: true},
   101  		{s: `pow(y::float, x::boolean)`, err: true},
   102  		{s: `floor(f::float)`, typ: influxql.Float},
   103  		{s: `floor(i::integer)`, typ: influxql.Integer},
   104  		{s: `floor(u::unsigned)`, typ: influxql.Unsigned},
   105  		{s: `floor(s::string)`, err: true},
   106  		{s: `floor(b::boolean)`, err: true},
   107  		{s: `ceil(f::float)`, typ: influxql.Float},
   108  		{s: `ceil(i::integer)`, typ: influxql.Integer},
   109  		{s: `ceil(u::unsigned)`, typ: influxql.Unsigned},
   110  		{s: `ceil(s::string)`, err: true},
   111  		{s: `ceil(b::boolean)`, err: true},
   112  		{s: `round(f::float)`, typ: influxql.Float},
   113  		{s: `round(i::integer)`, typ: influxql.Integer},
   114  		{s: `round(u::unsigned)`, typ: influxql.Unsigned},
   115  		{s: `round(s::string)`, err: true},
   116  		{s: `round(b::boolean)`, err: true},
   117  	} {
   118  		t.Run(tt.s, func(t *testing.T) {
   119  			expr := MustParseExpr(tt.s)
   120  
   121  			typmap := influxql.TypeValuerEval{
   122  				TypeMapper: query.MathTypeMapper{},
   123  			}
   124  			if got, err := typmap.EvalType(expr); err != nil {
   125  				if !tt.err {
   126  					t.Errorf("unexpected error: %s", err)
   127  				}
   128  			} else if tt.err {
   129  				t.Error("expected error")
   130  			} else if want := tt.typ; got != want {
   131  				t.Errorf("unexpected type:\n\t-: \"%s\"\n\t+: \"%s\"", want, got)
   132  			}
   133  		})
   134  	}
   135  }
   136  
   137  func TestMathValuer_Call(t *testing.T) {
   138  	type values map[string]interface{}
   139  	for _, tt := range []struct {
   140  		s      string
   141  		values values
   142  		exp    interface{}
   143  	}{
   144  		{s: `abs(f)`, values: values{"f": float64(2)}, exp: float64(2)},
   145  		{s: `abs(f)`, values: values{"f": float64(-2)}, exp: float64(2)},
   146  		{s: `abs(i)`, values: values{"i": int64(2)}, exp: int64(2)},
   147  		{s: `abs(i)`, values: values{"i": int64(-2)}, exp: int64(2)},
   148  		{s: `abs(u)`, values: values{"u": uint64(2)}, exp: uint64(2)},
   149  		{s: `sin(f)`, values: values{"f": math.Pi / 2}, exp: math.Sin(math.Pi / 2)},
   150  		{s: `sin(i)`, values: values{"i": int64(2)}, exp: math.Sin(2)},
   151  		{s: `sin(u)`, values: values{"u": uint64(2)}, exp: math.Sin(2)},
   152  		{s: `asin(f)`, values: values{"f": float64(0.5)}, exp: math.Asin(0.5)},
   153  		{s: `cos(f)`, values: values{"f": math.Pi / 2}, exp: math.Cos(math.Pi / 2)},
   154  		{s: `cos(i)`, values: values{"i": int64(2)}, exp: math.Cos(2)},
   155  		{s: `cos(u)`, values: values{"u": uint64(2)}, exp: math.Cos(2)},
   156  		{s: `acos(f)`, values: values{"f": float64(0.5)}, exp: math.Acos(0.5)},
   157  		{s: `tan(f)`, values: values{"f": math.Pi / 2}, exp: math.Tan(math.Pi / 2)},
   158  		{s: `tan(i)`, values: values{"i": int64(2)}, exp: math.Tan(2)},
   159  		{s: `tan(u)`, values: values{"u": uint64(2)}, exp: math.Tan(2)},
   160  		{s: `atan(f)`, values: values{"f": float64(2)}, exp: math.Atan(2)},
   161  		{s: `atan(i)`, values: values{"i": int64(2)}, exp: math.Atan(2)},
   162  		{s: `atan(u)`, values: values{"u": uint64(2)}, exp: math.Atan(2)},
   163  		{s: `atan2(y, x)`, values: values{"y": float64(2), "x": float64(3)}, exp: math.Atan2(2, 3)},
   164  		{s: `atan2(y, x)`, values: values{"y": int64(2), "x": int64(3)}, exp: math.Atan2(2, 3)},
   165  		{s: `atan2(y, x)`, values: values{"y": uint64(2), "x": uint64(3)}, exp: math.Atan2(2, 3)},
   166  		{s: `floor(f)`, values: values{"f": float64(2.5)}, exp: float64(2)},
   167  		{s: `floor(i)`, values: values{"i": int64(2)}, exp: int64(2)},
   168  		{s: `floor(u)`, values: values{"u": uint64(2)}, exp: uint64(2)},
   169  		{s: `ceil(f)`, values: values{"f": float64(2.5)}, exp: float64(3)},
   170  		{s: `ceil(i)`, values: values{"i": int64(2)}, exp: int64(2)},
   171  		{s: `ceil(u)`, values: values{"u": uint64(2)}, exp: uint64(2)},
   172  		{s: `round(f)`, values: values{"f": float64(2.4)}, exp: float64(2)},
   173  		{s: `round(f)`, values: values{"f": float64(2.6)}, exp: float64(3)},
   174  		{s: `round(i)`, values: values{"i": int64(2)}, exp: int64(2)},
   175  		{s: `round(u)`, values: values{"u": uint64(2)}, exp: uint64(2)},
   176  		{s: `exp(f)`, values: values{"f": float64(3)}, exp: math.Exp(3)},
   177  		{s: `exp(i)`, values: values{"i": int64(3)}, exp: math.Exp(3)},
   178  		{s: `exp(u)`, values: values{"u": uint64(3)}, exp: math.Exp(3)},
   179  		{s: `log(f, 8)`, values: values{"f": float64(3)}, exp: math.Log(3) / math.Log(8)},
   180  		{s: `log(i, 8)`, values: values{"i": int64(3)}, exp: math.Log(3) / math.Log(8)},
   181  		{s: `log(u, 8)`, values: values{"u": uint64(3)}, exp: math.Log(3) / math.Log(8)},
   182  		{s: `ln(f)`, values: values{"f": float64(3)}, exp: math.Log(3)},
   183  		{s: `ln(i)`, values: values{"i": int64(3)}, exp: math.Log(3)},
   184  		{s: `ln(u)`, values: values{"u": uint64(3)}, exp: math.Log(3)},
   185  		{s: `log2(f)`, values: values{"f": float64(3)}, exp: math.Log2(3)},
   186  		{s: `log2(i)`, values: values{"i": int64(3)}, exp: math.Log2(3)},
   187  		{s: `log2(u)`, values: values{"u": uint64(3)}, exp: math.Log2(3)},
   188  		{s: `log10(f)`, values: values{"f": float64(3)}, exp: math.Log10(3)},
   189  		{s: `log10(i)`, values: values{"i": int64(3)}, exp: math.Log10(3)},
   190  		{s: `log10(u)`, values: values{"u": uint64(3)}, exp: math.Log10(3)},
   191  		{s: `sqrt(f)`, values: values{"f": float64(3)}, exp: math.Sqrt(3)},
   192  		{s: `sqrt(i)`, values: values{"i": int64(3)}, exp: math.Sqrt(3)},
   193  		{s: `sqrt(u)`, values: values{"u": uint64(3)}, exp: math.Sqrt(3)},
   194  		{s: `pow(f, 2)`, values: values{"f": float64(4)}, exp: math.Pow(4, 2)},
   195  		{s: `pow(i, 2)`, values: values{"i": int64(4)}, exp: math.Pow(4, 2)},
   196  		{s: `pow(u, 2)`, values: values{"u": uint64(4)}, exp: math.Pow(4, 2)},
   197  	} {
   198  		t.Run(tt.s, func(t *testing.T) {
   199  			expr := MustParseExpr(tt.s)
   200  
   201  			valuer := influxql.ValuerEval{
   202  				Valuer: influxql.MultiValuer(
   203  					influxql.MapValuer(tt.values),
   204  					query.MathValuer{},
   205  				),
   206  			}
   207  			if got, want := valuer.Eval(expr), tt.exp; got != want {
   208  				t.Errorf("unexpected value: %v != %v", want, got)
   209  			}
   210  		})
   211  	}
   212  }