Skip to content

Commit

Permalink
support sql Value and Scan for custom date type
Browse files Browse the repository at this point in the history
  • Loading branch information
krhubert authored and jackc committed Oct 8, 2022
1 parent f59f140 commit 7294846
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 0 deletions.
27 changes: 27 additions & 0 deletions date.go
@@ -1,6 +1,7 @@
package pgtype

import (
"database/sql"
"database/sql/driver"
"encoding/binary"
"encoding/json"
Expand Down Expand Up @@ -34,6 +35,14 @@ func (dst *Date) Set(src interface{}) error {
}
}

if value, ok := src.(interface{ Value() (driver.Value, error) }); ok {
v, err := value.Value()
if err != nil {
return fmt.Errorf("cannot get value %v for Date: %v", value, err)
}
return dst.Set(v)
}

switch value := src.(type) {
case time.Time:
*dst = Date{Time: value, Status: Present}
Expand Down Expand Up @@ -76,6 +85,24 @@ func (dst Date) Get() interface{} {
}

func (src *Date) AssignTo(dst interface{}) error {
if scanner, ok := dst.(sql.Scanner); ok {
var err error
switch src.Status {
case Present:
if src.InfinityModifier != None {
err = scanner.Scan(src.InfinityModifier.String())
} else {
err = scanner.Scan(src.Time)
}
case Null:
err = scanner.Scan(nil)
}
if err != nil {
return fmt.Errorf("unable assign %v to %T: %s", src, dst, err)
}
return nil
}

switch src.Status {
case Present:
switch v := dst.(type) {
Expand Down
18 changes: 18 additions & 0 deletions date_array_test.go
Expand Up @@ -65,6 +65,14 @@ func TestDateArraySet(t *testing.T) {
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
Status: pgtype.Present},
},
{
source: []customDate{{t: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC)}},
result: pgtype.DateArray{
Elements: []pgtype.Date{{Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}},
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
Status: pgtype.Present,
},
},
{
source: (([]time.Time)(nil)),
result: pgtype.DateArray{Status: pgtype.Null},
Expand Down Expand Up @@ -162,6 +170,7 @@ func TestDateArrayAssignTo(t *testing.T) {
var timeSliceDim4 [][][][]time.Time
var timeArrayDim2 [2][1]time.Time
var timeArrayDim4 [2][1][1][3]time.Time
var customDateSlice []customDate

simpleTests := []struct {
src pgtype.DateArray
Expand All @@ -177,6 +186,15 @@ func TestDateArrayAssignTo(t *testing.T) {
dst: &timeSlice,
expected: []time.Time{time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC)},
},
{
src: pgtype.DateArray{
Elements: []pgtype.Date{{Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}},
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
Status: pgtype.Present,
},
dst: &customDateSlice,
expected: []customDate{{t: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC)}},
},
{
src: pgtype.DateArray{Status: pgtype.Null},
dst: &timeSlice,
Expand Down
35 changes: 35 additions & 0 deletions date_test.go
@@ -1,6 +1,8 @@
package pgtype_test

import (
"database/sql/driver"
"fmt"
"reflect"
"testing"
"time"
Expand All @@ -9,6 +11,37 @@ import (
"github.com/jackc/pgtype/testutil"
)

type customDate struct {
t time.Time
}

func (d customDate) Value() (driver.Value, error) {
return d.t.Format("2006-01-02"), nil
}

func (d *customDate) Scan(src interface{}) (err error) {
if src == nil {
d.t = time.Time{}
return nil
}

switch v := src.(type) {
case int64:
d.t = time.Unix(v, 0).UTC()
case float64:
d.t = time.Unix(int64(v), 0).UTC()
case string:
d.t, err = time.Parse("2006-01-02", v)
case []byte:
d.t, err = time.Parse("2006-01-02", string(v))
case time.Time:
d.t = v
default:
err = fmt.Errorf("failed to scan type '%T' into date", src)
}
return err
}

func TestDateTranscode(t *testing.T) {
testutil.TestSuccessfulTranscodeEqFunc(t, "date", []interface{}{
&pgtype.Date{Time: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
Expand Down Expand Up @@ -43,6 +76,7 @@ func TestDateSet(t *testing.T) {
{source: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Date{Time: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}},
{source: _time(time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC)), result: pgtype.Date{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}},
{source: "1999-12-31", result: pgtype.Date{Time: time.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}},
{source: customDate{t: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC)}, result: pgtype.Date{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}},
}

for i, tt := range successfulTests {
Expand All @@ -68,6 +102,7 @@ func TestDateAssignTo(t *testing.T) {
expected interface{}
}{
{src: pgtype.Date{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, dst: &tim, expected: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local)},
{src: pgtype.Date{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, dst: &customDate{}, expected: customDate{t: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local)}},
{src: pgtype.Date{Time: time.Time{}, Status: pgtype.Null}, dst: &ptim, expected: ((*time.Time)(nil))},
}

Expand Down

0 comments on commit 7294846

Please sign in to comment.