Fix: Scan and encode types with underlying types of arrays

Rather than special case the reported issue with UUID and [16]byte, this
commit allows the system to find the underlying type of any type that is
an array.

fixes https://github.com/jackc/pgx/issues/2107
pull/2108/head
Jack Christensen 2024-08-15 18:20:07 -05:00
parent d6fc8b02b4
commit 5747f37d9c
2 changed files with 33 additions and 5 deletions

View File

@ -573,17 +573,24 @@ func TryFindUnderlyingTypeScanPlan(dst any) (plan WrappedScanPlanNextSetter, nex
elemValue = dstValue.Elem() elemValue = dstValue.Elem()
} }
nextDstType := elemKindToPointerTypes[elemValue.Kind()] nextDstType := elemKindToPointerTypes[elemValue.Kind()]
if nextDstType == nil && elemValue.Kind() == reflect.Slice { if nextDstType == nil {
if elemValue.Type().Elem().Kind() == reflect.Uint8 { if elemValue.Kind() == reflect.Slice {
var v *[]byte if elemValue.Type().Elem().Kind() == reflect.Uint8 {
nextDstType = reflect.TypeOf(v) var v *[]byte
nextDstType = reflect.TypeOf(v)
}
}
// Get underlying type of any array.
// https://github.com/jackc/pgx/issues/2107
if elemValue.Kind() == reflect.Array {
nextDstType = reflect.PointerTo(reflect.ArrayOf(elemValue.Len(), elemValue.Type().Elem()))
} }
} }
if nextDstType != nil && dstValue.Type() != nextDstType && dstValue.CanConvert(nextDstType) { if nextDstType != nil && dstValue.Type() != nextDstType && dstValue.CanConvert(nextDstType) {
return &underlyingTypeScanPlan{dstType: dstValue.Type(), nextDstType: nextDstType}, dstValue.Convert(nextDstType).Interface(), true return &underlyingTypeScanPlan{dstType: dstValue.Type(), nextDstType: nextDstType}, dstValue.Convert(nextDstType).Interface(), true
} }
} }
return nil, nil, false return nil, nil, false
@ -1423,6 +1430,15 @@ func TryWrapFindUnderlyingTypeEncodePlan(value any) (plan WrappedEncodePlanNextS
return &underlyingTypeEncodePlan{nextValueType: byteSliceType}, refValue.Convert(byteSliceType).Interface(), true return &underlyingTypeEncodePlan{nextValueType: byteSliceType}, refValue.Convert(byteSliceType).Interface(), true
} }
// Get underlying type of any array.
// https://github.com/jackc/pgx/issues/2107
if refValue.Kind() == reflect.Array {
underlyingArrayType := reflect.ArrayOf(refValue.Len(), refValue.Type().Elem())
if refValue.Type() != underlyingArrayType {
return &underlyingTypeEncodePlan{nextValueType: underlyingArrayType}, refValue.Convert(underlyingArrayType).Interface(), true
}
}
return nil, nil, false return nil, nil, false
} }

View File

@ -10,6 +10,8 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
type renamedUUIDByteArray [16]byte
func TestUUIDCodec(t *testing.T) { func TestUUIDCodec(t *testing.T) {
pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "uuid", []pgxtest.ValueRoundTripTest{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "uuid", []pgxtest.ValueRoundTripTest{
{ {
@ -43,6 +45,16 @@ func TestUUIDCodec(t *testing.T) {
new(pgtype.UUID), new(pgtype.UUID),
isExpectedEq(pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}), isExpectedEq(pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}),
}, },
{
renamedUUIDByteArray{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
new(pgtype.UUID),
isExpectedEq(pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}),
},
{
[]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
new(renamedUUIDByteArray),
isExpectedEq(renamedUUIDByteArray{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}),
},
{ {
[]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
new(pgtype.UUID), new(pgtype.UUID),