mirror of
https://github.com/chaisql/chai.git
synced 2025-10-08 17:10:08 +08:00
Handle integer conversion errors (#425)
When converting a double larger or equal to math.MaxInt64 to an integer, it previously overflowed silently. It now returns an explicit error. This fixes along the way, the math.abs(-9223372036854775808) issue.
This commit is contained in:

committed by
GitHub

parent
daf4f79e9f
commit
01c87d1bf7
@@ -14,11 +14,6 @@ func CastAs(v types.Value, t types.ValueType) (types.Value, error) {
|
|||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Null values always remain null.
|
|
||||||
if v.Type() == types.NullValue {
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch t {
|
switch t {
|
||||||
case types.BoolValue:
|
case types.BoolValue:
|
||||||
return CastAsBool(v)
|
return CastAsBool(v)
|
||||||
@@ -45,6 +40,11 @@ func CastAs(v types.Value, t types.ValueType) (types.Value, error) {
|
|||||||
// it fails if the text doesn't contain a valid boolean.
|
// it fails if the text doesn't contain a valid boolean.
|
||||||
// Any other type is considered an invalid cast.
|
// Any other type is considered an invalid cast.
|
||||||
func CastAsBool(v types.Value) (types.Value, error) {
|
func CastAsBool(v types.Value) (types.Value, error) {
|
||||||
|
// Null values always remain null.
|
||||||
|
if v.Type() == types.NullValue {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
switch v.Type() {
|
switch v.Type() {
|
||||||
case types.BoolValue:
|
case types.BoolValue:
|
||||||
return v, nil
|
return v, nil
|
||||||
@@ -70,6 +70,11 @@ func CastAsBool(v types.Value) (types.Value, error) {
|
|||||||
// It fails if the text doesn't contain a valid float value.
|
// It fails if the text doesn't contain a valid float value.
|
||||||
// Any other type is considered an invalid cast.
|
// Any other type is considered an invalid cast.
|
||||||
func CastAsInteger(v types.Value) (types.Value, error) {
|
func CastAsInteger(v types.Value) (types.Value, error) {
|
||||||
|
// Null values always remain null.
|
||||||
|
if v.Type() == types.NullValue {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
switch v.Type() {
|
switch v.Type() {
|
||||||
case types.IntegerValue:
|
case types.IntegerValue:
|
||||||
return v, nil
|
return v, nil
|
||||||
@@ -79,7 +84,11 @@ func CastAsInteger(v types.Value) (types.Value, error) {
|
|||||||
}
|
}
|
||||||
return types.NewIntegerValue(0), nil
|
return types.NewIntegerValue(0), nil
|
||||||
case types.DoubleValue:
|
case types.DoubleValue:
|
||||||
return types.NewIntegerValue(int64(v.V().(float64))), nil
|
f := v.V().(float64)
|
||||||
|
if f > 0 && int64(f) < 0 {
|
||||||
|
return nil, stringutil.Errorf("integer out of range")
|
||||||
|
}
|
||||||
|
return types.NewIntegerValue(int64(f)), nil
|
||||||
case types.TextValue:
|
case types.TextValue:
|
||||||
i, err := strconv.ParseInt(v.V().(string), 10, 64)
|
i, err := strconv.ParseInt(v.V().(string), 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -102,6 +111,11 @@ func CastAsInteger(v types.Value) (types.Value, error) {
|
|||||||
// it fails if the text doesn't contain a valid float value.
|
// it fails if the text doesn't contain a valid float value.
|
||||||
// Any other type is considered an invalid cast.
|
// Any other type is considered an invalid cast.
|
||||||
func CastAsDouble(v types.Value) (types.Value, error) {
|
func CastAsDouble(v types.Value) (types.Value, error) {
|
||||||
|
// Null values always remain null.
|
||||||
|
if v.Type() == types.NullValue {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
switch v.Type() {
|
switch v.Type() {
|
||||||
case types.DoubleValue:
|
case types.DoubleValue:
|
||||||
return v, nil
|
return v, nil
|
||||||
@@ -121,6 +135,11 @@ func CastAsDouble(v types.Value) (types.Value, error) {
|
|||||||
// CastAsText returns a JSON representation of v.
|
// CastAsText returns a JSON representation of v.
|
||||||
// If the representation is a string, it gets unquoted.
|
// If the representation is a string, it gets unquoted.
|
||||||
func CastAsText(v types.Value) (types.Value, error) {
|
func CastAsText(v types.Value) (types.Value, error) {
|
||||||
|
// Null values always remain null.
|
||||||
|
if v.Type() == types.NullValue {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
switch v.Type() {
|
switch v.Type() {
|
||||||
case types.TextValue:
|
case types.TextValue:
|
||||||
return v, nil
|
return v, nil
|
||||||
@@ -142,6 +161,11 @@ func CastAsText(v types.Value) (types.Value, error) {
|
|||||||
// Text: decodes a base64 string, otherwise fails.
|
// Text: decodes a base64 string, otherwise fails.
|
||||||
// Any other type is considered an invalid cast.
|
// Any other type is considered an invalid cast.
|
||||||
func CastAsBlob(v types.Value) (types.Value, error) {
|
func CastAsBlob(v types.Value) (types.Value, error) {
|
||||||
|
// Null values always remain null.
|
||||||
|
if v.Type() == types.NullValue {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
if v.Type() == types.BlobValue {
|
if v.Type() == types.BlobValue {
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
@@ -164,6 +188,11 @@ func CastAsBlob(v types.Value) (types.Value, error) {
|
|||||||
// Text: decodes a JSON array, otherwise fails.
|
// Text: decodes a JSON array, otherwise fails.
|
||||||
// Any other type is considered an invalid cast.
|
// Any other type is considered an invalid cast.
|
||||||
func CastAsArray(v types.Value) (types.Value, error) {
|
func CastAsArray(v types.Value) (types.Value, error) {
|
||||||
|
// Null values always remain null.
|
||||||
|
if v.Type() == types.NullValue {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
if v.Type() == types.ArrayValue {
|
if v.Type() == types.ArrayValue {
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
@@ -185,6 +214,11 @@ func CastAsArray(v types.Value) (types.Value, error) {
|
|||||||
// Text: decodes a JSON object, otherwise fails.
|
// Text: decodes a JSON object, otherwise fails.
|
||||||
// Any other type is considered an invalid cast.
|
// Any other type is considered an invalid cast.
|
||||||
func CastAsDocument(v types.Value) (types.Value, error) {
|
func CastAsDocument(v types.Value) (types.Value, error) {
|
||||||
|
// Null values always remain null.
|
||||||
|
if v.Type() == types.NullValue {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
if v.Type() == types.DocumentValue {
|
if v.Type() == types.DocumentValue {
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package document
|
package document
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/genjidb/genji/types"
|
"github.com/genjidb/genji/types"
|
||||||
@@ -70,6 +71,7 @@ func TestCastAs(t *testing.T) {
|
|||||||
{blobV, nil, true},
|
{blobV, nil, true},
|
||||||
{arrayV, nil, true},
|
{arrayV, nil, true},
|
||||||
{docV, nil, true},
|
{docV, nil, true},
|
||||||
|
{types.NewDoubleValue(math.MaxInt64 + 1), nil, true},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@@ -17,6 +17,8 @@ NULL
|
|||||||
2.0
|
2.0
|
||||||
! math.abs('foo')
|
! math.abs('foo')
|
||||||
'cannot cast "foo" as double'
|
'cannot cast "foo" as double'
|
||||||
|
! math.abs(-9223372036854775808)
|
||||||
|
'integer out of range'
|
||||||
|
|
||||||
-- test: math.acos
|
-- test: math.acos
|
||||||
> math.acos(NULL)
|
> math.acos(NULL)
|
||||||
|
@@ -152,7 +152,7 @@ func ExprRunner(t *testing.T, testfile string) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// eval it to get a proper Value
|
// eval it to get a proper Value
|
||||||
want, err := e.Eval(emptyEnv)
|
want, err := e.Eval(environment.New(nil))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// parse the given expr
|
// parse the given expr
|
||||||
@@ -160,7 +160,7 @@ func ExprRunner(t *testing.T, testfile string) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// eval it to get a proper Value
|
// eval it to get a proper Value
|
||||||
got, err := e.Eval(emptyEnv)
|
got, err := e.Eval(environment.New(nil))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// finally, compare those two
|
// finally, compare those two
|
||||||
@@ -170,12 +170,14 @@ func ExprRunner(t *testing.T, testfile string) {
|
|||||||
t.Run("NOK "+stmt.Expr, func(t *testing.T) {
|
t.Run("NOK "+stmt.Expr, func(t *testing.T) {
|
||||||
// parse the given epxr
|
// parse the given epxr
|
||||||
e, err := parser.NewParser(strings.NewReader(stmt.Expr)).ParseExpr()
|
e, err := parser.NewParser(strings.NewReader(stmt.Expr)).ParseExpr()
|
||||||
require.NoError(t, err)
|
if err != nil {
|
||||||
|
require.Regexp(t, regexp.MustCompile(regexp.QuoteMeta(stmt.Res)), err.Error())
|
||||||
// eval it, it should return an error
|
} else {
|
||||||
_, err = e.Eval(emptyEnv)
|
// eval it, it should return an error
|
||||||
require.NotNilf(t, err, "expected expr `%s` to return an error, got nil", stmt.Expr)
|
_, err = e.Eval(environment.New(nil))
|
||||||
require.Regexp(t, regexp.MustCompile(regexp.QuoteMeta(stmt.Res)), err.Error())
|
require.NotNilf(t, err, "expected expr `%s` to return an error, got nil", stmt.Expr)
|
||||||
|
require.Regexp(t, regexp.MustCompile(regexp.QuoteMeta(stmt.Res)), err.Error())
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user