Address the overflow issue with values outside the int64 range

This commit is contained in:
Kuba Podgórski 2020-07-01 14:08:20 +02:00
parent d6caf34c02
commit 4bed1df101
2 changed files with 110 additions and 5 deletions

View file

@ -4486,25 +4486,37 @@ func (s SignedRow) ToTable() (*pb.TableResponse, error) {
// ToRows implements the ToRowser interface.
func (s SignedRow) ToRows(callback func(*pb.RowResponse) error) error {
// TODO: address the overflow issue with values outside the int64 range
ci := []*pb.ColumnInfo{{Name: s.Field(), Datatype: "int64"}}
negs := s.Neg.Columns()
for i := len(negs) - 1; i >= 0; i-- {
val, err := toNegInt64(negs[i])
if err != nil {
return errors.Wrap(err, "converting uint64 to int64 (negative)")
}
if err := callback(&pb.RowResponse{
Headers: ci,
Columns: []*pb.ColumnResponse{
&pb.ColumnResponse{ColumnVal: &pb.ColumnResponse_Int64Val{Int64Val: -1 * int64(negs[i])}},
}}); err != nil {
&pb.ColumnResponse{ColumnVal: &pb.ColumnResponse_Int64Val{Int64Val: val}},
},
}); err != nil {
return errors.Wrap(err, "calling callback")
}
ci = nil
}
for _, id := range s.Pos.Columns() {
val, err := toInt64(id)
if err != nil {
return errors.Wrap(err, "converting uint64 to int64 (positive)")
}
if err := callback(&pb.RowResponse{
Headers: ci,
Columns: []*pb.ColumnResponse{
&pb.ColumnResponse{ColumnVal: &pb.ColumnResponse_Int64Val{Int64Val: int64(id)}},
}}); err != nil {
&pb.ColumnResponse{ColumnVal: &pb.ColumnResponse_Int64Val{Int64Val: val}},
},
}); err != nil {
return errors.Wrap(err, "calling callback")
}
ci = nil
@ -4512,6 +4524,31 @@ func (s SignedRow) ToRows(callback func(*pb.RowResponse) error) error {
return nil
}
func toNegInt64(n uint64) (int64, error) {
const absMinInt64 = uint64(1 << 63)
if n > absMinInt64 {
return 0, errors.Errorf("value %d overflows int64", n)
}
if n == absMinInt64 {
return int64(-1 << 63), nil
}
// n < 1 << 63
return -int64(n), nil
}
func toInt64(n uint64) (int64, error) {
const maxInt64 = uint64(1<<63) - 1
if n > maxInt64 {
return 0, errors.Errorf("value %d overflows int64", n)
}
return int64(n), nil
}
func (sr *SignedRow) union(other SignedRow) SignedRow {
ret := SignedRow{&Row{}, &Row{}, ""}

View file

@ -495,3 +495,71 @@ func TestValCountComparisons(t *testing.T) {
})
}
}
func TestToNegInt64(t *testing.T) {
tests := []struct {
u64 uint64
i64 int64
overflow bool
}{
{
u64: uint64(1 << 63),
i64: int64(-1 << 63),
},
{
u64: uint64(1<<63) - 1,
i64: int64(-1<<63) + 1,
},
{
u64: uint64(1<<63) + 1,
overflow: true,
},
}
for _, tc := range tests {
val, err := toNegInt64(tc.u64)
if err != nil && !tc.overflow {
t.Fatalf("error: %+v, expected: %+v", err, tc)
}
if val != tc.i64 {
t.Fatalf("Expected: %+v, Got: %+v", tc.i64, val)
}
}
}
func TestToInt64(t *testing.T) {
tests := []struct {
u64 uint64
i64 int64
overflow bool
}{
{
u64: uint64(1<<63) - 1,
i64: 1<<63 - 1,
},
{
u64: uint64(0),
i64: 0,
},
{
u64: uint64(1 << 63),
overflow: true,
},
{
u64: 1<<64 - 1,
overflow: true,
},
}
for _, tc := range tests {
val, err := toInt64(tc.u64)
if err != nil && !tc.overflow {
t.Fatalf("error: %+v, expected: %+v", err, tc)
}
if val != tc.i64 {
t.Fatalf("Expected: %+v, Got: %+v", tc.i64, val)
}
}
}