diff --git a/enginetest/queries/queries.go b/enginetest/queries/queries.go index 9b8546f55e..4d37253ca9 100644 --- a/enginetest/queries/queries.go +++ b/enginetest/queries/queries.go @@ -8592,6 +8592,13 @@ from typestable`, Query: "flush engine logs", Expected: []sql.Row{}, }, + // TODO: this is the largest scale decimal we support, but it's not the largest MySQL supports + { + Query: "select round(5e29, -30)", + Expected: []sql.Row{ + {1e30}, + }, + }, } var KeylessQueries = []QueryTest{ diff --git a/sql/expression/function/ceil_round_floor.go b/sql/expression/function/ceil_round_floor.go index 636617c851..8114bd00c6 100644 --- a/sql/expression/function/ceil_round_floor.go +++ b/sql/expression/function/ceil_round_floor.go @@ -15,10 +15,8 @@ package function import ( - "encoding/hex" "fmt" "math" - "strconv" "github.com/shopspring/decimal" @@ -242,119 +240,67 @@ func (r *Round) Children() []sql.Expression { // Eval implements the Expression interface. func (r *Round) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { - xVal, err := r.Left.Eval(ctx, row) + val, err := r.Left.Eval(ctx, row) if err != nil { return nil, err } - if xVal == nil { + if val == nil { return nil, nil } - dVal := float64(0) + decType := types.MustCreateDecimalType(types.DecimalTypeMaxPrecision, types.DecimalTypeMaxScale) + val, _, err = decType.Convert(val) + if err != nil { + // TODO: truncate + return nil, err + } + prec := int32(0) if r.Right != nil { - var dTemp interface{} - dTemp, err = r.Right.Eval(ctx, row) + var tmp interface{} + tmp, err = r.Right.Eval(ctx, row) if err != nil { return nil, err } - if dTemp != nil { - switch dNum := dTemp.(type) { - case float64: - dVal = float64(int64(dNum)) - case float32: - dVal = float64(int64(dNum)) - case int64: - dVal = float64(dNum) - case int32: - dVal = float64(dNum) - case int16: - dVal = float64(dNum) - case int8: - dVal = float64(dNum) - case uint64: - dVal = float64(dNum) - case uint32: - dVal = float64(dNum) - case uint16: - dVal = float64(dNum) - case uint8: - dVal = float64(dNum) - case int: - dVal = float64(dNum) - case []byte: - val, err := strconv.ParseUint(hex.EncodeToString(dNum), 16, 64) - if err != nil { - return nil, err - } - dVal = float64(val) - default: - dTemp, _, err = types.Float64.Convert(dTemp) - if err == nil { - dVal = dTemp.(float64) - } + if tmp == nil { + return nil, nil + } + + if tmp != nil { + tmp, _, err = types.Int32.Convert(tmp) + if err != nil { + // TODO: truncate + return nil, err } - if dVal > 30 { // MySQL cuts off at 30 for larger values - dVal = 30 + prec = tmp.(int32) + // MySQL cuts off at 30 for larger values + // TODO: these limits are fine only because we can't handle decimals larger than this + if prec > types.DecimalTypeMaxPrecision { + prec = types.DecimalTypeMaxPrecision + } + if prec < -types.DecimalTypeMaxScale { + prec = -types.DecimalTypeMaxScale } } } - if types.IsText(r.Left.Type()) { - xVal, _, err = types.Float64.Convert(xVal) - if err != nil { - return int32(0), nil - } - } else if !types.IsNumber(r.Left.Type()) { - xVal, _, err = types.Float64.Convert(xVal) - if err != nil { - return int32(0), nil - } - - xNum := xVal.(float64) - return int32(math.Round(xNum*math.Pow(10.0, dVal)) / math.Pow(10.0, dVal)), nil + var res interface{} + tmp := val.(decimal.Decimal).Round(prec) + if types.IsSigned(r.Left.Type()) { + res, _, err = types.Int64.Convert(tmp) + } else if types.IsUnsigned(r.Left.Type()) { + res, _, err = types.Uint64.Convert(tmp) + } else if types.IsFloat(r.Left.Type()) { + res, _, err = types.Float64.Convert(tmp) + } else if types.IsDecimal(r.Left.Type()) { + res = tmp + } else if types.IsTextBlob(r.Left.Type()) { + res, _, err = types.Float64.Convert(tmp) } - // One way to round to a decimal place is to shift the number up by the desired decimal position, round to the - // nearest integer, and then shift back down. - // For example, we have 5.855 and want to round to 2 decimal places. - // In this case, xNum = 5.855 and dVal = 2 - // round(xNum * 10^dVal) / 10^dVal - // round(5.855 * 10^2) / 10^2 - // round(5.855 * 100) / 100 - // round(585.5) / 100 - // 586 / 100 - // 5.86 - switch xNum := xVal.(type) { - case float64: - return math.Round(xNum*math.Pow(10.0, dVal)) / math.Pow(10.0, dVal), nil - case float32: - return float32(math.Round(float64(xNum)*math.Pow(10.0, dVal)) / math.Pow(10.0, dVal)), nil - case int64: - return int64(math.Round(float64(xNum)*math.Pow(10.0, dVal)) / math.Pow(10.0, dVal)), nil - case int32: - return int32(math.Round(float64(xNum)*math.Pow(10.0, dVal)) / math.Pow(10.0, dVal)), nil - case int16: - return int16(math.Round(float64(xNum)*math.Pow(10.0, dVal)) / math.Pow(10.0, dVal)), nil - case int8: - return int8(math.Round(float64(xNum)*math.Pow(10.0, dVal)) / math.Pow(10.0, dVal)), nil - case uint64: - return uint64(math.Round(float64(xNum)*math.Pow(10.0, dVal)) / math.Pow(10.0, dVal)), nil - case uint32: - return uint32(math.Round(float64(xNum)*math.Pow(10.0, dVal)) / math.Pow(10.0, dVal)), nil - case uint16: - return uint16(math.Round(float64(xNum)*math.Pow(10.0, dVal)) / math.Pow(10.0, dVal)), nil - case uint8: - return uint8(math.Round(float64(xNum)*math.Pow(10.0, dVal)) / math.Pow(10.0, dVal)), nil - case int: - return int(math.Round(float64(xNum)*math.Pow(10.0, dVal)) / math.Pow(10.0, dVal)), nil - case decimal.Decimal: - return xNum.Round(int32(dVal)), nil - default: - return nil, sql.ErrInvalidType.New(r.Left.Type().String()) - } + return res, err } // IsNullable implements the Expression interface. diff --git a/sql/expression/function/ceil_round_floor_test.go b/sql/expression/function/ceil_round_floor_test.go index d6336f8018..abb2ca2d4c 100644 --- a/sql/expression/function/ceil_round_floor_test.go +++ b/sql/expression/function/ceil_round_floor_test.go @@ -119,148 +119,501 @@ func TestFloor(t *testing.T) { func TestRound(t *testing.T) { testCases := []struct { - name string - xType sql.Type - dType sql.Type - row sql.Row - expected interface{} - err *errors.Kind + name string + xExpr sql.Expression + dExpr sql.Expression + exp interface{} + err *errors.Kind }{ - {"float64 is nil", types.Float64, types.Int32, sql.NewRow(nil, nil), nil, nil}, - {"float64 without d", types.Float64, types.Int32, sql.NewRow(5.8, nil), float64(6), nil}, - {"float64 with d", types.Float64, types.Int32, sql.NewRow(5.855, 2), float64(5.86), nil}, - {"float64 with negative d", types.Float64, types.Int32, sql.NewRow(52.855, -1), float64(50), nil}, - {"float64 with float d", types.Float64, types.Float64, sql.NewRow(5.855, float64(2.123)), float64(5.86), nil}, - {"float64 with float negative d", types.Float64, types.Float64, sql.NewRow(52.855, float64(-1)), float64(50), nil}, - {"float64 with blob d", types.Float64, types.Blob, sql.NewRow(5.855, []byte{1, 2, 3}), float64(5.855), nil}, - {"float32 is nil", types.Float32, types.Int32, sql.NewRow(nil, nil), nil, nil}, - {"float32 without d", types.Float32, types.Int32, sql.NewRow(float32(5.8), nil), float32(6), nil}, - {"float32 with d", types.Float32, types.Int32, sql.NewRow(float32(5.855), 2), float32(5.86), nil}, - {"float32 with negative d", types.Float32, types.Int32, sql.NewRow(float32(52.855), -1), float32(50), nil}, - {"float32 with float d", types.Float32, types.Float64, sql.NewRow(float32(5.855), float32(2.123)), float32(5.86), nil}, - {"float32 with float negative d", types.Float32, types.Float64, sql.NewRow(float32(52.855), float32(-1)), float32(50), nil}, - {"float32 with blob d", types.Float32, types.Blob, sql.NewRow(float32(5.855), []byte{1, 2, 3}), float32(5.855), nil}, - {"int64 is nil", types.Int64, types.Int32, sql.NewRow(nil, nil), nil, nil}, - {"int64 without d", types.Int64, types.Int32, sql.NewRow(int64(5), nil), int64(5), nil}, - {"int64 with d", types.Int64, types.Int32, sql.NewRow(int64(5), 2), int64(5), nil}, - {"int64 with negative d", types.Int64, types.Int32, sql.NewRow(int64(52), -1), int64(50), nil}, - {"int64 with float d", types.Int64, types.Float64, sql.NewRow(int64(5), float32(2.123)), int64(5), nil}, - {"int64 with float negative d", types.Int64, types.Float64, sql.NewRow(int64(52), float32(-1)), int64(50), nil}, - {"int32 with blob d", types.Int32, types.Blob, sql.NewRow(int32(5), []byte{1, 2, 3}), int32(5), nil}, - {"int32 is nil", types.Int32, types.Int32, sql.NewRow(nil, nil), nil, nil}, - {"int32 without d", types.Int32, types.Int32, sql.NewRow(int32(5), nil), int32(5), nil}, - {"int32 with d", types.Int32, types.Int32, sql.NewRow(int32(5), 2), int32(5), nil}, - {"int32 with negative d", types.Int32, types.Int32, sql.NewRow(int32(52), -1), int32(50), nil}, - {"int32 with float d", types.Int32, types.Float64, sql.NewRow(int32(5), float32(2.123)), int32(5), nil}, - {"int32 with float negative d", types.Int32, types.Float64, sql.NewRow(int32(52), float32(-1)), int32(50), nil}, - {"int32 with blob d", types.Int32, types.Blob, sql.NewRow(int32(5), []byte{1, 2, 3}), int32(5), nil}, - {"int16 is nil", types.Int16, types.Int16, sql.NewRow(nil, nil), nil, nil}, - {"int16 without d", types.Int16, types.Int16, sql.NewRow(int16(5), nil), int16(5), nil}, - {"int16 with d", types.Int16, types.Int16, sql.NewRow(int16(5), 2), int16(5), nil}, - {"int16 with negative d", types.Int16, types.Int16, sql.NewRow(int16(52), -1), int16(50), nil}, - {"int16 with float d", types.Int16, types.Float64, sql.NewRow(int16(5), float32(2.123)), int16(5), nil}, - {"int16 with float negative d", types.Int16, types.Float64, sql.NewRow(int16(52), float32(-1)), int16(50), nil}, - {"int16 with blob d", types.Int16, types.Blob, sql.NewRow(int16(5), []byte{1, 2, 3}), int16(5), nil}, - {"int8 is nil", types.Int8, types.Int8, sql.NewRow(nil, nil), nil, nil}, - {"int8 without d", types.Int8, types.Int8, sql.NewRow(int8(5), nil), int8(5), nil}, - {"int8 with d", types.Int8, types.Int8, sql.NewRow(int8(5), 2), int8(5), nil}, - {"int8 with negative d", types.Int8, types.Int8, sql.NewRow(int8(52), -1), int8(50), nil}, - {"int8 with float d", types.Int8, types.Float64, sql.NewRow(int8(5), float32(2.123)), int8(5), nil}, - {"int8 with float negative d", types.Int8, types.Float64, sql.NewRow(int8(52), float32(-1)), int8(50), nil}, - {"int8 with blob d", types.Int8, types.Blob, sql.NewRow(int8(5), []byte{1, 2, 3}), int8(5), nil}, - {"uint64 is nil", types.Uint64, types.Int32, sql.NewRow(nil, nil), nil, nil}, - {"uint64 without d", types.Uint64, types.Int32, sql.NewRow(uint64(5), nil), uint64(5), nil}, - {"uint64 with d", types.Uint64, types.Int32, sql.NewRow(uint64(5), 2), uint64(5), nil}, - {"uint64 with negative d", types.Uint64, types.Int32, sql.NewRow(uint64(52), -1), uint64(50), nil}, - {"uint64 with float d", types.Uint64, types.Float64, sql.NewRow(uint64(5), float32(2.123)), uint64(5), nil}, - {"uint64 with float negative d", types.Uint64, types.Float64, sql.NewRow(uint64(52), float32(-1)), uint64(50), nil}, - {"uint32 with blob d", types.Uint32, types.Blob, sql.NewRow(uint32(5), []byte{1, 2, 3}), uint32(5), nil}, - {"uint32 is nil", types.Uint32, types.Int32, sql.NewRow(nil, nil), nil, nil}, - {"uint32 without d", types.Uint32, types.Int32, sql.NewRow(uint32(5), nil), uint32(5), nil}, - {"uint32 with d", types.Uint32, types.Int32, sql.NewRow(uint32(5), 2), uint32(5), nil}, - {"uint32 with negative d", types.Uint32, types.Int32, sql.NewRow(uint32(52), -1), uint32(50), nil}, - {"uint32 with float d", types.Uint32, types.Float64, sql.NewRow(uint32(5), float32(2.123)), uint32(5), nil}, - {"uint32 with float negative d", types.Uint32, types.Float64, sql.NewRow(uint32(52), float32(-1)), uint32(50), nil}, - {"uint32 with blob d", types.Uint32, types.Blob, sql.NewRow(uint32(5), []byte{1, 2, 3}), uint32(5), nil}, - {"uint16 with blob d", types.Uint16, types.Blob, sql.NewRow(uint16(5), []byte{1, 2, 3}), uint16(5), nil}, - {"uint16 is nil", types.Uint16, types.Int16, sql.NewRow(nil, nil), nil, nil}, - {"uint16 without d", types.Uint16, types.Int16, sql.NewRow(uint16(5), nil), uint16(5), nil}, - {"uint16 with d", types.Uint16, types.Int16, sql.NewRow(uint16(5), 2), uint16(5), nil}, - {"uint16 with negative d", types.Uint16, types.Int16, sql.NewRow(uint16(52), -1), uint16(50), nil}, - {"uint16 with float d", types.Uint16, types.Float64, sql.NewRow(uint16(5), float32(2.123)), uint16(5), nil}, - {"uint16 with float negative d", types.Uint16, types.Float64, sql.NewRow(uint16(52), float32(-1)), uint16(50), nil}, - {"uint16 with blob d", types.Uint16, types.Blob, sql.NewRow(uint16(5), []byte{1, 2, 3}), uint16(5), nil}, - {"uint8 with blob d", types.Uint8, types.Blob, sql.NewRow(uint8(5), []byte{1, 2, 3}), uint8(5), nil}, - {"uint8 is nil", types.Uint8, types.Int8, sql.NewRow(nil, nil), nil, nil}, - {"uint8 without d", types.Uint8, types.Int8, sql.NewRow(uint8(5), nil), uint8(5), nil}, - {"uint8 with d", types.Uint8, types.Int8, sql.NewRow(uint8(5), 2), uint8(5), nil}, - {"uint8 with negative d", types.Uint8, types.Int8, sql.NewRow(uint8(52), -1), uint8(50), nil}, - {"uint8 with float d", types.Uint8, types.Float64, sql.NewRow(uint8(5), float32(2.123)), uint8(5), nil}, - {"uint8 with float negative d", types.Uint8, types.Float64, sql.NewRow(uint8(52), float32(-1)), uint8(50), nil}, - {"uint8 with blob d", types.Uint8, types.Blob, sql.NewRow(uint8(5), []byte{1, 2, 3}), uint8(5), nil}, - {"blob is nil", types.Blob, types.Int32, sql.NewRow(nil, nil), nil, nil}, - {"blob is ok", types.Blob, types.Int32, sql.NewRow([]byte{1, 2, 3}, nil), float64(66051), nil}, - {"text int without d", types.Text, types.Int32, sql.NewRow("5", nil), float64(5), nil}, - {"text int with d", types.Text, types.Int32, sql.NewRow("5", 2), float64(5), nil}, - {"text int with negative d", types.Text, types.Int32, sql.NewRow("52", -1), float64(50), nil}, - {"text int with float d", types.Text, types.Float64, sql.NewRow("5", float32(2.123)), float64(5), nil}, - {"text int with float negative d", types.Text, types.Float64, sql.NewRow("52", float32(-1)), float64(50), nil}, - {"text float without d", types.Text, types.Int32, sql.NewRow("5.8", nil), float64(6), nil}, - {"text float with d", types.Text, types.Int32, sql.NewRow("5.855", 2), float64(5.86), nil}, - {"text float with negative d", types.Text, types.Int32, sql.NewRow("52.855", -1), float64(50), nil}, - {"text float with float d", types.Text, types.Float64, sql.NewRow("5.855", float64(2.123)), float64(5.86), nil}, - {"text float with float negative d", types.Text, types.Float64, sql.NewRow("52.855", float64(-1)), float64(50), nil}, - {"text float with blob d", types.Text, types.Blob, sql.NewRow("5.855", []byte{1, 2, 3}), float64(5.855), nil}, - } + { + name: "float64 is nil", + xExpr: expression.NewLiteral(nil, types.Null), + exp: nil, + }, + { + name: "float64 without d", + xExpr: expression.NewLiteral(5.8, types.Float64), + exp: 6.0, + }, + { + name: "float64 with nil d", + xExpr: expression.NewLiteral(5.855, types.Float64), + dExpr: expression.NewLiteral(nil, types.Null), + exp: nil, + }, + { + name: "float64 with d", + xExpr: expression.NewLiteral(5.855, types.Float64), + dExpr: expression.NewLiteral(2, types.Int32), + exp: 5.86, + }, + { + name: "float64 with negative d", + xExpr: expression.NewLiteral(52.855, types.Float64), + dExpr: expression.NewLiteral(-1, types.Int32), + exp: 50.0, + }, + { + name: "float64 with negative d", + xExpr: expression.NewLiteral(52.855, types.Float64), + dExpr: expression.NewLiteral(-2, types.Int32), + exp: 100.0, + }, + { + name: "float64 with large d", + xExpr: expression.NewLiteral(1234567890.0987654321, types.Float64), + dExpr: expression.NewLiteral(999_999_999, types.Int32), + exp: 1234567890.0987654321, + }, + { + name: "float64 with large negative d", + xExpr: expression.NewLiteral(52.855, types.Float64), + dExpr: expression.NewLiteral(-999_999_999, types.Int32), + exp: 0.0, + }, + { + name: "float64 with float d", + xExpr: expression.NewLiteral(5.855, types.Float64), + dExpr: expression.NewLiteral(2.123, types.Float64), + exp: 5.86, + }, + { + name: "float64 with float negative d", + xExpr: expression.NewLiteral(52.855, types.Float64), + dExpr: expression.NewLiteral(-1.0, types.Float64), + exp: 50.0, + }, + { + name: "float64 with blob d", + xExpr: expression.NewLiteral(5.855, types.Float64), + dExpr: expression.NewLiteral([]byte{'1', '2', '3'}, types.Blob), + exp: 5.855, + }, - for _, tt := range testCases { - var args = make([]sql.Expression, 2) - args[0] = expression.NewGetField(0, tt.xType, "", false) - args[1] = expression.NewGetField(1, tt.dType, "", false) - f, err := NewRound(args...) + { + name: "float32 without d", + xExpr: expression.NewLiteral(5.8, types.Float32), + exp: 6.0, + }, + { + name: "float32 with d", + xExpr: expression.NewLiteral(5.855, types.Float32), + dExpr: expression.NewLiteral(2, types.Int32), + exp: 5.86, + }, + { + name: "float32 with negative d", + xExpr: expression.NewLiteral(52.855, types.Float32), + dExpr: expression.NewLiteral(-1, types.Int32), + exp: 50.0, + }, + { + name: "float32 with float d", + xExpr: expression.NewLiteral(5.855, types.Float32), + dExpr: expression.NewLiteral(2.123, types.Float64), + exp: 5.86, + }, + { + name: "float32 with float negative d", + xExpr: expression.NewLiteral(52.855, types.Float32), + dExpr: expression.NewLiteral(-1.0, types.Float32), + exp: 50.0, + }, + { + name: "float32 with blob d", + xExpr: expression.NewLiteral(5.855, types.Float32), + dExpr: expression.NewLiteral([]byte{'1', '2', '3'}, types.Blob), + exp: 5.855, + }, - t.Run(tt.name, func(t *testing.T) { - require := require.New(t) + { + name: "int64 without d", + xExpr: expression.NewLiteral(5, types.Int64), + exp: int64(5), + }, + { + name: "int64 with d", + xExpr: expression.NewLiteral(5, types.Int64), + dExpr: expression.NewLiteral(2, types.Int32), + exp: int64(5), + }, + { + name: "int64 with negative d", + xExpr: expression.NewLiteral(52, types.Int64), + dExpr: expression.NewLiteral(-1, types.Int32), + exp: int64(50), + }, + { + name: "int64 with float d", + xExpr: expression.NewLiteral(5, types.Int64), + dExpr: expression.NewLiteral(2.123, types.Float64), + exp: int64(5), + }, + { + name: "int64 with float negative d", + xExpr: expression.NewLiteral(52, types.Int64), + dExpr: expression.NewLiteral(-1.0, types.Float32), + exp: int64(50), + }, + { + name: "int64 with blob d", + xExpr: expression.NewLiteral(5, types.Int64), + dExpr: expression.NewLiteral([]byte{'1', '2', '3'}, types.Blob), + exp: int64(5), + }, - require.Nil(err) + { + name: "int32 without d", + xExpr: expression.NewLiteral(5, types.Int32), + exp: int64(5), + }, + { + name: "int32 with d", + xExpr: expression.NewLiteral(5, types.Int32), + dExpr: expression.NewLiteral(2, types.Int32), + exp: int64(5), + }, + { + name: "int32 with negative d", + xExpr: expression.NewLiteral(52, types.Int32), + dExpr: expression.NewLiteral(-1, types.Int32), + exp: int64(50), + }, + { + name: "int32 with float d", + xExpr: expression.NewLiteral(5, types.Int32), + dExpr: expression.NewLiteral(2.123, types.Float64), + exp: int64(5), + }, + { + name: "int32 with float negative d", + xExpr: expression.NewLiteral(52, types.Int32), + dExpr: expression.NewLiteral(-1.0, types.Float32), + exp: int64(50), + }, + { + name: "int32 with blob d", + xExpr: expression.NewLiteral(5, types.Int32), + dExpr: expression.NewLiteral([]byte{'1', '2', '3'}, types.Blob), + exp: int64(5), + }, - result, err := f.Eval(sql.NewEmptyContext(), tt.row) - if tt.err != nil { - require.Error(err) - require.True(tt.err.Is(err)) - } else { - require.NoError(err) - require.Equal(tt.expected, result) - } + { + name: "int16 without d", + xExpr: expression.NewLiteral(5, types.Int16), + exp: int64(5), + }, + { + name: "int16 with d", + xExpr: expression.NewLiteral(5, types.Int16), + dExpr: expression.NewLiteral(2, types.Int32), + exp: int64(5), + }, + { + name: "int16 with negative d", + xExpr: expression.NewLiteral(52, types.Int16), + dExpr: expression.NewLiteral(-1, types.Int32), + exp: int64(50), + }, + { + name: "int16 with float d", + xExpr: expression.NewLiteral(5, types.Int16), + dExpr: expression.NewLiteral(2.123, types.Float64), + exp: int64(5), + }, + { + name: "int16 with float negative d", + xExpr: expression.NewLiteral(52, types.Int16), + dExpr: expression.NewLiteral(-1.0, types.Float32), + exp: int64(50), + }, + { + name: "int16 with blob d", + xExpr: expression.NewLiteral(5, types.Int16), + dExpr: expression.NewLiteral([]byte{'1', '2', '3'}, types.Blob), + exp: int64(5), + }, - switch { - case types.IsFloat(tt.xType): - require.True(types.IsFloat(f.Type())) - require.False(f.IsNullable()) - case types.IsInteger(tt.xType): - require.True(types.IsInteger(f.Type())) - require.False(f.IsNullable()) - default: - require.True(types.IsInteger(f.Type())) - require.False(f.IsNullable()) - } - }) - } + { + name: "int8 without d", + xExpr: expression.NewLiteral(5, types.Int16), + exp: int64(5), + }, + { + name: "int8 with d", + xExpr: expression.NewLiteral(5, types.Int8), + dExpr: expression.NewLiteral(2, types.Int32), + exp: int64(5), + }, + { + name: "int8 with negative d", + xExpr: expression.NewLiteral(52, types.Int8), + dExpr: expression.NewLiteral(-1, types.Int32), + exp: int64(50), + }, + { + name: "int8 with float d", + xExpr: expression.NewLiteral(5, types.Int8), + dExpr: expression.NewLiteral(2.123, types.Float64), + exp: int64(5), + }, + { + name: "int8 with float negative d", + xExpr: expression.NewLiteral(52, types.Int8), + dExpr: expression.NewLiteral(-1.0, types.Float32), + exp: int64(50), + }, + { + name: "int8 with blob d", + xExpr: expression.NewLiteral(5, types.Int8), + dExpr: expression.NewLiteral([]byte{'1', '2', '3'}, types.Blob), + exp: int64(5), + }, + + { + name: "uint64 without d", + xExpr: expression.NewLiteral(5, types.Uint64), + exp: uint64(5), + }, + { + name: "uint64 with d", + xExpr: expression.NewLiteral(5, types.Uint64), + dExpr: expression.NewLiteral(2, types.Int32), + exp: uint64(5), + }, + { + name: "uint64 with negative d", + xExpr: expression.NewLiteral(52, types.Uint64), + dExpr: expression.NewLiteral(-1, types.Int32), + exp: uint64(50), + }, + { + name: "uint64 with float d", + xExpr: expression.NewLiteral(5, types.Uint64), + dExpr: expression.NewLiteral(2.123, types.Float64), + exp: uint64(5), + }, + { + name: "uint64 with float negative d", + xExpr: expression.NewLiteral(52, types.Uint64), + dExpr: expression.NewLiteral(-1.0, types.Float32), + exp: uint64(50), + }, + { + name: "uint64 with blob d", + xExpr: expression.NewLiteral(5, types.Uint64), + dExpr: expression.NewLiteral([]byte{'1', '2', '3'}, types.Blob), + exp: uint64(5), + }, + + { + name: "uint32 without d", + xExpr: expression.NewLiteral(5, types.Uint32), + exp: uint64(5), + }, + { + name: "uint32 with d", + xExpr: expression.NewLiteral(5, types.Uint32), + dExpr: expression.NewLiteral(2, types.Int32), + exp: uint64(5), + }, + { + name: "uint32 with negative d", + xExpr: expression.NewLiteral(52, types.Uint32), + dExpr: expression.NewLiteral(-1, types.Int32), + exp: uint64(50), + }, + { + name: "uint32 with float d", + xExpr: expression.NewLiteral(5, types.Uint32), + dExpr: expression.NewLiteral(2.123, types.Float64), + exp: uint64(5), + }, + { + name: "uint32 with float negative d", + xExpr: expression.NewLiteral(52, types.Uint32), + dExpr: expression.NewLiteral(-1.0, types.Float32), + exp: uint64(50), + }, + { + name: "uint32 with blob d", + xExpr: expression.NewLiteral(5, types.Uint32), + dExpr: expression.NewLiteral([]byte{'1', '2', '3'}, types.Blob), + exp: uint64(5), + }, + + { + name: "uint16 without d", + xExpr: expression.NewLiteral(5, types.Uint16), + exp: uint64(5), + }, + { + name: "uint16 with d", + xExpr: expression.NewLiteral(5, types.Uint16), + dExpr: expression.NewLiteral(2, types.Int32), + exp: uint64(5), + }, + { + name: "uint16 with negative d", + xExpr: expression.NewLiteral(52, types.Uint16), + dExpr: expression.NewLiteral(-1, types.Int32), + exp: uint64(50), + }, + { + name: "uint16 with float d", + xExpr: expression.NewLiteral(5, types.Uint16), + dExpr: expression.NewLiteral(2.123, types.Float64), + exp: uint64(5), + }, + { + name: "uint16 with float negative d", + xExpr: expression.NewLiteral(52, types.Uint16), + dExpr: expression.NewLiteral(-1.0, types.Float32), + exp: uint64(50), + }, + { + name: "uint16 with blob d", + xExpr: expression.NewLiteral(5, types.Uint16), + dExpr: expression.NewLiteral([]byte{'1', '2', '3'}, types.Blob), + exp: uint64(5), + }, + + { + name: "int8 without d", + xExpr: expression.NewLiteral(5, types.Uint8), + exp: uint64(5), + }, + { + name: "int8 with d", + xExpr: expression.NewLiteral(5, types.Uint8), + dExpr: expression.NewLiteral(2, types.Int32), + exp: uint64(5), + }, + { + name: "int8 with negative d", + xExpr: expression.NewLiteral(52, types.Uint8), + dExpr: expression.NewLiteral(-1, types.Int32), + exp: uint64(50), + }, + { + name: "int8 with float d", + xExpr: expression.NewLiteral(5, types.Uint8), + dExpr: expression.NewLiteral(2.123, types.Float64), + exp: uint64(5), + }, + { + name: "int8 with float negative d", + xExpr: expression.NewLiteral(52, types.Uint8), + dExpr: expression.NewLiteral(-1.0, types.Float32), + exp: uint64(50), + }, + { + name: "int8 with blob d", + xExpr: expression.NewLiteral(5, types.Uint8), + dExpr: expression.NewLiteral([]byte{'1', '2', '3'}, types.Blob), + exp: uint64(5), + }, + + { + name: "text int without d", + xExpr: expression.NewLiteral("5", types.Text), + exp: 5.0, + }, + { + name: "text int with d", + xExpr: expression.NewLiteral("5", types.Text), + dExpr: expression.NewLiteral(2, types.Int32), + exp: 5.0, + }, + { + name: "text int with negative d", + xExpr: expression.NewLiteral("52", types.Text), + dExpr: expression.NewLiteral(-1, types.Int32), + exp: 50.0, + }, + { + name: "text int with float d", + xExpr: expression.NewLiteral("5", types.Text), + dExpr: expression.NewLiteral(2.123, types.Float64), + exp: 5.0, + }, + { + name: "text int with float negative d", + xExpr: expression.NewLiteral("52", types.Text), + dExpr: expression.NewLiteral(-1.0, types.Float32), + exp: 50.0, + }, + { + name: "text int with blob d", + xExpr: expression.NewLiteral("5", types.Text), + dExpr: expression.NewLiteral([]byte{'1', '2', '3'}, types.Blob), + exp: 5.0, + }, - // Test on invalid type return 0 - var args = make([]sql.Expression, 2) - args[0] = expression.NewGetField(0, types.Blob, "", false) - args[1] = expression.NewGetField(1, types.Int32, "", false) + { + name: "text float without d", + xExpr: expression.NewLiteral("5.8", types.Text), + exp: 6.0, + }, + { + name: "text float with d", + xExpr: expression.NewLiteral("5.855", types.Text), + dExpr: expression.NewLiteral(2, types.Int32), + exp: 5.86, + }, + { + name: "text float with negative d", + xExpr: expression.NewLiteral("52.855", types.Text), + dExpr: expression.NewLiteral(-1, types.Int32), + exp: 50.0, + }, + { + name: "text float with float d", + xExpr: expression.NewLiteral("5.855", types.Text), + dExpr: expression.NewLiteral(2.123, types.Float64), + exp: 5.86, + }, + { + name: "text float with float negative d", + xExpr: expression.NewLiteral("52.855", types.Text), + dExpr: expression.NewLiteral(-1.0, types.Float32), + exp: 50.0, + }, + { + name: "text float with blob d", + xExpr: expression.NewLiteral(5, types.Text), + dExpr: expression.NewLiteral([]byte{'1', '2', '3'}, types.Blob), + exp: 5.0, + }, - f, err := NewRound(args...) - req := require.New(t) - req.Nil(err) + { + name: "blob is nil", + xExpr: expression.NewLiteral(nil, types.Blob), + dExpr: expression.NewLiteral(nil, types.Int32), + exp: nil, + }, + { + name: "blob is ok", + xExpr: expression.NewLiteral([]byte{'1', '2', '3'}, types.Blob), + exp: 123.0, + }, - exprs := f.Children() - req.True(len(exprs) > 0 && len(exprs) < 3) - req.NotNil(exprs[0]) + // TODO: tests truncated strings + } + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + f, err := NewRound(tt.xExpr, tt.dExpr) + require.NoError(t, err) - result, err := f.Eval(sql.NewEmptyContext(), sql.NewRow([]byte{1, 2, 3}, 2)) - req.NoError(err) - req.Equal(float64(66051), result) + res, err := f.Eval(sql.NewEmptyContext(), nil) + if tt.err != nil { + require.Error(t, err) + require.True(t, tt.err.Is(err)) + return + } + require.NoError(t, err) + require.Equal(t, tt.exp, res) + }) + } }