From fc020c24ac9590f6547f8ad1d291fc75b4873a84 Mon Sep 17 00:00:00 2001 From: Nicholas Wilson Date: Wed, 24 Jul 2019 12:32:18 +0100 Subject: [PATCH 1/2] Add support for pgtype.UUID to write into any [16]byte type --- pgtype/convert.go | 29 +++++++++++++++++++++++++++++ pgtype/uuid.go | 2 +- pgtype/uuid_test.go | 21 +++++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/pgtype/convert.go b/pgtype/convert.go index 5dfb738e..ee6907c4 100644 --- a/pgtype/convert.go +++ b/pgtype/convert.go @@ -163,6 +163,27 @@ func underlyingTimeType(val interface{}) (interface{}, bool) { return time.Time{}, false } +// underlyingUUIDType gets the underlying type that can be converted to [16]byte +func underlyingUUIDType(val interface{}) (interface{}, bool) { + refVal := reflect.ValueOf(val) + + switch refVal.Kind() { + case reflect.Ptr: + if refVal.IsNil() { + return time.Time{}, false + } + convVal := refVal.Elem().Interface() + return convVal, true + } + + uuidType := reflect.TypeOf([16]byte{}) + if refVal.Type().ConvertibleTo(uuidType) { + return refVal.Convert(uuidType).Interface(), true + } + + return nil, false +} + // underlyingSliceType gets the underlying slice type func underlyingSliceType(val interface{}) (interface{}, bool) { refVal := reflect.ValueOf(val) @@ -401,6 +422,14 @@ func GetAssignToDstType(dst interface{}) (interface{}, bool) { } } + if dstVal.Kind() == reflect.Array { + if baseElemType, ok := kindTypes[dstVal.Type().Elem().Kind()]; ok { + baseArrayType := reflect.PtrTo(reflect.ArrayOf(dstVal.Len(), baseElemType)) + nextDst := dstPtr.Convert(baseArrayType) + return nextDst.Interface(), dstPtr.Type() != nextDst.Type() + } + } + return nil, false } diff --git a/pgtype/uuid.go b/pgtype/uuid.go index 5e1eead5..8d33d8f8 100644 --- a/pgtype/uuid.go +++ b/pgtype/uuid.go @@ -39,7 +39,7 @@ func (dst *UUID) Set(src interface{}) error { } *dst = UUID{Bytes: uuid, Status: Present} default: - if originalSrc, ok := underlyingPtrType(src); ok { + if originalSrc, ok := underlyingUUIDType(src); ok { return dst.Set(originalSrc) } return errors.Errorf("cannot convert %v to UUID", value) diff --git a/pgtype/uuid_test.go b/pgtype/uuid_test.go index 162d999f..1eddeda1 100644 --- a/pgtype/uuid_test.go +++ b/pgtype/uuid_test.go @@ -15,6 +15,8 @@ func TestUUIDTranscode(t *testing.T) { }) } +type SomeUUIDType [16]byte + func TestUUIDSet(t *testing.T) { successfulTests := []struct { source interface{} @@ -32,6 +34,10 @@ func TestUUIDSet(t *testing.T) { source: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, result: pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, }, + { + source: SomeUUIDType{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + result: pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, + }, { source: ([]byte)(nil), result: pgtype.UUID{Status: pgtype.Null}, @@ -86,6 +92,21 @@ func TestUUIDAssignTo(t *testing.T) { } } + { + src := pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present} + var dst SomeUUIDType + expected := [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} + + err := src.AssignTo(&dst) + if err != nil { + t.Error(err) + } + + if dst != expected { + t.Errorf("expected %v to assign %v, but result was %v", src, expected, dst) + } + } + { src := pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present} var dst string From 251e6b7730c7b31b600e6fe06162e541f3032604 Mon Sep 17 00:00:00 2001 From: Nicholas Wilson Date: Wed, 24 Jul 2019 12:32:43 +0100 Subject: [PATCH 2/2] Tidying: make underlyingTimeType consistent with other underlyingFooType The first return value is ignored when returning false - so there's no point returning an empty time.Time when it can be nil. --- pgtype/convert.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pgtype/convert.go b/pgtype/convert.go index ee6907c4..029e3d48 100644 --- a/pgtype/convert.go +++ b/pgtype/convert.go @@ -149,7 +149,7 @@ func underlyingTimeType(val interface{}) (interface{}, bool) { switch refVal.Kind() { case reflect.Ptr: if refVal.IsNil() { - return time.Time{}, false + return nil, false } convVal := refVal.Elem().Interface() return convVal, true @@ -160,7 +160,7 @@ func underlyingTimeType(val interface{}) (interface{}, bool) { return refVal.Convert(timeType).Interface(), true } - return time.Time{}, false + return nil, false } // underlyingUUIDType gets the underlying type that can be converted to [16]byte