From 847f888631e7356f88766bcb4986afd3c4b8fd76 Mon Sep 17 00:00:00 2001 From: Jack Christensen Date: Sat, 8 Apr 2023 14:39:48 -0500 Subject: [PATCH] Fix scan array of record to pointer to slice of struct https://github.com/jackc/pgx/issues/1570 --- pgtype/pgtype.go | 15 ++++++++++----- pgtype/pgtype_test.go | 16 ++++++++++++++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/pgtype/pgtype.go b/pgtype/pgtype.go index 83b349ce..e764bf62 100644 --- a/pgtype/pgtype.go +++ b/pgtype/pgtype.go @@ -1089,15 +1089,16 @@ func TryWrapPtrSliceScanPlan(target any) (plan WrappedScanPlanNextSetter, nextVa return &wrapPtrSliceScanPlan[time.Time]{}, (*FlatArray[time.Time])(target), true } - targetValue := reflect.ValueOf(target) - if targetValue.Kind() != reflect.Ptr { + targetType := reflect.TypeOf(target) + if targetType.Kind() != reflect.Ptr { return nil, nil, false } - targetElemValue := targetValue.Elem() + targetElemType := targetType.Elem() - if targetElemValue.Kind() == reflect.Slice { - return &wrapPtrSliceReflectScanPlan{}, &anySliceArrayReflect{slice: targetElemValue}, true + if targetElemType.Kind() == reflect.Slice { + slice := reflect.New(targetElemType).Elem() + return &wrapPtrSliceReflectScanPlan{}, &anySliceArrayReflect{slice: slice}, true } return nil, nil, false } @@ -1198,6 +1199,10 @@ func (m *Map) PlanScan(oid uint32, formatCode int16, target any) ScanPlan { } func (m *Map) planScan(oid uint32, formatCode int16, target any) ScanPlan { + if target == nil { + return &scanPlanFail{m: m, oid: oid, formatCode: formatCode} + } + if _, ok := target.(*UndecodedBytes); ok { return scanPlanAnyToUndecodedBytes{} } diff --git a/pgtype/pgtype_test.go b/pgtype/pgtype_test.go index a3870d51..341ce5d8 100644 --- a/pgtype/pgtype_test.go +++ b/pgtype/pgtype_test.go @@ -330,6 +330,22 @@ func TestMapScanPtrToPtrToSlice(t *testing.T) { require.Equal(t, []string{"foo", "bar"}, *v) } +func TestMapScanPtrToPtrToSliceOfStruct(t *testing.T) { + type Team struct { + TeamID int + Name string + } + + // Have to use binary format because text format doesn't include type information. + m := pgtype.NewMap() + src := []byte{0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xc9, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1e, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x17, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x19, 0x0, 0x0, 0x0, 0x6, 0x74, 0x65, 0x61, 0x6d, 0x20, 0x31, 0x0, 0x0, 0x0, 0x1e, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x17, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x19, 0x0, 0x0, 0x0, 0x6, 0x74, 0x65, 0x61, 0x6d, 0x20, 0x32} + var v *[]Team + plan := m.PlanScan(pgtype.RecordArrayOID, pgtype.BinaryFormatCode, &v) + err := plan.Scan(src, &v) + require.NoError(t, err) + require.Equal(t, []Team{{1, "team 1"}, {2, "team 2"}}, *v) +} + type databaseValuerString string func (s databaseValuerString) Value() (driver.Value, error) {