Skip to content

Commit

Permalink
fix(spanner): invalid numeric should throw an error
Browse files Browse the repository at this point in the history
  • Loading branch information
hengfengli committed Apr 14, 2021
1 parent 531fdd5 commit 923c486
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 0 deletions.
30 changes: 30 additions & 0 deletions spanner/value.go
Expand Up @@ -24,6 +24,7 @@ import (
"math/big"
"reflect"
"strconv"
"strings"
"time"

"cloud.google.com/go/civil"
Expand Down Expand Up @@ -56,6 +57,27 @@ func NumericString(r *big.Rat) string {
return r.FloatString(NumericScaleDigits)
}

// validateNumeric returns nil if there are no errors. It will return an error
// when the numeric number is not valid.
func validateNumeric(r *big.Rat) error {
// Add one more digit to the scale component to find out if there are more
// digits than required.
strRep := strings.TrimRight(r.FloatString(NumericScaleDigits+1), "0")
s := strings.Split(strRep, ".")
if len(s) != 2 {
return fmt.Errorf("invalid numeric float string: %s", strRep)
}
whole := s[0]
scale := s[1]
if len(scale) > NumericScaleDigits {
return fmt.Errorf("max scale for a numeric is %d. The requested numeric has more", NumericScaleDigits)
}
if len(whole) > NumericPrecisionDigits-NumericScaleDigits {
return fmt.Errorf("max precision for the whole component of a numeric is %d. The requested numeric has a whole component with precision %d", NumericPrecisionDigits-NumericScaleDigits, len(whole))
}
return nil
}

var (
// CommitTimestamp is a special value used to tell Cloud Spanner to insert
// the commit timestamp of the transaction into a column. It can be used in
Expand Down Expand Up @@ -2654,6 +2676,10 @@ func encodeValue(v interface{}) (*proto3.Value, *sppb.Type, error) {
pt = listType(floatType())
case big.Rat:
pb.Kind = stringKind(NumericString(&v))
err = validateNumeric(&v)
if err != nil {
return nil, nil, err
}
pt = numericType()
case []big.Rat:
if v != nil {
Expand All @@ -2680,6 +2706,10 @@ func encodeValue(v interface{}) (*proto3.Value, *sppb.Type, error) {
if v != nil {
pb.Kind = stringKind(NumericString(v))
}
err = validateNumeric(v)
if err != nil {
return nil, nil, err
}
pt = numericType()
case []*big.Rat:
if v != nil {
Expand Down
31 changes: 31 additions & 0 deletions spanner/value_test.go
Expand Up @@ -406,6 +406,37 @@ func TestEncodeValue(t *testing.T) {
}
}

// Test encoding invalid values.
func TestEncodeInvalidValues(t *testing.T) {
type CustomNumeric big.Rat

invalidNumPtr1 := big.NewRat(11234567891, 1e10)
invalidNumPtr2, _ := (&big.Rat{}).SetString("199999999999999999999999999999.999999999")

for i, test := range []struct {
desc string
in interface{}
errMsg string
}{
// NUMERIC
{desc: "numeric pointer with invalid scale component", in: invalidNumPtr1, errMsg: "max scale for a numeric is 9. The requested numeric has more"},
{desc: "numeric pointer with invalid whole component", in: invalidNumPtr2, errMsg: "max precision for the whole component of a numeric is 29. The requested numeric has a whole component with precision 30"},
{desc: "numeric with invalid scale component", in: *invalidNumPtr1, errMsg: "max scale for a numeric is 9. The requested numeric has more"},
{desc: "numeric with invalid whole component", in: *invalidNumPtr2, errMsg: "max precision for the whole component of a numeric is 29. The requested numeric has a whole component with precision 30"},
// CUSTOM NUMERIC
{desc: "custom numeric type with invalid scale component", in: CustomNumeric(*invalidNumPtr1), errMsg: "max scale for a numeric is 9. The requested numeric has more"},
{desc: "custom numeric type with invalid whole component", in: CustomNumeric(*invalidNumPtr2), errMsg: "max precision for the whole component of a numeric is 29. The requested numeric has a whole component with precision 30"},
} {
_, _, err := encodeValue(test.in)
if err == nil {
t.Fatalf("#%d (%s): want error during encoding, but got nil", i, test.desc)
}
if err.Error() != test.errMsg {
t.Errorf("#%d (%s): incorrect error message, got %v, want %v", i, test.desc, err, test.errMsg)
}
}
}

type encodeTest struct {
desc string
in interface{}
Expand Down

0 comments on commit 923c486

Please sign in to comment.