Add support for single dimensional arrays

https://github.com/jackc/pgx/issues/1442
pull/1446/head
Jack Christensen 2022-12-20 20:11:04 -06:00
parent 11fa083a0d
commit f42af35884
3 changed files with 119 additions and 0 deletions

View File

@ -126,6 +126,29 @@ func TestArrayCodecAnySlice(t *testing.T) {
})
}
// https://github.com/jackc/pgx/issues/1442
func TestArrayCodecAnyArray(t *testing.T) {
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
type _point3 [3]float32
for i, tt := range []struct {
expected any
}{
{_point3{0, 0, 0}},
{_point3{1, 2, 3}},
} {
var actual _point3
err := conn.QueryRow(
ctx,
"select $1::float4[]",
tt.expected,
).Scan(&actual)
assert.NoErrorf(t, err, "%d", i)
assert.Equalf(t, tt.expected, actual, "%d", i)
}
})
}
// https://github.com/jackc/pgx/issues/1273#issuecomment-1218262703
func TestArrayCodecSliceArgConversion(t *testing.T) {
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {

View File

@ -910,3 +910,43 @@ func (a *anyMultiDimSliceArray) ScanIndexType() any {
}
return reflect.New(lowestSliceType.Elem()).Interface()
}
type anyArrayArrayReflect struct {
array reflect.Value
}
func (a anyArrayArrayReflect) Dimensions() []ArrayDimension {
return []ArrayDimension{{Length: int32(a.array.Len()), LowerBound: 1}}
}
func (a anyArrayArrayReflect) Index(i int) any {
return a.array.Index(i).Interface()
}
func (a anyArrayArrayReflect) IndexType() any {
return reflect.New(a.array.Type().Elem()).Elem().Interface()
}
func (a *anyArrayArrayReflect) SetDimensions(dimensions []ArrayDimension) error {
if dimensions == nil {
return fmt.Errorf("anyArrayArrayReflect: cannot scan NULL into %v", a.array.Type().String())
}
if len(dimensions) != 1 {
return fmt.Errorf("anyArrayArrayReflect: cannot scan multi-dimensional array into %v", a.array.Type().String())
}
if int(dimensions[0].Length) != a.array.Len() {
return fmt.Errorf("anyArrayArrayReflect: cannot scan array with length %v into %v", dimensions[0].Length, a.array.Type().String())
}
return nil
}
func (a *anyArrayArrayReflect) ScanIndex(i int) any {
return a.array.Index(i).Addr().Interface()
}
func (a *anyArrayArrayReflect) ScanIndexType() any {
return reflect.New(a.array.Type().Elem()).Interface()
}

View File

@ -223,6 +223,7 @@ func NewMap() *Map {
TryWrapStructEncodePlan,
TryWrapSliceEncodePlan,
TryWrapMultiDimSliceEncodePlan,
TryWrapArrayEncodePlan,
},
TryWrapScanPlanFuncs: []TryWrapScanPlanFunc{
@ -232,6 +233,7 @@ func NewMap() *Map {
TryWrapStructScanPlan,
TryWrapPtrSliceScanPlan,
TryWrapPtrMultiDimSliceScanPlan,
TryWrapPtrArrayScanPlan,
},
}
@ -1139,6 +1141,31 @@ func (plan *wrapPtrMultiDimSliceScanPlan) Scan(src []byte, target any) error {
return plan.next.Scan(src, &anyMultiDimSliceArray{slice: reflect.ValueOf(target).Elem()})
}
// TryWrapPtrArrayScanPlan tries to wrap a pointer to a single dimension array.
func TryWrapPtrArrayScanPlan(target any) (plan WrappedScanPlanNextSetter, nextValue any, ok bool) {
targetValue := reflect.ValueOf(target)
if targetValue.Kind() != reflect.Ptr {
return nil, nil, false
}
targetElemValue := targetValue.Elem()
if targetElemValue.Kind() == reflect.Array {
return &wrapPtrArrayReflectScanPlan{}, &anyArrayArrayReflect{array: targetElemValue}, true
}
return nil, nil, false
}
type wrapPtrArrayReflectScanPlan struct {
next ScanPlan
}
func (plan *wrapPtrArrayReflectScanPlan) SetNext(next ScanPlan) { plan.next = next }
func (plan *wrapPtrArrayReflectScanPlan) Scan(src []byte, target any) error {
return plan.next.Scan(src, &anyArrayArrayReflect{array: reflect.ValueOf(target).Elem()})
}
// PlanScan prepares a plan to scan a value into target.
func (m *Map) PlanScan(oid uint32, formatCode int16, target any) ScanPlan {
oidMemo := m.memoizedScanPlans[oid]
@ -1941,6 +1968,35 @@ func (plan *wrapMultiDimSliceEncodePlan) Encode(value any, buf []byte) (newBuf [
return plan.next.Encode(&w, buf)
}
func TryWrapArrayEncodePlan(value any) (plan WrappedEncodePlanNextSetter, nextValue any, ok bool) {
if _, ok := value.(driver.Valuer); ok {
return nil, nil, false
}
if valueType := reflect.TypeOf(value); valueType != nil && valueType.Kind() == reflect.Array {
w := anyArrayArrayReflect{
array: reflect.ValueOf(value),
}
return &wrapArrayEncodeReflectPlan{}, w, true
}
return nil, nil, false
}
type wrapArrayEncodeReflectPlan struct {
next EncodePlan
}
func (plan *wrapArrayEncodeReflectPlan) SetNext(next EncodePlan) { plan.next = next }
func (plan *wrapArrayEncodeReflectPlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
w := anyArrayArrayReflect{
array: reflect.ValueOf(value),
}
return plan.next.Encode(w, buf)
}
func newEncodeError(value any, m *Map, oid uint32, formatCode int16, err error) error {
var format string
switch formatCode {