Skip to content

Commit

Permalink
Handling of empty string to datetime conversion (hypermodeinc#2891)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
srfrog authored Jan 22, 2019
1 parent ef404eb commit f6a2c04
Show file tree
Hide file tree
Showing 5 changed files with 426 additions and 430 deletions.
184 changes: 90 additions & 94 deletions types/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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 {
Expand All @@ -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:
Expand All @@ -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)
Expand Down Expand Up @@ -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)
}
Expand All @@ -184,57 +183,56 @@ 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)
}
}
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])
}

switch toID {
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 {
Expand All @@ -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)
}
Expand All @@ -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)
}
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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)
}
Expand All @@ -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)
Expand All @@ -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[:]
Expand All @@ -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:
Expand All @@ -394,34 +391,36 @@ 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)
}
case DateTimeID:
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)
}
Expand All @@ -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
Expand All @@ -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)
}
Expand Down
Loading

0 comments on commit f6a2c04

Please sign in to comment.