From 8e52b3c771d3706bf2e31eb19e9734b351909cf8 Mon Sep 17 00:00:00 2001 From: illia-li Date: Thu, 12 Dec 2024 17:08:00 -0400 Subject: [PATCH 1/2] fix marshal `timestamp` added range matching check for the `time.Time` --- serialization/timestamp/marshal_utils.go | 12 ++++++---- .../marshal_16_timestamp_corrupt_test.go | 24 +++++++++++++++++++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/serialization/timestamp/marshal_utils.go b/serialization/timestamp/marshal_utils.go index 834d539fc..2336c58df 100644 --- a/serialization/timestamp/marshal_utils.go +++ b/serialization/timestamp/marshal_utils.go @@ -6,11 +6,10 @@ import ( "time" ) -const ( - maxValInt64 int64 = 86399999999999 - minValInt64 int64 = 0 - maxValDur time.Duration = 86399999999999 - minValDur time.Duration = 0 +var ( + maxTimestamp = time.Date(292278994, 8, 17, 7, 12, 55, 807*1000000, time.UTC) + zeroTimestamp = time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC) + minTimestamp = time.Date(-292275055, 5, 16, 16, 47, 4, 192*1000000, time.UTC) ) func EncInt64(v int64) ([]byte, error) { @@ -25,6 +24,9 @@ func EncInt64R(v *int64) ([]byte, error) { } func EncTime(v time.Time) ([]byte, error) { + if v.After(maxTimestamp) || v.Before(minTimestamp) { + return nil, fmt.Errorf("failed to marshal timestamp: the (%T)(%s) value should be in the range from -292275055-05-16T16:47:04.192Z to 292278994-08-17T07:12:55.807", v, v.Format(time.RFC3339Nano)) + } if v.IsZero() { return make([]byte, 0), nil } diff --git a/tests/serialization/marshal_16_timestamp_corrupt_test.go b/tests/serialization/marshal_16_timestamp_corrupt_test.go index 436c1c1df..438df5536 100644 --- a/tests/serialization/marshal_16_timestamp_corrupt_test.go +++ b/tests/serialization/marshal_16_timestamp_corrupt_test.go @@ -40,9 +40,33 @@ func TestMarshalTimestampCorrupt(t *testing.T) { } for _, tSuite := range testSuites { + marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { + serialization.NegativeMarshalSet{ + Values: mod.Values{ + time.Date(292278994, 8, 17, 7, 12, 55, 808*1000000, time.UTC), + time.Date(292278994, 8, 17, 7, 12, 56, 807*1000000, time.UTC), + time.Date(292278994, 8, 17, 7, 13, 55, 807*1000000, time.UTC), + time.Date(292278994, 8, 17, 8, 12, 55, 807*1000000, time.UTC), + time.Date(292278994, 8, 18, 7, 12, 55, 807*1000000, time.UTC), + time.Date(292278994, 9, 17, 7, 12, 55, 807*1000000, time.UTC), + time.Date(292278995, 8, 17, 7, 12, 55, 807*1000000, time.UTC), + }.AddVariants(mod.All...), + }.Run("big_vals", t, marshal) + + serialization.NegativeMarshalSet{ + Values: mod.Values{ + time.Date(-292275055, 5, 16, 16, 47, 4, 191*1000000, time.UTC), + time.Date(-292275055, 5, 16, 16, 47, 3, 192*1000000, time.UTC), + time.Date(-292275055, 5, 16, 16, 46, 4, 192*1000000, time.UTC), + time.Date(-292275055, 5, 16, 15, 47, 4, 192*1000000, time.UTC), + time.Date(-292275055, 5, 15, 16, 47, 4, 192*1000000, time.UTC), + time.Date(-292275055, 4, 16, 16, 47, 4, 192*1000000, time.UTC), + time.Date(-292275056, 5, 16, 16, 47, 4, 192*1000000, time.UTC), + }.AddVariants(mod.All...), + }.Run("small_vals", t, marshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x7f\xff\xff\xff\xff\xff\xff\xff\xff"), From 5f6e3374d1ed397efabeeb85652cd07fc63490a6 Mon Sep 17 00:00:00 2001 From: illia-li Date: Thu, 12 Dec 2024 17:11:49 -0400 Subject: [PATCH 2/2] fix `timestamp` serialization `zero data` <-> `zeroTimestamp` Changes: 1) Unmarshalling `zero data` into `time.Time` return `time.Time{}` before, now returns `zeroTimestamp` 2) Marshalling the `0001-1-1T00:00:00Z` value of the `time.Time` return `zero data` before, now returns a data that is equivalent to the difference between `0001-1-1T00:00:00Z` and `1970-1-1T00:00:00Z` 3) Tests fixed --- cassandra_test.go | 6 ++++-- serialization/timestamp/marshal_utils.go | 3 --- serialization/timestamp/unmarshal_utils.go | 5 +++-- tests/serialization/marshal_16_timestamp_test.go | 15 ++++----------- 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/cassandra_test.go b/cassandra_test.go index 088993c50..c6e6236f3 100644 --- a/cassandra_test.go +++ b/cassandra_test.go @@ -2075,7 +2075,9 @@ func TestNilInQuery(t *testing.T) { // Don't initialize time.Time bind variable if cassandra timestamp column is empty func TestEmptyTimestamp(t *testing.T) { + zeroTimestamp := time.UnixMilli(0).UTC() session := createSession(t) + defer session.Close() if err := createTable(session, "CREATE TABLE gocql_test.test_empty_timestamp (id int, time timestamp, num int, PRIMARY KEY (id))"); err != nil { @@ -2092,8 +2094,8 @@ func TestEmptyTimestamp(t *testing.T) { t.Fatalf("failed to select with err: %v", err) } - if !timeVal.IsZero() { - t.Errorf("time.Time bind variable should still be empty (was %s)", timeVal) + if !timeVal.Equal(zeroTimestamp) { + t.Errorf("time.Time bind variable should be zero (was %s)", timeVal) } } diff --git a/serialization/timestamp/marshal_utils.go b/serialization/timestamp/marshal_utils.go index 2336c58df..8a7e5b1d0 100644 --- a/serialization/timestamp/marshal_utils.go +++ b/serialization/timestamp/marshal_utils.go @@ -27,9 +27,6 @@ func EncTime(v time.Time) ([]byte, error) { if v.After(maxTimestamp) || v.Before(minTimestamp) { return nil, fmt.Errorf("failed to marshal timestamp: the (%T)(%s) value should be in the range from -292275055-05-16T16:47:04.192Z to 292278994-08-17T07:12:55.807", v, v.Format(time.RFC3339Nano)) } - if v.IsZero() { - return make([]byte, 0), nil - } ms := v.Unix()*1e3 + int64(v.Nanosecond())/1e6 return []byte{byte(ms >> 56), byte(ms >> 48), byte(ms >> 40), byte(ms >> 32), byte(ms >> 24), byte(ms >> 16), byte(ms >> 8), byte(ms)}, nil } diff --git a/serialization/timestamp/unmarshal_utils.go b/serialization/timestamp/unmarshal_utils.go index 41d0a0203..c1eaab174 100644 --- a/serialization/timestamp/unmarshal_utils.go +++ b/serialization/timestamp/unmarshal_utils.go @@ -55,7 +55,7 @@ func DecTime(p []byte, v *time.Time) error { } switch len(p) { case 0: - *v = time.Time{} + *v = zeroTimestamp case 8: *v = decTime(p) default: @@ -73,7 +73,8 @@ func DecTimeR(p []byte, v **time.Time) error { if p == nil { *v = nil } else { - *v = new(time.Time) + val := zeroTimestamp + *v = &val } case 8: val := decTime(p) diff --git a/tests/serialization/marshal_16_timestamp_test.go b/tests/serialization/marshal_16_timestamp_test.go index 5bc2ba86d..d80aecaea 100644 --- a/tests/serialization/marshal_16_timestamp_test.go +++ b/tests/serialization/marshal_16_timestamp_test.go @@ -40,12 +40,7 @@ func TestMarshalsTimestamp(t *testing.T) { }, } - zeroTime := time.Unix(0, 0).UTC() - - // The `time` package have a speciality - values `time.Time{}` and `time.Unix(0,0).UTC()` are different - // The old unmarshal function unmarshalls `nil` and `zero` data into `time.Time{}`, but data with zeros into `time.Unix(0,0).UTC()` - brokenTime := serialization.GetTypes(time.Time{}, &time.Time{}) - _ = brokenTime + zeroTimestamp := time.Unix(0, 0).UTC() for _, tSuite := range testSuites { marshal := tSuite.marshal @@ -62,23 +57,21 @@ func TestMarshalsTimestamp(t *testing.T) { serialization.PositiveSet{ Data: nil, Values: mod.Values{ - int64(0), zeroTime, + int64(0), zeroTimestamp, }.AddVariants(mod.CustomType), - BrokenUnmarshalTypes: brokenTime, }.Run("[nil]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: make([]byte, 0), Values: mod.Values{ - int64(0), zeroTime, + int64(0), zeroTimestamp, }.AddVariants(mod.All...), - BrokenUnmarshalTypes: brokenTime, }.Run("[]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\x00\x00"), Values: mod.Values{ - int64(0), zeroTime, + int64(0), zeroTimestamp, }.AddVariants(mod.All...), }.Run("zeros", t, marshal, unmarshal)