From f6a2c045aca6073cfa60414de8fd729e9f07bd41 Mon Sep 17 00:00:00 2001 From: Gus <8219721+srfrog@users.noreply.github.com> Date: Mon, 21 Jan 2019 22:59:25 -0700 Subject: [PATCH] Handling of empty string to datetime conversion (#2891) * commented unused func indexCellsForCap (linter). * let conversion of empty string to datetime work for zero time value. linter fixes. * added Converter tests * linter fixes * removed unused func createDate and other linter fixes * convert zero-time value to empty/zero values of other types. code formatting changes. * added tests for 0 int/float to convert to datatime zero-time value. added more tests and fixed format. * refactored tests for better legibility and support * removed unlikely test on time value * reformatted tests * minor cleanups * added binary, bool and datetime tests * added bool to string test * minor cleanups --- types/conversion.go | 184 ++++++----- types/conversion_test.go | 642 ++++++++++++++++++++------------------- types/earth.go | 16 +- types/s2index.go | 4 +- types/scalar_types.go | 10 +- 5 files changed, 426 insertions(+), 430 deletions(-) diff --git a/types/conversion.go b/types/conversion.go index b6c0a3d9712..31038b2f2ea 100644 --- a/types/conversion.go +++ b/types/conversion.go @@ -60,26 +60,22 @@ func Convert(from Val, toID TypeID) (Val, error) { return to, x.Errorf("Invalid data for float %v", data) } i := binary.LittleEndian.Uint64(data) - val := math.Float64frombits(i) - *res = float64(val) + *res = math.Float64frombits(i) case BoolID: - if len(data) == 0 { + if len(data) == 0 || data[0] == 0 { *res = false - break - } - if data[0] == 0 { - *res = bool(false) return to, nil } else if data[0] == 1 { - *res = bool(true) + *res = true return to, nil - } else { - return to, x.Errorf("Invalid value for bool %v", data[0]) } + return to, x.Errorf("Invalid value for bool %v", data[0]) case DateTimeID: var t time.Time - if err := t.UnmarshalBinary(data); err != nil { - return to, err + if !bytes.Equal(data, []byte("")) { + if err := t.UnmarshalBinary(data); err != nil { + return to, err + } } *res = t case GeoID: @@ -99,15 +95,13 @@ func Convert(from Val, toID TypeID) (Val, error) { vc := string(data) switch toID { case BinaryID: - // Marshal Binary *res = []byte(vc) case IntID: - // Marshal text. val, err := strconv.ParseInt(vc, 10, 64) if err != nil { return to, err } - *res = int64(val) + *res = val case FloatID: val, err := strconv.ParseFloat(vc, 64) if err != nil { @@ -116,7 +110,7 @@ func Convert(from Val, toID TypeID) (Val, error) { if math.IsNaN(val) { return to, fmt.Errorf("Got invalid value: NaN") } - *res = float64(val) + *res = val case StringID, DefaultID: *res = vc case BoolID: @@ -126,14 +120,17 @@ func Convert(from Val, toID TypeID) (Val, error) { if err != nil { return to, err } - *res = bool(val) + *res = val } case DateTimeID: - t, err := ParseTime(vc) - if err != nil { - return to, err + *res = time.Time{} + if vc != "" { + t, err := ParseTime(vc) + if err != nil { + return to, err + } + *res = t } - *res = t case GeoID: var g geom.T text := bytes.Replace([]byte(vc), []byte("'"), []byte("\""), -1) @@ -162,18 +159,20 @@ func Convert(from Val, toID TypeID) (Val, error) { case IntID: *res = vc case BinaryID: - // Marshal Binary var bs [8]byte binary.LittleEndian.PutUint64(bs[:], uint64(vc)) *res = bs[:] case FloatID: *res = float64(vc) case BoolID: - *res = bool(vc != 0) + *res = vc != 0 case StringID, DefaultID: - *res = string(strconv.FormatInt(vc, 10)) + *res = strconv.FormatInt(vc, 10) case DateTimeID: - *res = time.Unix(vc, 0).UTC() + *res = time.Time{} + if vc != 0 { + *res = time.Unix(vc, 0).UTC() + } default: return to, cantConvert(fromID, toID) } @@ -184,31 +183,32 @@ func Convert(from Val, toID TypeID) (Val, error) { return to, x.Errorf("Invalid data for float %v", data) } i := binary.LittleEndian.Uint64(data) - val := math.Float64frombits(i) - vc := float64(val) + vc := math.Float64frombits(i) switch toID { case FloatID: *res = vc case BinaryID: - // Marshal Binary var bs [8]byte - u := math.Float64bits(float64(vc)) + u := math.Float64bits(vc) binary.LittleEndian.PutUint64(bs[:], u) *res = bs[:] case IntID: - if vc > math.MaxInt64 || vc < math.MinInt64 || math.IsNaN(float64(vc)) { + if vc > math.MaxInt64 || vc < math.MinInt64 || math.IsNaN(vc) { return to, x.Errorf("Float out of int64 range") } *res = int64(vc) case BoolID: - *res = bool(vc != 0) + *res = vc != 0 case StringID, DefaultID: - *res = string(strconv.FormatFloat(float64(vc), 'G', -1, 64)) + *res = strconv.FormatFloat(vc, 'G', -1, 64) case DateTimeID: - secs := int64(vc) - fracSecs := vc - float64(secs) - nsecs := int64(fracSecs * nanoSecondsInSec) - *res = time.Unix(secs, nsecs).UTC() + *res = time.Time{} + if vc != 0 { + secs := int64(vc) + fracSecs := vc - float64(secs) + nsecs := int64(fracSecs * nanoSecondsInSec) + *res = time.Unix(secs, nsecs).UTC() + } default: return to, cantConvert(fromID, toID) } @@ -216,11 +216,12 @@ func Convert(from Val, toID TypeID) (Val, error) { case BoolID: { var vc bool - if data[0] == 0 { - vc = bool(false) - } else if data[0] == 1 { - vc = bool(true) - } else { + switch { + case data[0] == 0: + vc = false + case data[0] == 1: + vc = true + default: return to, x.Errorf("Invalid value for bool %v", data[0]) } @@ -228,13 +229,10 @@ func Convert(from Val, toID TypeID) (Val, error) { case BoolID: *res = vc case BinaryID: - // Marshal Binary - var bs [1]byte - bs[0] = 0 + *res = []byte{0} if vc { - bs[0] = 1 + *res = []byte{1} } - *res = bs[:] case IntID: *res = int64(0) if vc { @@ -246,7 +244,7 @@ func Convert(from Val, toID TypeID) (Val, error) { *res = float64(1) } case StringID, DefaultID: - *res = string(strconv.FormatBool(bool(vc))) + *res = strconv.FormatBool(vc) default: return to, cantConvert(fromID, toID) } @@ -257,34 +255,39 @@ func Convert(from Val, toID TypeID) (Val, error) { if err := t.UnmarshalBinary(data); err != nil { return to, err } - vc := t + // NOTE: when converting datetime values to anything else, we must + // check for zero-time value and return the zero value of the new type. switch toID { case DateTimeID: - *res = vc + *res = t case BinaryID: - // Marshal Binary - r, err := vc.MarshalBinary() - if err != nil { - return to, err + *res = []byte("") + if !t.IsZero() { + r, err := t.MarshalBinary() + if err != nil { + return to, err + } + *res = r } - *res = r case StringID, DefaultID: - val, err := vc.MarshalText() - if err != nil { - return to, err + *res = "" + if !t.IsZero() { + val, err := t.MarshalText() + if err != nil { + return to, err + } + *res = string(val) } - *res = string(val) case IntID: - secs := vc.Unix() - if secs > math.MaxInt64 || secs < math.MinInt64 { - return to, x.Errorf("Time out of int64 range") + *res = int64(0) + if !t.IsZero() { + *res = t.Unix() } - *res = int64(secs) case FloatID: - secs := float64(vc.Unix()) - nano := float64(vc.Nanosecond()) - val := secs + nano/nanoSecondsInSec - *res = float64(val) + *res = float64(0) + if !t.IsZero() { + *res = float64(t.UnixNano()) / float64(nanoSecondsInSec) + } default: return to, cantConvert(fromID, toID) } @@ -299,7 +302,6 @@ func Convert(from Val, toID TypeID) (Val, error) { case GeoID: *res = vc case BinaryID: - // Marshal Binary r, err := wkb.Marshal(vc, binary.LittleEndian) if err != nil { return to, err @@ -320,7 +322,6 @@ func Convert(from Val, toID TypeID) (Val, error) { vc := string(data) switch toID { case BinaryID: - // Marshal Binary *res = []byte(vc) case StringID, PasswordID: *res = vc @@ -347,8 +348,7 @@ func Marshal(from Val, to *Val) error { case StringID, DefaultID: *res = string(vc) case BinaryID: - // Marshal Binary - *res = []byte(vc) + *res = vc default: return cantConvert(fromID, toID) } @@ -358,7 +358,6 @@ func Marshal(from Val, to *Val) error { case StringID, DefaultID: *res = vc case BinaryID: - // Marshal Binary *res = []byte(vc) default: return cantConvert(fromID, toID) @@ -367,9 +366,8 @@ func Marshal(from Val, to *Val) error { vc := val.(int64) switch toID { case StringID, DefaultID: - *res = strconv.FormatInt(int64(vc), 10) + *res = strconv.FormatInt(vc, 10) case BinaryID: - // Marshal Binary var bs [8]byte binary.LittleEndian.PutUint64(bs[:], uint64(vc)) *res = bs[:] @@ -380,11 +378,10 @@ func Marshal(from Val, to *Val) error { vc := val.(float64) switch toID { case StringID, DefaultID: - *res = strconv.FormatFloat(float64(vc), 'G', -1, 64) + *res = strconv.FormatFloat(vc, 'G', -1, 64) case BinaryID: - // Marshal Binary var bs [8]byte - u := math.Float64bits(float64(vc)) + u := math.Float64bits(vc) binary.LittleEndian.PutUint64(bs[:], u) *res = bs[:] default: @@ -394,15 +391,12 @@ func Marshal(from Val, to *Val) error { vc := val.(bool) switch toID { case StringID, DefaultID: - *res = strconv.FormatBool(bool(vc)) + *res = strconv.FormatBool(vc) case BinaryID: - // Marshal Binary - var bs [1]byte - bs[0] = 0 + *res = []byte{0} if vc { - bs[0] = 1 + *res = []byte{1} } - *res = bs[:] default: return cantConvert(fromID, toID) } @@ -410,18 +404,23 @@ func Marshal(from Val, to *Val) error { vc := val.(time.Time) switch toID { case StringID, DefaultID: - val, err := vc.MarshalText() - if err != nil { - return err + *res = "" + if !vc.IsZero() { + val, err := vc.MarshalText() + if err != nil { + return err + } + *res = string(val) } - *res = string(val) case BinaryID: - // Marshal Binary - r, err := vc.MarshalBinary() - if err != nil { - return err + *res = []byte("") + if !vc.IsZero() { + r, err := vc.MarshalBinary() + if err != nil { + return err + } + *res = r } - *res = r default: return cantConvert(fromID, toID) } @@ -432,7 +431,6 @@ func Marshal(from Val, to *Val) error { } switch toID { case BinaryID: - // Marshal Binary r, err := wkb.Marshal(vc, binary.LittleEndian) if err != nil { return err @@ -453,12 +451,10 @@ func Marshal(from Val, to *Val) error { case StringID: *res = vc case BinaryID: - // Marshal Binary *res = []byte(vc) default: return cantConvert(fromID, toID) } - default: return cantConvert(fromID, toID) } diff --git a/types/conversion_test.go b/types/conversion_test.go index 9d37d826272..44f17093808 100644 --- a/types/conversion_test.go +++ b/types/conversion_test.go @@ -64,6 +64,21 @@ func TestSameConversionString(t *testing.T) { } } +func TestSameConversionDateTime(t *testing.T) { + tests := []struct { + in time.Time + }{ + {in: time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC)}, + {in: time.Time{}}, + } + + for _, tc := range tests { + out, err := Convert(Val{Tid: BinaryID, Value: bs(tc.in)}, DateTimeID) + require.NoError(t, err) + require.EqualValues(t, Val{Tid: DateTimeID, Value: tc.in}, out) + } +} + func TestConvertToDefault(t *testing.T) { tests := []struct { in Val @@ -75,8 +90,8 @@ func TestConvertToDefault(t *testing.T) { {in: Val{BinaryID, []byte("2016")}, out: "2016"}, {in: Val{IntID, bs(int64(3))}, out: "3"}, {in: Val{FloatID, bs(float64(-3.5))}, out: "-3.5"}, - {in: Val{DateTimeID, bs(time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC))}, - out: "2006-01-02T15:04:05Z"}, + {in: Val{DateTimeID, bs(time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC))}, out: "2006-01-02T15:04:05Z"}, + {in: Val{DateTimeID, bs(time.Time{})}, out: ""}, } for _, tc := range tests { @@ -99,6 +114,7 @@ func TestConvertFromDefault(t *testing.T) { {in: "hello", out: Val{StringID, "hello"}}, {in: "", out: Val{StringID, ""}}, {in: "2016", out: Val{DateTimeID, time.Date(2016, 1, 1, 0, 0, 0, 0, time.UTC)}}, + {in: "", out: Val{DateTimeID, time.Time{}}}, } for _, tc := range tests { @@ -108,6 +124,55 @@ func TestConvertFromDefault(t *testing.T) { } } +func TestConvertToBinary(t *testing.T) { + tests := []struct { + in Val + out []byte + }{ + {in: Val{StringID, []byte("a")}, out: []byte("a")}, + {in: Val{StringID, []byte("")}, out: []byte("")}, + {in: Val{DefaultID, []byte("abc")}, out: []byte("abc")}, + {in: Val{BinaryID, []byte("2016")}, out: []byte("2016")}, + {in: Val{IntID, bs(int64(3))}, out: bs(int64(3))}, + {in: Val{FloatID, bs(float64(-3.5))}, out: bs(float64(-3.5))}, + {in: Val{DateTimeID, bs(time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC))}, out: bs(time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC))}, + {in: Val{DateTimeID, bs(time.Time{})}, out: []byte("")}, + } + + for _, tc := range tests { + out, err := Convert(tc.in, BinaryID) + require.NoError(t, err) + require.EqualValues(t, Val{Tid: BinaryID, Value: tc.out}, out) + } +} + +func TestConvertFromBinary(t *testing.T) { + tests := []struct { + in []byte + out Val + }{ + {in: bs(true), out: Val{BoolID, true}}, + {in: bs(false), out: Val{BoolID, false}}, + {in: []byte(""), out: Val{BoolID, false}}, + {in: nil, out: Val{BoolID, false}}, + {in: bs(int64(1)), out: Val{IntID, int64(1)}}, + {in: bs(float64(1.3)), out: Val{FloatID, float64(1.3)}}, + {in: []byte("2016"), out: Val{BinaryID, []byte("2016")}}, + {in: []byte(""), out: Val{BinaryID, []byte("")}}, + {in: []byte("hello"), out: Val{StringID, "hello"}}, + {in: []byte(""), out: Val{StringID, ""}}, + {in: bs(time.Date(2016, 1, 1, 0, 0, 0, 0, time.UTC)), out: Val{DateTimeID, time.Date(2016, 1, 1, 0, 0, 0, 0, time.UTC)}}, + {in: bs(time.Time{}), out: Val{DateTimeID, time.Time{}}}, + {in: []byte(""), out: Val{DateTimeID, time.Time{}}}, + } + + for _, tc := range tests { + out, err := Convert(Val{BinaryID, tc.in}, tc.out.Tid) + require.NoError(t, err) + require.EqualValues(t, tc.out, out) + } +} + func TestConvertStringToDateTime(t *testing.T) { tests := []struct { in string @@ -117,6 +182,7 @@ func TestConvertStringToDateTime(t *testing.T) { {in: "2006-01-02", out: time.Date(2006, 01, 02, 0, 0, 0, 0, time.UTC)}, {in: "2006-01", out: time.Date(2006, 01, 01, 0, 0, 0, 0, time.UTC)}, {in: "2006", out: time.Date(2006, 01, 01, 0, 0, 0, 0, time.UTC)}, + {in: "", out: time.Time{}}, } for _, tc := range tests { @@ -126,6 +192,24 @@ func TestConvertStringToDateTime(t *testing.T) { } } +func TestConvertDateTimeToString(t *testing.T) { + tests := []struct { + in time.Time + out string + }{ + {in: time.Date(2006, 01, 02, 15, 04, 05, 0, time.UTC), out: "2006-01-02T15:04:05Z"}, + {in: time.Date(2006, 01, 02, 0, 0, 0, 0, time.UTC), out: "2006-01-02T00:00:00Z"}, + {in: time.Date(2006, 01, 01, 0, 0, 0, 0, time.UTC), out: "2006-01-01T00:00:00Z"}, + {in: time.Time{}, out: ""}, + } + + for _, tc := range tests { + out, err := Convert(Val{Tid: DateTimeID, Value: bs(tc.in)}, StringID) + require.NoError(t, err) + require.EqualValues(t, Val{Tid: StringID, Value: tc.out}, out) + } +} + func TestConvertFromPassword(t *testing.T) { tests := []struct { in string @@ -210,6 +294,21 @@ func TestConvertToPassword(t *testing.T) { } } +func TestSameConversionBool(t *testing.T) { + tests := []struct { + in bool + }{ + {in: true}, + {in: false}, + } + + for _, tc := range tests { + out, err := Convert(Val{Tid: BinaryID, Value: bs(tc.in)}, BoolID) + require.NoError(t, err) + require.EqualValues(t, Val{Tid: BoolID, Value: tc.in}, out) + } +} + func TestConvertIntToBool(t *testing.T) { tests := []struct { in int64 @@ -353,396 +452,303 @@ func TestFalsy(t *testing.T) { } } -/* func TestSameConversionFloat(t *testing.T) { - data := []struct { - in Float - out Float + tests := []struct { + in float64 }{ - {3.4434, 3.4434}, - {-3, -3}, - {0.5e2, 0.5e2}, - } - for _, tc := range data { - if out, err := Convert(&tc.in, FloatID); err != nil { - t.Errorf("Unexpected error converting int to bool: %v", err) - } else if *(out.(*Float)) != tc.out { - t.Errorf("Converting float to float: Expected %v, got %v", tc.out, out) - } + {in: float64(3.4434)}, + {in: float64(-3.0)}, + {in: float64(0.5e2)}, } -} -func TestSameConversionInt(t *testing.T) { - data := []struct { - in Int32 - out Int32 - }{ - {3, 3}, - {-3, -3}, - {0, 0}, - } - for _, tc := range data { - if out, err := Convert(&tc.in, IntID); err != nil { - t.Errorf("Unexpected error converting int to bool: %v", err) - } else if *(out.(*Int32)) != tc.out { - t.Errorf("Converting int to int: Expected %v, got %v", tc.out, out) - } + for _, tc := range tests { + out, err := Convert(Val{Tid: BinaryID, Value: bs(tc.in)}, FloatID) + require.NoError(t, err) + require.EqualValues(t, Val{Tid: FloatID, Value: tc.in}, out) } } - -func TestConvertInt32ToBool(t *testing.T) { - data := []struct { - in Int32 - out Bool +func TestSameConversionInt(t *testing.T) { + tests := []struct { + in int64 }{ - {3, true}, - {-3, true}, - {0, false}, - } - for _, tc := range data { - if out, err := Convert(&tc.in, BoolID); err != nil { - t.Errorf("Unexpected error converting int to bool: %v", err) - } else if *(out.(*Bool)) != tc.out { - t.Errorf("Converting int to bool: Expected %v, got %v", tc.out, out) - } + {in: int64(3)}, + {in: int64(-3)}, + {in: int64(9999)}, + {in: int64(0)}, } -} -func TestConvertFloatToBool(t *testing.T) { - data := []struct { - in Float - out Bool - }{ - {3.0, true}, - {-3.5, true}, - {0, false}, - {-0.0, false}, - {Float(math.NaN()), true}, - {Float(math.Inf(1)), true}, - {Float(math.Inf(-1)), true}, - } - for _, tc := range data { - if out, err := Convert(&tc.in, BoolID); err != nil { - t.Errorf("Unexpected error converting float to bool: %v", err) - } else if *(out.(*Bool)) != tc.out { - t.Errorf("Converting float to bool: Expected %v, got %v", tc.out, out) - } + for _, tc := range tests { + out, err := Convert(Val{Tid: BinaryID, Value: bs(tc.in)}, IntID) + require.NoError(t, err) + require.EqualValues(t, Val{Tid: IntID, Value: tc.in}, out) } } -func TestConvertStringToBool(t *testing.T) { - data := []struct { - in String - out Bool +func TestConvertBoolToFloat(t *testing.T) { + tests := []struct { + in bool + out float64 }{ - {"1", true}, - {"true", true}, - {"True", true}, - {"T", true}, - {"F", false}, - {"0", false}, - {"false", false}, - {"False", false}, - } - for _, tc := range data { - if out, err := Convert(&tc.in, BoolID); err != nil { - t.Errorf("Unexpected error converting string to bool: %v", err) - } else if *(out.(*Bool)) != tc.out { - t.Errorf("Converting string to bool: Expected %v, got %v", tc.out, out) - } + {in: true, out: float64(1.0)}, + {in: false, out: float64(0.0)}, } - errData := []String{ - "hello", - "", - "3", - "-3", - } - - for _, tc := range errData { - if out, err := Convert(&tc, BoolID); err == nil { - t.Errorf("Expected error converting string %s to bool %v", tc, out) - } + for _, tc := range tests { + out, err := Convert(Val{Tid: BoolID, Value: bs(tc.in)}, FloatID) + require.NoError(t, err) + require.EqualValues(t, Val{Tid: FloatID, Value: tc.out}, out) } } -func TestConvertDateTimeToBool(t *testing.T) { - tm := Time{time.Now()} - if _, err := Convert(&tm, BoolID); err == nil { - t.Errorf("Expected error converting time to bool") +func TestConvertBoolToString(t *testing.T) { + tests := []struct { + in bool + out string + }{ + {in: true, out: "true"}, + {in: false, out: "false"}, } -} -func TestConvertBoolToInt32(t *testing.T) { - data := []struct { - in Bool - out Int32 - }{ - {true, 1}, - {false, 0}, - } - for _, tc := range data { - if out, err := Convert(&tc.in, IntID); err != nil { - t.Errorf("Unexpected error converting bool to int: %v", err) - } else if *(out.(*Int32)) != tc.out { - t.Errorf("Converting bool to in: Expected %v, got %v", tc.out, out) - } + for _, tc := range tests { + out, err := Convert(Val{Tid: BoolID, Value: bs(tc.in)}, StringID) + require.NoError(t, err) + require.EqualValues(t, Val{Tid: StringID, Value: tc.out}, out) } } -func TestConvertFloatToInt32(t *testing.T) { - data := []struct { - in Float - out Int32 +func TestConvertIntToFloat(t *testing.T) { + tests := []struct { + in int64 + out float64 }{ - {3.0, 3}, - {-3.5, -3}, - {0, 0}, - {-0.0, 0}, - } - for _, tc := range data { - if out, err := Convert(&tc.in, IntID); err != nil { - t.Errorf("Unexpected error converting float to int: %v", err) - } else if *(out.(*Int32)) != tc.out { - t.Errorf("Converting float to int: Expected %v, got %v", tc.out, out) - } - } - errData := []float64{ - math.NaN(), - math.Inf(1), - math.Inf(-1), - 522638295213.3243, - -522638295213.3243, + {in: int64(3), out: float64(3.0)}, + {in: int64(-3), out: float64(-3.0)}, + {in: int64(0), out: float64(0.0)}, } - for _, tc := range errData { - if out, err := Convert((*Float)(&tc), IntID); err == nil { - t.Errorf("Expected error converting float %f to int %v", tc, out) - } + + for _, tc := range tests { + out, err := Convert(Val{Tid: IntID, Value: bs(tc.in)}, FloatID) + require.NoError(t, err) + require.EqualValues(t, Val{Tid: FloatID, Value: tc.out}, out) } } -func TestConvertStringToInt32(t *testing.T) { - data := []struct { - in String - out Int32 +func TestConvertFloatToInt(t *testing.T) { + tests := []struct { + in float64 + out int64 + failure string }{ - {"1", 1}, - {"13816", 13816}, - {"-1221", -1221}, - {"0", 0}, - } - for _, tc := range data { - if out, err := Convert(&tc.in, IntID); err != nil { - t.Errorf("Unexpected error converting string to int: %v", err) - } else if *(out.(*Int32)) != tc.out { - t.Errorf("Converting string to int: Expected %v, got %v", tc.out, out) - } - } - - errData := []String{ - "hello", - "", - "3.0", - "-3a.5", - "203716381366627", + {in: float64(3), out: int64(3)}, + {in: float64(-3.0), out: int64(-3)}, + {in: float64(0), out: int64(0)}, + {in: float64(522638295213.3243), out: int64(522638295213)}, + {in: float64(-522638295213.3243), out: int64(-522638295213)}, + { + in: math.NaN(), + failure: "Float out of int64 range", + }, + { + in: math.Inf(0), + failure: "Float out of int64 range", + }, + { + in: math.Inf(-1), + failure: "Float out of int64 range", + }, } - for _, tc := range errData { - if out, err := Convert(&tc, IntID); err == nil { - t.Errorf("Expected error converting string %s to int %v", tc, out) + for _, tc := range tests { + out, err := Convert(Val{Tid: FloatID, Value: bs(tc.in)}, IntID) + if tc.failure != "" { + require.Error(t, err) + require.EqualError(t, err, tc.failure) + continue } + require.NoError(t, err) + require.EqualValues(t, Val{Tid: IntID, Value: tc.out}, out) } } -func TestConvertDateTimeToInt32(t *testing.T) { - data := []struct { - in time.Time - out Int32 +func TestConvertStringToInt(t *testing.T) { + tests := []struct { + in string + out int64 + failure string }{ - {time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), 1257894000}, - {time.Date(1969, time.November, 10, 23, 0, 0, 0, time.UTC), -4410000}, - } - for _, tc := range data { - if out, err := Convert(&Time{tc.in}, IntID); err != nil { - t.Errorf("Unexpected error converting time to int: %v", err) - } else if *(out.(*Int32)) != tc.out { - t.Errorf("Converting time to int: Expected %v, got %v", tc.out, out) - } - } - - errData := []time.Time{ - time.Date(2039, time.November, 10, 23, 0, 0, 0, time.UTC), - time.Date(1901, time.November, 10, 23, 0, 0, 0, time.UTC), + {in: "1", out: int64(1)}, + {in: "13816", out: int64(13816)}, + {in: "-1221", out: int64(-1221)}, + {in: "0", out: int64(0)}, + {in: "203716381366627", out: int64(203716381366627)}, + { + in: "srfrog", + failure: `strconv.ParseInt: parsing "srfrog": invalid syntax`, + }, + { + in: "", + failure: `strconv.ParseInt: parsing "": invalid syntax`, + }, + { + in: "3.0", + failure: `strconv.ParseInt: parsing "3.0": invalid syntax`, + }, + { + in: "-3a.5", + failure: `strconv.ParseInt: parsing "-3a.5": invalid syntax`, + }, } - for _, tc := range errData { - if out, err := Convert(&Time{tc}, IntID); err == nil { - t.Errorf("Expected error converting time %s to int %v", tc, out) + for _, tc := range tests { + out, err := Convert(Val{Tid: StringID, Value: []byte(tc.in)}, IntID) + if tc.failure != "" { + require.Error(t, err) + require.EqualError(t, err, tc.failure) + continue } + require.NoError(t, err) + require.EqualValues(t, Val{Tid: IntID, Value: tc.out}, out) } } -func TestConvertBoolToFloat(t *testing.T) { - data := []struct { - in Bool - out Float +func TestConvertStringToFloat(t *testing.T) { + tests := []struct { + in string + out float64 + failure string }{ - {true, 1.0}, - {false, 0.0}, - } - for _, tc := range data { - if out, err := Convert(&tc.in, FloatID); err != nil { - t.Errorf("Unexpected error converting bool to float: %v", err) - } else if *(out.(*Float)) != tc.out { - t.Errorf("Converting bool to float: Expected %v, got %v", tc.out, out) - } + {in: "1", out: float64(1)}, + {in: "13816.251", out: float64(13816.251)}, + {in: "-1221.12", out: float64(-1221.12)}, + {in: "-0.0", out: float64(-0.0)}, + {in: "1e10", out: float64(1e10)}, + {in: "1e-2", out: float64(0.01)}, + { + in: "srfrog", + failure: `strconv.ParseFloat: parsing "srfrog": invalid syntax`, + }, + { + in: "", + failure: `strconv.ParseFloat: parsing "": invalid syntax`, + }, + { + in: "-3a.5", + failure: `strconv.ParseFloat: parsing "-3a.5": invalid syntax`, + }, + { + in: "1e400", + failure: `strconv.ParseFloat: parsing "1e400": value out of range`, + }, } -} -func TestConvertInt32ToFloat(t *testing.T) { - data := []struct { - in Int32 - out Float - }{ - {3, 3.0}, - {-3, -3.0}, - {0, 0.0}, - } - for _, tc := range data { - if out, err := Convert(&tc.in, FloatID); err != nil { - t.Errorf("Unexpected error converting int to float: %v", err) - } else if *(out.(*Float)) != tc.out { - t.Errorf("Converting int to float: Expected %v, got %v", tc.out, out) + for _, tc := range tests { + out, err := Convert(Val{Tid: StringID, Value: []byte(tc.in)}, FloatID) + if tc.failure != "" { + require.Error(t, err) + require.EqualError(t, err, tc.failure) + continue } + require.NoError(t, err) + require.EqualValues(t, Val{Tid: FloatID, Value: tc.out}, out) } } -func TestConvertStringToFloat(t *testing.T) { - data := []struct { - in String - out Float +func TestConvertFloatToDateTime(t *testing.T) { + tests := []struct { + in float64 + out time.Time }{ - {"1", 1}, - {"13816.251", 13816.251}, - {"-1221.12", -1221.12}, - {"-0.0", -0.0}, - {"1e10", 1e10}, - {"1e-2", 0.01}, - } - for _, tc := range data { - if out, err := Convert(&tc.in, FloatID); err != nil { - t.Errorf("Unexpected error converting string to float: %v", err) - } else if *(out.(*Float)) != tc.out { - t.Errorf("Converting string to float: Expected %v, got %v", tc.out, out) - } + {in: float64(1257894000), out: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)}, + {in: float64(-4410000), out: time.Date(1969, time.November, 10, 23, 0, 0, 0, time.UTC)}, + {in: float64(2204578800), out: time.Date(2039, time.November, 10, 23, 0, 0, 0, time.UTC)}, + {in: float64(-2150326800), out: time.Date(1901, time.November, 10, 23, 0, 0, 0, time.UTC)}, + {in: float64(1257894000.001), out: time.Date(2009, time.November, 10, 23, 0, 0, 999927, time.UTC)}, + {in: float64(-4409999.999), out: time.Date(1969, time.November, 10, 23, 0, 0, 1000001, time.UTC)}, + {in: float64(0), out: time.Time{}}, } - errData := []String{ - "hello", - "", - "-3a.5", - "1e400", - } - - for _, tc := range errData { - if out, err := Convert(&tc, FloatID); err == nil { - t.Errorf("Expected error converting string %s to float %v", tc, out) - } + for _, tc := range tests { + out, err := Convert(Val{Tid: FloatID, Value: bs(tc.in)}, DateTimeID) + require.NoError(t, err) + require.EqualValues(t, Val{Tid: DateTimeID, Value: tc.out}, out) } } func TestConvertDateTimeToFloat(t *testing.T) { - data := []struct { + tests := []struct { in time.Time - out Float + out float64 }{ - {time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), 1257894000}, - {time.Date(1969, time.November, 10, 23, 0, 0, 0, time.UTC), -4410000}, - {time.Date(2009, time.November, 10, 23, 0, 0, 1000000, time.UTC), 1257894000.001}, - {time.Date(1969, time.November, 10, 23, 0, 0, 1000000, time.UTC), -4409999.999}, - {time.Date(2039, time.November, 10, 23, 0, 0, 0, time.UTC), 2204578800}, - {time.Date(1901, time.November, 10, 23, 0, 0, 0, time.UTC), -2150326800}, - } - for _, tc := range data { - if out, err := Convert(&Time{tc.in}, FloatID); err != nil { - t.Errorf("Unexpected error converting time to int: %v", err) - } else if *(out.(*Float)) != tc.out { - t.Errorf("Converting time to int: Expected %v, got %v", tc.out, out) - } + {in: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), out: float64(1257894000)}, + {in: time.Date(1969, time.November, 10, 23, 0, 0, 0, time.UTC), out: float64(-4410000)}, + {in: time.Date(2039, time.November, 10, 23, 0, 0, 0, time.UTC), out: float64(2204578800)}, + {in: time.Date(1901, time.November, 10, 23, 0, 0, 0, time.UTC), out: float64(-2150326800)}, + {in: time.Date(2009, time.November, 10, 23, 0, 0, 1000000, time.UTC), out: float64(1257894000.001)}, + {in: time.Date(1969, time.November, 10, 23, 0, 0, 1000000, time.UTC), out: float64(-4409999.999)}, + {in: time.Time{}, out: float64(0)}, } -} -func TestConvertBoolToTime(t *testing.T) { - b := Bool(false) - if _, err := Convert(&b, DateTimeID); err == nil { - t.Errorf("Expected error converting bool to time") + for _, tc := range tests { + out, err := Convert(Val{Tid: DateTimeID, Value: bs(tc.in)}, FloatID) + require.NoError(t, err) + require.EqualValues(t, Val{Tid: FloatID, Value: tc.out}, out) } } -func TestConvertInt32ToTime(t *testing.T) { - data := []struct { - in Int32 +func TestConvertIntToDateTime(t *testing.T) { + tests := []struct { + in int64 out time.Time }{ - {1257894000, time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)}, - {-4410000, time.Date(1969, time.November, 10, 23, 0, 0, 0, time.UTC)}, - {0, time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC)}, - } - for _, tc := range data { - tout := Time{tc.out} - if out, err := Convert(&tc.in, DateTimeID); err != nil { - t.Errorf("Unexpected error converting time to int: %v", err) - } else if *(out.(*Time)) != tout { - t.Errorf("Converting time to int: Expected %v, got %v", tc.out, out) - } + {in: int64(1257894000), out: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)}, + {in: int64(-4410000), out: time.Date(1969, time.November, 10, 23, 0, 0, 0, time.UTC)}, + {in: int64(0), out: time.Time{}}, + } + + for _, tc := range tests { + out, err := Convert(Val{Tid: IntID, Value: bs(tc.in)}, DateTimeID) + require.NoError(t, err) + require.EqualValues(t, Val{Tid: DateTimeID, Value: tc.out}, out) } } -func TestConvertFloatToTime(t *testing.T) { - data := []struct { - in Float - out time.Time +func TestConvertDateTimeToInt(t *testing.T) { + tests := []struct { + in time.Time + out int64 }{ - {1257894000, time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)}, - {-4410000, time.Date(1969, time.November, 10, 23, 0, 0, 0, time.UTC)}, - {0, time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC)}, - {2204578800, time.Date(2039, time.November, 10, 23, 0, 0, 0, time.UTC)}, - {-2150326800, time.Date(1901, time.November, 10, 23, 0, 0, 0, time.UTC)}, - // For these two the conversion is not exact due to float64 rounding - {1257894000.001, time.Date(2009, time.November, 10, 23, 0, 0, 999927, time.UTC)}, - {-4409999.999, time.Date(1969, time.November, 10, 23, 0, 0, 1000001, time.UTC)}, - } - for _, tc := range data { - tout := Time{tc.out} - if out, err := Convert(&tc.in, DateTimeID); err != nil { - t.Errorf("Unexpected error converting float to int: %v", err) - } else if *(out.(*Time)) != tout { - t.Errorf("Converting float to int: Expected %v, got %v", tc.out, out) - } + {in: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), out: int64(1257894000)}, + {in: time.Date(1969, time.November, 10, 23, 0, 0, 0, time.UTC), out: int64(-4410000)}, + {in: time.Date(2039, time.November, 10, 23, 0, 0, 0, time.UTC), out: int64(2204578800)}, + {in: time.Date(1901, time.November, 10, 23, 0, 0, 0, time.UTC), out: int64(-2150326800)}, + {in: time.Time{}, out: int64(0)}, } -} + for _, tc := range tests { + out, err := Convert(Val{Tid: DateTimeID, Value: bs(tc.in)}, IntID) + require.NoError(t, err) + require.EqualValues(t, Val{Tid: IntID, Value: tc.out}, out) + } +} func TestConvertToString(t *testing.T) { - f := Float(13816.251) - i := Int32(-1221) - b := Bool(true) - data := []struct { - in Value - out String + tests := []struct { + in Val + out string }{ - {&Time{time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC)}, "2006-01-02T15:04:05Z"}, - {&f, "1.3816251E+04"}, - {&i, "-1221"}, - {&b, "true"}, - } - for _, tc := range data { - if out, err := Convert(tc.in, StringID); err != nil { - t.Errorf("Unexpected error converting to string: %v", err) - } else if *(out.(*String)) != tc.out { - t.Errorf("Converting to string: Expected %v, got %v", tc.out, out) - } + {in: Val{Tid: FloatID, Value: bs(float64(13816.251))}, out: "13816.251"}, + {in: Val{Tid: IntID, Value: bs(int64(-1221))}, out: "-1221"}, + {in: Val{Tid: BoolID, Value: bs(true)}, out: "true"}, + {in: Val{Tid: StringID, Value: []byte("srfrog")}, out: "srfrog"}, + {in: Val{Tid: PasswordID, Value: []byte("password")}, out: "password"}, + {in: Val{Tid: DateTimeID, Value: bs(time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC))}, out: "2006-01-02T15:04:05Z"}, + {in: Val{Tid: DateTimeID, Value: bs(time.Time{})}, out: ""}, + } + + for _, tc := range tests { + out, err := Convert(tc.in, StringID) + require.NoError(t, err) + require.EqualValues(t, Val{Tid: StringID, Value: tc.out}, out) } } -*/ diff --git a/types/earth.go b/types/earth.go index 1f23078f831..8cede0b9660 100644 --- a/types/earth.go +++ b/types/earth.go @@ -50,13 +50,13 @@ func EarthArea(a float64) Area { // String converts the length to human readable units func (l Length) String() string { - if l > 1000 { + switch { + case l > 1000: return fmt.Sprintf("%.3f km", l/1000) - } else if l < 1 { + case l < 1: return fmt.Sprintf("%.3f cm", l*100) - } else { - return fmt.Sprintf("%.3f m", l) } + return fmt.Sprintf("%.3f m", l) } const km2 = 1000 * 1000 @@ -64,11 +64,11 @@ const cm2 = 100 * 100 // String converts the area to human readable units func (a Area) String() string { - if a > km2 { + switch { + case a > km2: return fmt.Sprintf("%.3f km^2", a/km2) - } else if a < 1 { + case a < 1: return fmt.Sprintf("%.3f cm^2", a*cm2) - } else { - return fmt.Sprintf("%.3f m^2", a) } + return fmt.Sprintf("%.3f m^2", a) } diff --git a/types/s2index.go b/types/s2index.go index 6279695d343..005348d06dd 100644 --- a/types/s2index.go +++ b/types/s2index.go @@ -20,7 +20,7 @@ import ( "log" "github.com/golang/geo/s2" - "github.com/twpayne/go-geom" + geom "github.com/twpayne/go-geom" "github.com/dgraph-io/dgraph/x" ) @@ -48,6 +48,7 @@ func IndexGeoTokens(g geom.T) ([]string, error) { } // IndexKeysForCap returns the keys to be used in a geospatial index for a Cap. +/* func indexCellsForCap(c s2.Cap) s2.CellUnion { rc := &s2.RegionCoverer{ MinLevel: MinCellLevel, @@ -57,6 +58,7 @@ func indexCellsForCap(c s2.Cap) s2.CellUnion { } return rc.Covering(c) } +*/ const ( parentPrefix = "p/" diff --git a/types/scalar_types.go b/types/scalar_types.go index 869c451852d..8c2a01d4da3 100644 --- a/types/scalar_types.go +++ b/types/scalar_types.go @@ -23,9 +23,7 @@ import ( geom "github.com/twpayne/go-geom" ) -const ( - nanoSecondsInSec = 1000000000 -) +const nanoSecondsInSec = 1000000000 // Note: These ids are stored in the posting lists to indicate the type // of the data. The order *cannot* be changed without breaking existing @@ -155,12 +153,6 @@ func ValueForType(id TypeID) Val { } } -func createDate(y int, m time.Month, d int) time.Time { - var dt time.Time - dt = time.Date(y, m, d, 0, 0, 0, 0, time.UTC) - return dt -} - func ParseTime(val string) (time.Time, error) { var t time.Time if err := t.UnmarshalText([]byte(val)); err == nil {