From 3d906d47dd2b42d6d34feab70006c99791e27306 Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Tue, 23 Jan 2024 17:50:11 +0000 Subject: [PATCH] Avoid allocations. --- driver/driver.go | 9 +++++++-- driver/time.go | 13 ++++++------- driver/time_test.go | 24 ++++++------------------ 3 files changed, 19 insertions(+), 27 deletions(-) diff --git a/driver/driver.go b/driver/driver.go index 1d22bc8..ae55b00 100644 --- a/driver/driver.go +++ b/driver/driver.go @@ -538,8 +538,13 @@ func (r *rows) Next(dest []driver.Value) error { for i := range dest { if t, ok := r.decodeTime(i, dest[i]); ok { dest[i] = t - } else if s, ok := dest[i].(string); ok { - dest[i] = stringOrTime(s) + continue + } + if s, ok := dest[i].(string); ok { + t, ok := maybeTime(s) + if ok { + dest[i] = t + } } } return err diff --git a/driver/time.go b/driver/time.go index 9196f1c..630a5b1 100644 --- a/driver/time.go +++ b/driver/time.go @@ -1,7 +1,6 @@ package driver import ( - "database/sql/driver" "time" ) @@ -9,24 +8,24 @@ import ( // if it roundtrips back to the same string. // This way times can be persisted to, and recovered from, the database, // but if a string is needed, [database/sql] will recover the same string. -func stringOrTime(text string) driver.Value { +func maybeTime(text string) (_ time.Time, _ bool) { // Weed out (some) values that can't possibly be // [time.RFC3339Nano] timestamps. if len(text) < len("2006-01-02T15:04:05Z") { - return text + return } if len(text) > len(time.RFC3339Nano) { - return text + return } if text[4] != '-' || text[10] != 'T' || text[16] != ':' { - return text + return } // Slow path. var buf [len(time.RFC3339Nano)]byte date, err := time.Parse(time.RFC3339Nano, text) if err == nil && text == string(date.AppendFormat(buf[:0], time.RFC3339Nano)) { - return date + return date, true } - return text + return } diff --git a/driver/time_test.go b/driver/time_test.go index ea0854c..a19380d 100644 --- a/driver/time_test.go +++ b/driver/time_test.go @@ -22,26 +22,18 @@ func Fuzz_stringOrTime_1(f *testing.F) { f.Add("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.") f.Fuzz(func(t *testing.T, str string) { - value := stringOrTime(str) - - switch v := value.(type) { - case time.Time: + v, ok := maybeTime(str) + if ok { // Make sure times round-trip to the same string: // https://pkg.go.dev/database/sql#Rows.Scan if v.Format(time.RFC3339Nano) != str { t.Fatalf("did not round-trip: %q", str) } - case string: - if v != str { - t.Fatalf("did not round-trip: %q", str) - } - + } else { date, err := time.Parse(time.RFC3339Nano, str) if err == nil && date.Format(time.RFC3339Nano) == str { t.Fatalf("would round-trip: %q", str) } - default: - t.Fatalf("invalid type %T: %q", v, str) } }) } @@ -59,10 +51,8 @@ func Fuzz_stringOrTime_2(f *testing.F) { f.Add(int64(-763421161058), int64(222_222_222)) // twosday, year 22222BC checkTime := func(t testing.TB, date time.Time) { - value := stringOrTime(date.Format(time.RFC3339Nano)) - - switch v := value.(type) { - case time.Time: + v, ok := maybeTime(date.Format(time.RFC3339Nano)) + if ok { // Make sure times round-trip to the same time: if !v.Equal(date) { t.Fatalf("did not round-trip: %v", date) @@ -73,10 +63,8 @@ func Fuzz_stringOrTime_2(f *testing.F) { if off1 != off2 { t.Fatalf("did not round-trip: %v", date) } - case string: + } else { t.Fatalf("was not recovered: %v", date) - default: - t.Fatalf("invalid type %T: %v", v, date) } }