From f573cde09c56ecd161b98b6f568539c4015c69d3 Mon Sep 17 00:00:00 2001
From: Jack Christensen <jack@jackchristensen.com>
Date: Sat, 8 Jan 2022 18:33:08 -0600
Subject: [PATCH] Convert bytea to Codec

---
 pgtype/bytea.go            | 296 +++++++++++++++--------
 pgtype/bytea_array.go      | 476 -------------------------------------
 pgtype/bytea_array_test.go | 229 ------------------
 pgtype/bytea_test.go       | 131 +++++-----
 pgtype/generic_binary.go   |  39 ---
 pgtype/pgtype.go           |   8 +-
 rows.go                    |  27 +--
 7 files changed, 297 insertions(+), 909 deletions(-)
 delete mode 100644 pgtype/bytea_array.go
 delete mode 100644 pgtype/bytea_array_test.go
 delete mode 100644 pgtype/generic_binary.go

diff --git a/pgtype/bytea.go b/pgtype/bytea.go
index d4c4e436..2eb50610 100644
--- a/pgtype/bytea.go
+++ b/pgtype/bytea.go
@@ -6,141 +6,249 @@ import (
 	"fmt"
 )
 
-type Bytea struct {
-	Bytes []byte
-	Valid bool
+type BytesScanner interface {
+	// ScanBytes receives a byte slice of driver memory that is only valid until the next database method call.
+	ScanBytes(v []byte) error
 }
 
-func (dst *Bytea) Set(src interface{}) error {
-	if src == nil {
-		*dst = Bytea{}
+type BytesValuer interface {
+	// BytesValue returns a byte slice of the byte data. The caller must not change the returned slice.
+	BytesValue() ([]byte, error)
+}
+
+// DriverBytes is a byte slice that holds a reference to memory owned by the driver. It is only valid until the next
+// database method call. e.g. Any call to a Rows or Conn method invalidates the slice.
+type DriverBytes []byte
+
+func (b *DriverBytes) ScanBytes(v []byte) error {
+	*b = v
+	return nil
+}
+
+// PreallocBytes is a byte slice of preallocated memory that scanned bytes will be copied to. If it is too small a new
+// slice will be allocated.
+type PreallocBytes []byte
+
+func (b *PreallocBytes) ScanBytes(v []byte) error {
+	if v == nil {
+		*b = nil
 		return nil
 	}
 
-	if value, ok := src.(interface{ Get() interface{} }); ok {
-		value2 := value.Get()
-		if value2 != value {
-			return dst.Set(value2)
-		}
+	if len(v) <= len(*b) {
+		*b = (*b)[:len(v)]
+	} else {
+		*b = make(PreallocBytes, len(v))
+	}
+	copy(*b, v)
+	return nil
+}
+
+// UndecodedBytes can be used as a scan target to get the raw bytes from PostgreSQL without any decoding.
+type UndecodedBytes []byte
+
+type scanPlanAnyToUndecodedBytes struct{}
+
+func (scanPlanAnyToUndecodedBytes) Scan(ci *ConnInfo, oid uint32, formatCode int16, src []byte, dst interface{}) error {
+	dstBuf := dst.(*UndecodedBytes)
+	if src == nil {
+		*dstBuf = nil
+		return nil
 	}
 
-	switch value := src.(type) {
-	case []byte:
-		if value != nil {
-			*dst = Bytea{Bytes: value, Valid: true}
-		} else {
-			*dst = Bytea{}
+	*dstBuf = make([]byte, len(src))
+	copy(*dstBuf, src)
+	return nil
+}
+
+type ByteaCodec struct{}
+
+func (ByteaCodec) FormatSupported(format int16) bool {
+	return format == TextFormatCode || format == BinaryFormatCode
+}
+
+func (ByteaCodec) PreferredFormat() int16 {
+	return BinaryFormatCode
+}
+
+func (ByteaCodec) PlanEncode(ci *ConnInfo, oid uint32, format int16, value interface{}) EncodePlan {
+	switch format {
+	case BinaryFormatCode:
+		switch value.(type) {
+		case []byte:
+			return encodePlanBytesCodecBinaryBytes{}
+		case BytesValuer:
+			return encodePlanBytesCodecBinaryBytesValuer{}
 		}
-	default:
-		if originalSrc, ok := underlyingBytesType(src); ok {
-			return dst.Set(originalSrc)
+	case TextFormatCode:
+		switch value.(type) {
+		case []byte:
+			return encodePlanBytesCodecTextBytes{}
+		case BytesValuer:
+			return encodePlanBytesCodecTextBytesValuer{}
 		}
-		return fmt.Errorf("cannot convert %v to Bytea", value)
 	}
 
 	return nil
 }
 
-func (dst Bytea) Get() interface{} {
-	if !dst.Valid {
-		return nil
+type encodePlanBytesCodecBinaryBytes struct{}
+
+func (encodePlanBytesCodecBinaryBytes) Encode(value interface{}, buf []byte) (newBuf []byte, err error) {
+	b := value.([]byte)
+	if b == nil {
+		return nil, nil
 	}
-	return dst.Bytes
+
+	return append(buf, b...), nil
 }
 
-func (src *Bytea) AssignTo(dst interface{}) error {
-	if !src.Valid {
-		return NullAssignTo(dst)
+type encodePlanBytesCodecBinaryBytesValuer struct{}
+
+func (encodePlanBytesCodecBinaryBytesValuer) Encode(value interface{}, buf []byte) (newBuf []byte, err error) {
+	b, err := value.(BytesValuer).BytesValue()
+	if err != nil {
+		return nil, err
+	}
+	if b == nil {
+		return nil, nil
 	}
 
-	switch v := dst.(type) {
-	case *[]byte:
-		buf := make([]byte, len(src.Bytes))
-		copy(buf, src.Bytes)
-		*v = buf
-		return nil
-	default:
-		if nextDst, retry := GetAssignToDstType(dst); retry {
-			return src.AssignTo(nextDst)
+	return append(buf, b...), nil
+}
+
+type encodePlanBytesCodecTextBytes struct{}
+
+func (encodePlanBytesCodecTextBytes) Encode(value interface{}, buf []byte) (newBuf []byte, err error) {
+	b := value.([]byte)
+	if b == nil {
+		return nil, nil
+	}
+
+	buf = append(buf, `\x`...)
+	buf = append(buf, hex.EncodeToString(b)...)
+	return buf, nil
+}
+
+type encodePlanBytesCodecTextBytesValuer struct{}
+
+func (encodePlanBytesCodecTextBytesValuer) Encode(value interface{}, buf []byte) (newBuf []byte, err error) {
+	b, err := value.(BytesValuer).BytesValue()
+	if err != nil {
+		return nil, err
+	}
+	if b == nil {
+		return nil, nil
+	}
+
+	buf = append(buf, `\x`...)
+	buf = append(buf, hex.EncodeToString(b)...)
+	return buf, nil
+}
+
+func (ByteaCodec) PlanScan(ci *ConnInfo, oid uint32, format int16, target interface{}, actualTarget bool) ScanPlan {
+
+	switch format {
+	case BinaryFormatCode:
+		switch target.(type) {
+		case *[]byte:
+			return scanPlanBinaryBytesToBytes{}
+		case BytesScanner:
+			return scanPlanBinaryBytesToBytesScanner{}
+		}
+	case TextFormatCode:
+		switch target.(type) {
+		case *[]byte:
+			return scanPlanTextByteaToBytes{}
+		case BytesScanner:
+			return scanPlanTextByteaToBytesScanner{}
 		}
-		return fmt.Errorf("unable to assign to %T", dst)
 	}
+
+	return nil
 }
 
-// DecodeText only supports the hex format. This has been the default since
-// PostgreSQL 9.0.
-func (dst *Bytea) DecodeText(ci *ConnInfo, src []byte) error {
+type scanPlanBinaryBytesToBytes struct{}
+
+func (scanPlanBinaryBytesToBytes) Scan(ci *ConnInfo, oid uint32, formatCode int16, src []byte, dst interface{}) error {
+	dstBuf := dst.(*[]byte)
 	if src == nil {
-		*dst = Bytea{}
+		*dstBuf = nil
 		return nil
 	}
 
+	*dstBuf = make([]byte, len(src))
+	copy(*dstBuf, src)
+	return nil
+}
+
+type scanPlanBinaryBytesToBytesScanner struct{}
+
+func (scanPlanBinaryBytesToBytesScanner) Scan(ci *ConnInfo, oid uint32, formatCode int16, src []byte, dst interface{}) error {
+	scanner := (dst).(BytesScanner)
+	return scanner.ScanBytes(src)
+}
+
+type scanPlanTextByteaToBytes struct{}
+
+func (scanPlanTextByteaToBytes) Scan(ci *ConnInfo, oid uint32, formatCode int16, src []byte, dst interface{}) error {
+	dstBuf := dst.(*[]byte)
+	if src == nil {
+		*dstBuf = nil
+		return nil
+	}
+
+	buf, err := decodeHexBytea(src)
+	if err != nil {
+		return err
+	}
+	*dstBuf = buf
+
+	return nil
+}
+
+type scanPlanTextByteaToBytesScanner struct{}
+
+func (scanPlanTextByteaToBytesScanner) Scan(ci *ConnInfo, oid uint32, formatCode int16, src []byte, dst interface{}) error {
+	scanner := (dst).(BytesScanner)
+	buf, err := decodeHexBytea(src)
+	if err != nil {
+		return err
+	}
+	return scanner.ScanBytes(buf)
+}
+
+func decodeHexBytea(src []byte) ([]byte, error) {
+	if src == nil {
+		return nil, nil
+	}
+
 	if len(src) < 2 || src[0] != '\\' || src[1] != 'x' {
-		return fmt.Errorf("invalid hex format")
+		return nil, fmt.Errorf("invalid hex format")
 	}
 
 	buf := make([]byte, (len(src)-2)/2)
 	_, err := hex.Decode(buf, src[2:])
 	if err != nil {
-		return err
+		return nil, err
 	}
 
-	*dst = Bytea{Bytes: buf, Valid: true}
-	return nil
-}
-
-func (dst *Bytea) DecodeBinary(ci *ConnInfo, src []byte) error {
-	if src == nil {
-		*dst = Bytea{}
-		return nil
-	}
-
-	*dst = Bytea{Bytes: src, Valid: true}
-	return nil
-}
-
-func (src Bytea) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
-	if !src.Valid {
-		return nil, nil
-	}
-
-	buf = append(buf, `\x`...)
-	buf = append(buf, hex.EncodeToString(src.Bytes)...)
 	return buf, nil
 }
 
-func (src Bytea) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
-	if !src.Valid {
-		return nil, nil
-	}
-
-	return append(buf, src.Bytes...), nil
+func (c ByteaCodec) DecodeDatabaseSQLValue(ci *ConnInfo, oid uint32, format int16, src []byte) (driver.Value, error) {
+	return codecDecodeToTextFormat(c, ci, oid, format, src)
 }
 
-// Scan implements the database/sql Scanner interface.
-func (dst *Bytea) Scan(src interface{}) error {
+func (c ByteaCodec) DecodeValue(ci *ConnInfo, oid uint32, format int16, src []byte) (interface{}, error) {
 	if src == nil {
-		*dst = Bytea{}
-		return nil
-	}
-
-	switch src := src.(type) {
-	case string:
-		return dst.DecodeText(nil, []byte(src))
-	case []byte:
-		buf := make([]byte, len(src))
-		copy(buf, src)
-		*dst = Bytea{Bytes: buf, Valid: true}
-		return nil
-	}
-
-	return fmt.Errorf("cannot scan %T", src)
-}
-
-// Value implements the database/sql/driver Valuer interface.
-func (src Bytea) Value() (driver.Value, error) {
-	if !src.Valid {
 		return nil, nil
 	}
-	return src.Bytes, nil
+
+	var buf []byte
+	err := codecScan(c, ci, oid, format, src, &buf)
+	if err != nil {
+		return nil, err
+	}
+	return buf, nil
 }
diff --git a/pgtype/bytea_array.go b/pgtype/bytea_array.go
deleted file mode 100644
index 7c539e21..00000000
--- a/pgtype/bytea_array.go
+++ /dev/null
@@ -1,476 +0,0 @@
-// Code generated by erb. DO NOT EDIT.
-
-package pgtype
-
-import (
-	"database/sql/driver"
-	"encoding/binary"
-	"fmt"
-	"reflect"
-
-	"github.com/jackc/pgio"
-)
-
-type ByteaArray struct {
-	Elements   []Bytea
-	Dimensions []ArrayDimension
-	Valid      bool
-}
-
-func (dst *ByteaArray) Set(src interface{}) error {
-	// untyped nil and typed nil interfaces are different
-	if src == nil {
-		*dst = ByteaArray{}
-		return nil
-	}
-
-	if value, ok := src.(interface{ Get() interface{} }); ok {
-		value2 := value.Get()
-		if value2 != value {
-			return dst.Set(value2)
-		}
-	}
-
-	// Attempt to match to select common types:
-	switch value := src.(type) {
-
-	case [][]byte:
-		if value == nil {
-			*dst = ByteaArray{}
-		} else if len(value) == 0 {
-			*dst = ByteaArray{Valid: true}
-		} else {
-			elements := make([]Bytea, len(value))
-			for i := range value {
-				if err := elements[i].Set(value[i]); err != nil {
-					return err
-				}
-			}
-			*dst = ByteaArray{
-				Elements:   elements,
-				Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}},
-				Valid:      true,
-			}
-		}
-
-	case []Bytea:
-		if value == nil {
-			*dst = ByteaArray{}
-		} else if len(value) == 0 {
-			*dst = ByteaArray{Valid: true}
-		} else {
-			*dst = ByteaArray{
-				Elements:   value,
-				Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}},
-				Valid:      true,
-			}
-		}
-	default:
-		// Fallback to reflection if an optimised match was not found.
-		// The reflection is necessary for arrays and multidimensional slices,
-		// but it comes with a 20-50% performance penalty for large arrays/slices
-		reflectedValue := reflect.ValueOf(src)
-		if !reflectedValue.IsValid() || reflectedValue.IsZero() {
-			*dst = ByteaArray{}
-			return nil
-		}
-
-		dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0)
-		if !ok {
-			return fmt.Errorf("cannot find dimensions of %v for ByteaArray", src)
-		}
-		if elementsLength == 0 {
-			*dst = ByteaArray{Valid: true}
-			return nil
-		}
-		if len(dimensions) == 0 {
-			if originalSrc, ok := underlyingSliceType(src); ok {
-				return dst.Set(originalSrc)
-			}
-			return fmt.Errorf("cannot convert %v to ByteaArray", src)
-		}
-
-		*dst = ByteaArray{
-			Elements:   make([]Bytea, elementsLength),
-			Dimensions: dimensions,
-			Valid:      true,
-		}
-		elementCount, err := dst.setRecursive(reflectedValue, 0, 0)
-		if err != nil {
-			// Maybe the target was one dimension too far, try again:
-			if len(dst.Dimensions) > 1 {
-				dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1]
-				elementsLength = 0
-				for _, dim := range dst.Dimensions {
-					if elementsLength == 0 {
-						elementsLength = int(dim.Length)
-					} else {
-						elementsLength *= int(dim.Length)
-					}
-				}
-				dst.Elements = make([]Bytea, elementsLength)
-				elementCount, err = dst.setRecursive(reflectedValue, 0, 0)
-				if err != nil {
-					return err
-				}
-			} else {
-				return err
-			}
-		}
-		if elementCount != len(dst.Elements) {
-			return fmt.Errorf("cannot convert %v to ByteaArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount)
-		}
-	}
-
-	return nil
-}
-
-func (dst *ByteaArray) setRecursive(value reflect.Value, index, dimension int) (int, error) {
-	switch value.Kind() {
-	case reflect.Array:
-		fallthrough
-	case reflect.Slice:
-		if len(dst.Dimensions) == dimension {
-			break
-		}
-
-		valueLen := value.Len()
-		if int32(valueLen) != dst.Dimensions[dimension].Length {
-			return 0, fmt.Errorf("multidimensional arrays must have array expressions with matching dimensions")
-		}
-		for i := 0; i < valueLen; i++ {
-			var err error
-			index, err = dst.setRecursive(value.Index(i), index, dimension+1)
-			if err != nil {
-				return 0, err
-			}
-		}
-
-		return index, nil
-	}
-	if !value.CanInterface() {
-		return 0, fmt.Errorf("cannot convert all values to ByteaArray")
-	}
-	if err := dst.Elements[index].Set(value.Interface()); err != nil {
-		return 0, fmt.Errorf("%v in ByteaArray", err)
-	}
-	index++
-
-	return index, nil
-}
-
-func (dst ByteaArray) Get() interface{} {
-	if !dst.Valid {
-		return nil
-	}
-	return dst
-}
-
-func (src *ByteaArray) AssignTo(dst interface{}) error {
-	if !src.Valid {
-		return NullAssignTo(dst)
-	}
-
-	if len(src.Dimensions) <= 1 {
-		// Attempt to match to select common types:
-		switch v := dst.(type) {
-
-		case *[][]byte:
-			*v = make([][]byte, len(src.Elements))
-			for i := range src.Elements {
-				if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil {
-					return err
-				}
-			}
-			return nil
-
-		}
-	}
-
-	// Try to convert to something AssignTo can use directly.
-	if nextDst, retry := GetAssignToDstType(dst); retry {
-		return src.AssignTo(nextDst)
-	}
-
-	// Fallback to reflection if an optimised match was not found.
-	// The reflection is necessary for arrays and multidimensional slices,
-	// but it comes with a 20-50% performance penalty for large arrays/slices
-	value := reflect.ValueOf(dst)
-	if value.Kind() == reflect.Ptr {
-		value = value.Elem()
-	}
-
-	switch value.Kind() {
-	case reflect.Array, reflect.Slice:
-	default:
-		return fmt.Errorf("cannot assign %T to %T", src, dst)
-	}
-
-	if len(src.Elements) == 0 {
-		if value.Kind() == reflect.Slice {
-			value.Set(reflect.MakeSlice(value.Type(), 0, 0))
-			return nil
-		}
-	}
-
-	elementCount, err := src.assignToRecursive(value, 0, 0)
-	if err != nil {
-		return err
-	}
-	if elementCount != len(src.Elements) {
-		return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount)
-	}
-
-	return nil
-}
-
-func (src *ByteaArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) {
-	switch kind := value.Kind(); kind {
-	case reflect.Array:
-		fallthrough
-	case reflect.Slice:
-		if len(src.Dimensions) == dimension {
-			break
-		}
-
-		length := int(src.Dimensions[dimension].Length)
-		if reflect.Array == kind {
-			typ := value.Type()
-			if typ.Len() != length {
-				return 0, fmt.Errorf("expected size %d array, but %s has size %d array", length, typ, typ.Len())
-			}
-			value.Set(reflect.New(typ).Elem())
-		} else {
-			value.Set(reflect.MakeSlice(value.Type(), length, length))
-		}
-
-		var err error
-		for i := 0; i < length; i++ {
-			index, err = src.assignToRecursive(value.Index(i), index, dimension+1)
-			if err != nil {
-				return 0, err
-			}
-		}
-
-		return index, nil
-	}
-	if len(src.Dimensions) != dimension {
-		return 0, fmt.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension)
-	}
-	if !value.CanAddr() {
-		return 0, fmt.Errorf("cannot assign all values from ByteaArray")
-	}
-	addr := value.Addr()
-	if !addr.CanInterface() {
-		return 0, fmt.Errorf("cannot assign all values from ByteaArray")
-	}
-	if err := src.Elements[index].AssignTo(addr.Interface()); err != nil {
-		return 0, err
-	}
-	index++
-	return index, nil
-}
-
-func (dst *ByteaArray) DecodeText(ci *ConnInfo, src []byte) error {
-	if src == nil {
-		*dst = ByteaArray{}
-		return nil
-	}
-
-	uta, err := ParseUntypedTextArray(string(src))
-	if err != nil {
-		return err
-	}
-
-	var elements []Bytea
-
-	if len(uta.Elements) > 0 {
-		elements = make([]Bytea, len(uta.Elements))
-
-		for i, s := range uta.Elements {
-			var elem Bytea
-			var elemSrc []byte
-			if s != "NULL" || uta.Quoted[i] {
-				elemSrc = []byte(s)
-			}
-			err = elem.DecodeText(ci, elemSrc)
-			if err != nil {
-				return err
-			}
-
-			elements[i] = elem
-		}
-	}
-
-	*dst = ByteaArray{Elements: elements, Dimensions: uta.Dimensions, Valid: true}
-
-	return nil
-}
-
-func (dst *ByteaArray) DecodeBinary(ci *ConnInfo, src []byte) error {
-	if src == nil {
-		*dst = ByteaArray{}
-		return nil
-	}
-
-	var arrayHeader ArrayHeader
-	rp, err := arrayHeader.DecodeBinary(ci, src)
-	if err != nil {
-		return err
-	}
-
-	if len(arrayHeader.Dimensions) == 0 {
-		*dst = ByteaArray{Dimensions: arrayHeader.Dimensions, Valid: true}
-		return nil
-	}
-
-	elementCount := arrayHeader.Dimensions[0].Length
-	for _, d := range arrayHeader.Dimensions[1:] {
-		elementCount *= d.Length
-	}
-
-	elements := make([]Bytea, elementCount)
-
-	for i := range elements {
-		elemLen := int(int32(binary.BigEndian.Uint32(src[rp:])))
-		rp += 4
-		var elemSrc []byte
-		if elemLen >= 0 {
-			elemSrc = src[rp : rp+elemLen]
-			rp += elemLen
-		}
-		err = elements[i].DecodeBinary(ci, elemSrc)
-		if err != nil {
-			return err
-		}
-	}
-
-	*dst = ByteaArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Valid: true}
-	return nil
-}
-
-func (src ByteaArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
-	if !src.Valid {
-		return nil, nil
-	}
-
-	if len(src.Dimensions) == 0 {
-		return append(buf, '{', '}'), nil
-	}
-
-	buf = EncodeTextArrayDimensions(buf, src.Dimensions)
-
-	// dimElemCounts is the multiples of elements that each array lies on. For
-	// example, a single dimension array of length 4 would have a dimElemCounts of
-	// [4]. A multi-dimensional array of lengths [3,5,2] would have a
-	// dimElemCounts of [30,10,2]. This is used to simplify when to render a '{'
-	// or '}'.
-	dimElemCounts := make([]int, len(src.Dimensions))
-	dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length)
-	for i := len(src.Dimensions) - 2; i > -1; i-- {
-		dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1]
-	}
-
-	inElemBuf := make([]byte, 0, 32)
-	for i, elem := range src.Elements {
-		if i > 0 {
-			buf = append(buf, ',')
-		}
-
-		for _, dec := range dimElemCounts {
-			if i%dec == 0 {
-				buf = append(buf, '{')
-			}
-		}
-
-		elemBuf, err := elem.EncodeText(ci, inElemBuf)
-		if err != nil {
-			return nil, err
-		}
-		if elemBuf == nil {
-			buf = append(buf, `NULL`...)
-		} else {
-			buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...)
-		}
-
-		for _, dec := range dimElemCounts {
-			if (i+1)%dec == 0 {
-				buf = append(buf, '}')
-			}
-		}
-	}
-
-	return buf, nil
-}
-
-func (src ByteaArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
-	if !src.Valid {
-		return nil, nil
-	}
-
-	arrayHeader := ArrayHeader{
-		Dimensions: src.Dimensions,
-	}
-
-	if dt, ok := ci.DataTypeForName("bytea"); ok {
-		arrayHeader.ElementOID = int32(dt.OID)
-	} else {
-		return nil, fmt.Errorf("unable to find oid for type name %v", "bytea")
-	}
-
-	for i := range src.Elements {
-		if !src.Elements[i].Valid {
-			arrayHeader.ContainsNull = true
-			break
-		}
-	}
-
-	buf = arrayHeader.EncodeBinary(ci, buf)
-
-	for i := range src.Elements {
-		sp := len(buf)
-		buf = pgio.AppendInt32(buf, -1)
-
-		elemBuf, err := src.Elements[i].EncodeBinary(ci, buf)
-		if err != nil {
-			return nil, err
-		}
-		if elemBuf != nil {
-			buf = elemBuf
-			pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))
-		}
-	}
-
-	return buf, nil
-}
-
-// Scan implements the database/sql Scanner interface.
-func (dst *ByteaArray) Scan(src interface{}) error {
-	if src == nil {
-		return dst.DecodeText(nil, nil)
-	}
-
-	switch src := src.(type) {
-	case string:
-		return dst.DecodeText(nil, []byte(src))
-	case []byte:
-		srcCopy := make([]byte, len(src))
-		copy(srcCopy, src)
-		return dst.DecodeText(nil, srcCopy)
-	}
-
-	return fmt.Errorf("cannot scan %T", src)
-}
-
-// Value implements the database/sql/driver Valuer interface.
-func (src ByteaArray) Value() (driver.Value, error) {
-	buf, err := src.EncodeText(nil, nil)
-	if err != nil {
-		return nil, err
-	}
-	if buf == nil {
-		return nil, nil
-	}
-
-	return string(buf), nil
-}
diff --git a/pgtype/bytea_array_test.go b/pgtype/bytea_array_test.go
deleted file mode 100644
index 08b69c26..00000000
--- a/pgtype/bytea_array_test.go
+++ /dev/null
@@ -1,229 +0,0 @@
-package pgtype_test
-
-import (
-	"reflect"
-	"testing"
-
-	"github.com/jackc/pgx/v5/pgtype"
-	"github.com/jackc/pgx/v5/pgtype/testutil"
-)
-
-func TestByteaArrayTranscode(t *testing.T) {
-	testutil.TestSuccessfulTranscode(t, "bytea[]", []interface{}{
-		&pgtype.ByteaArray{
-			Elements:   nil,
-			Dimensions: nil,
-			Valid:      true,
-		},
-		&pgtype.ByteaArray{
-			Elements: []pgtype.Bytea{
-				{Bytes: []byte{1, 2, 3}, Valid: true},
-				{},
-			},
-			Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}},
-			Valid:      true,
-		},
-		&pgtype.ByteaArray{},
-		&pgtype.ByteaArray{
-			Elements: []pgtype.Bytea{
-				{Bytes: []byte{1, 2, 3}, Valid: true},
-				{Bytes: []byte{1, 2, 3}, Valid: true},
-				{Bytes: []byte{}, Valid: true},
-				{Bytes: []byte{1, 2, 3}, Valid: true},
-				{},
-				{Bytes: []byte{1}, Valid: true},
-			},
-			Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}},
-			Valid:      true,
-		},
-		&pgtype.ByteaArray{
-			Elements: []pgtype.Bytea{
-				{Bytes: []byte{1, 2, 3}, Valid: true},
-				{Bytes: []byte{}, Valid: true},
-				{Bytes: []byte{1, 2, 3}, Valid: true},
-				{Bytes: []byte{1}, Valid: true},
-			},
-			Dimensions: []pgtype.ArrayDimension{
-				{Length: 2, LowerBound: 4},
-				{Length: 2, LowerBound: 2},
-			},
-			Valid: true,
-		},
-	})
-}
-
-func TestByteaArraySet(t *testing.T) {
-	successfulTests := []struct {
-		source interface{}
-		result pgtype.ByteaArray
-	}{
-		{
-			source: [][]byte{{1, 2, 3}},
-			result: pgtype.ByteaArray{
-				Elements:   []pgtype.Bytea{{Bytes: []byte{1, 2, 3}, Valid: true}},
-				Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
-				Valid:      true},
-		},
-		{
-			source: (([][]byte)(nil)),
-			result: pgtype.ByteaArray{},
-		},
-		{
-			source: [][][]byte{{{1}}, {{2}}},
-			result: pgtype.ByteaArray{
-				Elements:   []pgtype.Bytea{{Bytes: []byte{1}, Valid: true}, {Bytes: []byte{2}, Valid: true}},
-				Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}},
-				Valid:      true},
-		},
-		{
-			source: [][][][][]byte{{{{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}}}, {{{{10, 11, 12}, {13, 14, 15}, {16, 17, 18}}}}},
-			result: pgtype.ByteaArray{
-				Elements: []pgtype.Bytea{
-					{Bytes: []byte{1, 2, 3}, Valid: true},
-					{Bytes: []byte{4, 5, 6}, Valid: true},
-					{Bytes: []byte{7, 8, 9}, Valid: true},
-					{Bytes: []byte{10, 11, 12}, Valid: true},
-					{Bytes: []byte{13, 14, 15}, Valid: true},
-					{Bytes: []byte{16, 17, 18}, Valid: true}},
-				Dimensions: []pgtype.ArrayDimension{
-					{LowerBound: 1, Length: 2},
-					{LowerBound: 1, Length: 1},
-					{LowerBound: 1, Length: 1},
-					{LowerBound: 1, Length: 3}},
-				Valid: true},
-		},
-		{
-			source: [2][1][]byte{{{1}}, {{2}}},
-			result: pgtype.ByteaArray{
-				Elements:   []pgtype.Bytea{{Bytes: []byte{1}, Valid: true}, {Bytes: []byte{2}, Valid: true}},
-				Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}},
-				Valid:      true},
-		},
-		{
-			source: [2][1][1][3][]byte{{{{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}}}, {{{{10, 11, 12}, {13, 14, 15}, {16, 17, 18}}}}},
-			result: pgtype.ByteaArray{
-				Elements: []pgtype.Bytea{
-					{Bytes: []byte{1, 2, 3}, Valid: true},
-					{Bytes: []byte{4, 5, 6}, Valid: true},
-					{Bytes: []byte{7, 8, 9}, Valid: true},
-					{Bytes: []byte{10, 11, 12}, Valid: true},
-					{Bytes: []byte{13, 14, 15}, Valid: true},
-					{Bytes: []byte{16, 17, 18}, Valid: true}},
-				Dimensions: []pgtype.ArrayDimension{
-					{LowerBound: 1, Length: 2},
-					{LowerBound: 1, Length: 1},
-					{LowerBound: 1, Length: 1},
-					{LowerBound: 1, Length: 3}},
-				Valid: true},
-		},
-	}
-
-	for i, tt := range successfulTests {
-		var r pgtype.ByteaArray
-		err := r.Set(tt.source)
-		if err != nil {
-			t.Errorf("%d: %v", i, err)
-		}
-
-		if !reflect.DeepEqual(r, tt.result) {
-			t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r)
-		}
-	}
-}
-
-func TestByteaArrayAssignTo(t *testing.T) {
-	var byteByteSlice [][]byte
-	var byteByteSliceDim2 [][][]byte
-	var byteByteSliceDim4 [][][][][]byte
-	var byteByteArraySliceDim2 [2][1][]byte
-	var byteByteArraySliceDim4 [2][1][1][3][]byte
-
-	simpleTests := []struct {
-		src      pgtype.ByteaArray
-		dst      interface{}
-		expected interface{}
-	}{
-		{
-			src: pgtype.ByteaArray{
-				Elements:   []pgtype.Bytea{{Bytes: []byte{1, 2, 3}, Valid: true}},
-				Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
-				Valid:      true,
-			},
-			dst:      &byteByteSlice,
-			expected: [][]byte{{1, 2, 3}},
-		},
-		{
-			src:      pgtype.ByteaArray{},
-			dst:      &byteByteSlice,
-			expected: (([][]byte)(nil)),
-		},
-		{
-			src:      pgtype.ByteaArray{Valid: true},
-			dst:      &byteByteSlice,
-			expected: [][]byte{},
-		},
-		{
-			src: pgtype.ByteaArray{
-				Elements:   []pgtype.Bytea{{Bytes: []byte{1}, Valid: true}, {Bytes: []byte{2}, Valid: true}},
-				Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}},
-				Valid:      true},
-			dst:      &byteByteSliceDim2,
-			expected: [][][]byte{{{1}}, {{2}}},
-		},
-		{
-			src: pgtype.ByteaArray{
-				Elements: []pgtype.Bytea{
-					{Bytes: []byte{1, 2, 3}, Valid: true},
-					{Bytes: []byte{4, 5, 6}, Valid: true},
-					{Bytes: []byte{7, 8, 9}, Valid: true},
-					{Bytes: []byte{10, 11, 12}, Valid: true},
-					{Bytes: []byte{13, 14, 15}, Valid: true},
-					{Bytes: []byte{16, 17, 18}, Valid: true}},
-				Dimensions: []pgtype.ArrayDimension{
-					{LowerBound: 1, Length: 2},
-					{LowerBound: 1, Length: 1},
-					{LowerBound: 1, Length: 1},
-					{LowerBound: 1, Length: 3}},
-				Valid: true},
-			dst:      &byteByteSliceDim4,
-			expected: [][][][][]byte{{{{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}}}, {{{{10, 11, 12}, {13, 14, 15}, {16, 17, 18}}}}},
-		},
-		{
-			src: pgtype.ByteaArray{
-				Elements:   []pgtype.Bytea{{Bytes: []byte{1}, Valid: true}, {Bytes: []byte{2}, Valid: true}},
-				Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}},
-				Valid:      true},
-			dst:      &byteByteArraySliceDim2,
-			expected: [2][1][]byte{{{1}}, {{2}}},
-		},
-		{
-			src: pgtype.ByteaArray{
-				Elements: []pgtype.Bytea{
-					{Bytes: []byte{1, 2, 3}, Valid: true},
-					{Bytes: []byte{4, 5, 6}, Valid: true},
-					{Bytes: []byte{7, 8, 9}, Valid: true},
-					{Bytes: []byte{10, 11, 12}, Valid: true},
-					{Bytes: []byte{13, 14, 15}, Valid: true},
-					{Bytes: []byte{16, 17, 18}, Valid: true}},
-				Dimensions: []pgtype.ArrayDimension{
-					{LowerBound: 1, Length: 2},
-					{LowerBound: 1, Length: 1},
-					{LowerBound: 1, Length: 1},
-					{LowerBound: 1, Length: 3}},
-				Valid: true},
-			dst:      &byteByteArraySliceDim4,
-			expected: [2][1][1][3][]byte{{{{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}}}, {{{{10, 11, 12}, {13, 14, 15}, {16, 17, 18}}}}},
-		},
-	}
-
-	for i, tt := range simpleTests {
-		err := tt.src.AssignTo(tt.dst)
-		if err != nil {
-			t.Errorf("%d: %v", i, err)
-		}
-
-		if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) {
-			t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst)
-		}
-	}
-}
diff --git a/pgtype/bytea_test.go b/pgtype/bytea_test.go
index 21751e24..d99d28b6 100644
--- a/pgtype/bytea_test.go
+++ b/pgtype/bytea_test.go
@@ -1,73 +1,94 @@
 package pgtype_test
 
 import (
-	"reflect"
+	"bytes"
+	"context"
 	"testing"
 
 	"github.com/jackc/pgx/v5/pgtype"
 	"github.com/jackc/pgx/v5/pgtype/testutil"
+	"github.com/stretchr/testify/require"
 )
 
-func TestByteaTranscode(t *testing.T) {
-	testutil.TestSuccessfulTranscode(t, "bytea", []interface{}{
-		&pgtype.Bytea{Bytes: []byte{1, 2, 3}, Valid: true},
-		&pgtype.Bytea{Bytes: []byte{}, Valid: true},
-		&pgtype.Bytea{Bytes: nil},
+func isExpectedEqBytes(a interface{}) func(interface{}) bool {
+	return func(v interface{}) bool {
+		ab := a.([]byte)
+		vb := v.([]byte)
+
+		if (ab == nil) != (vb == nil) {
+			return false
+		}
+
+		if ab == nil {
+			return true
+		}
+
+		return bytes.Compare(ab, vb) == 0
+	}
+}
+
+func TestByteaCodec(t *testing.T) {
+	testPgxCodec(t, "bytea", []PgxTranscodeTestCase{
+		{[]byte{1, 2, 3}, new([]byte), isExpectedEqBytes([]byte{1, 2, 3})},
+		{[]byte{}, new([]byte), isExpectedEqBytes([]byte{})},
+		{[]byte(nil), new([]byte), isExpectedEqBytes([]byte(nil))},
+		{nil, new([]byte), isExpectedEqBytes([]byte(nil))},
 	})
 }
 
-func TestByteaSet(t *testing.T) {
-	successfulTests := []struct {
-		source interface{}
-		result pgtype.Bytea
-	}{
-		{source: []byte{1, 2, 3}, result: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Valid: true}},
-		{source: []byte{}, result: pgtype.Bytea{Bytes: []byte{}, Valid: true}},
-		{source: []byte(nil), result: pgtype.Bytea{}},
-		{source: _byteSlice{1, 2, 3}, result: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Valid: true}},
-		{source: _byteSlice(nil), result: pgtype.Bytea{}},
-	}
+func TestDriverBytes(t *testing.T) {
+	conn := testutil.MustConnectPgx(t)
+	defer testutil.MustCloseContext(t, conn)
 
-	for i, tt := range successfulTests {
-		var r pgtype.Bytea
-		err := r.Set(tt.source)
-		if err != nil {
-			t.Errorf("%d: %v", i, err)
-		}
+	ctx := context.Background()
 
-		if !reflect.DeepEqual(r, tt.result) {
-			t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r)
-		}
-	}
-}
-
-func TestByteaAssignTo(t *testing.T) {
 	var buf []byte
-	var _buf _byteSlice
-	var pbuf *[]byte
-	var _pbuf *_byteSlice
+	err := conn.QueryRow(ctx, `select $1::bytea`, []byte{1, 2}).Scan((*pgtype.DriverBytes)(&buf))
+	require.NoError(t, err)
 
-	simpleTests := []struct {
-		src      pgtype.Bytea
-		dst      interface{}
-		expected interface{}
-	}{
-		{src: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Valid: true}, dst: &buf, expected: []byte{1, 2, 3}},
-		{src: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Valid: true}, dst: &_buf, expected: _byteSlice{1, 2, 3}},
-		{src: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Valid: true}, dst: &pbuf, expected: &[]byte{1, 2, 3}},
-		{src: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Valid: true}, dst: &_pbuf, expected: &_byteSlice{1, 2, 3}},
-		{src: pgtype.Bytea{}, dst: &pbuf, expected: ((*[]byte)(nil))},
-		{src: pgtype.Bytea{}, dst: &_pbuf, expected: ((*_byteSlice)(nil))},
-	}
+	require.Len(t, buf, 2)
+	require.Equal(t, buf, []byte{1, 2})
+	require.Equalf(t, cap(buf), len(buf), "cap(buf) is larger than len(buf)")
 
-	for i, tt := range simpleTests {
-		err := tt.src.AssignTo(tt.dst)
-		if err != nil {
-			t.Errorf("%d: %v", i, err)
-		}
-
-		if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) {
-			t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst)
-		}
-	}
+	// Don't actually have any way to be sure that the bytes are from the driver at the moment as underlying driver
+	// doesn't reuse buffers at the present.
+}
+
+func TestPreallocBytes(t *testing.T) {
+	conn := testutil.MustConnectPgx(t)
+	defer testutil.MustCloseContext(t, conn)
+
+	ctx := context.Background()
+
+	origBuf := []byte{5, 6, 7, 8}
+	buf := origBuf
+	err := conn.QueryRow(ctx, `select $1::bytea`, []byte{1, 2}).Scan((*pgtype.PreallocBytes)(&buf))
+	require.NoError(t, err)
+
+	require.Len(t, buf, 2)
+	require.Equal(t, 4, cap(buf))
+	require.Equal(t, buf, []byte{1, 2})
+
+	require.Equal(t, []byte{1, 2, 7, 8}, origBuf)
+
+	err = conn.QueryRow(ctx, `select $1::bytea`, []byte{3, 4, 5, 6, 7}).Scan((*pgtype.PreallocBytes)(&buf))
+	require.NoError(t, err)
+	require.Len(t, buf, 5)
+	require.Equal(t, 5, cap(buf))
+
+	require.Equal(t, []byte{1, 2, 7, 8}, origBuf)
+}
+
+func TestUndecodedBytes(t *testing.T) {
+	conn := testutil.MustConnectPgx(t)
+	defer testutil.MustCloseContext(t, conn)
+
+	ctx := context.Background()
+
+	var buf []byte
+	err := conn.QueryRow(ctx, `select 1`).Scan((*pgtype.UndecodedBytes)(&buf))
+	require.NoError(t, err)
+
+	require.Len(t, buf, 4)
+	require.Equal(t, buf, []byte{0, 0, 0, 1})
 }
diff --git a/pgtype/generic_binary.go b/pgtype/generic_binary.go
deleted file mode 100644
index 76a1d351..00000000
--- a/pgtype/generic_binary.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package pgtype
-
-import (
-	"database/sql/driver"
-)
-
-// GenericBinary is a placeholder for binary format values that no other type exists
-// to handle.
-type GenericBinary Bytea
-
-func (dst *GenericBinary) Set(src interface{}) error {
-	return (*Bytea)(dst).Set(src)
-}
-
-func (dst GenericBinary) Get() interface{} {
-	return (Bytea)(dst).Get()
-}
-
-func (src *GenericBinary) AssignTo(dst interface{}) error {
-	return (*Bytea)(src).AssignTo(dst)
-}
-
-func (dst *GenericBinary) DecodeBinary(ci *ConnInfo, src []byte) error {
-	return (*Bytea)(dst).DecodeBinary(ci, src)
-}
-
-func (src GenericBinary) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
-	return (Bytea)(src).EncodeBinary(ci, buf)
-}
-
-// Scan implements the database/sql Scanner interface.
-func (dst *GenericBinary) Scan(src interface{}) error {
-	return (*Bytea)(dst).Scan(src)
-}
-
-// Value implements the database/sql/driver Valuer interface.
-func (src GenericBinary) Value() (driver.Value, error) {
-	return (Bytea)(src).Value()
-}
diff --git a/pgtype/pgtype.go b/pgtype/pgtype.go
index 47e3518e..7fff7dd5 100644
--- a/pgtype/pgtype.go
+++ b/pgtype/pgtype.go
@@ -258,7 +258,7 @@ func NewConnInfo() *ConnInfo {
 	ci.RegisterDataType(DataType{Name: "_aclitem", OID: ACLItemArrayOID, Codec: &ArrayCodec{ElementCodec: &TextFormatOnlyCodec{TextCodec{}}, ElementOID: ACLItemOID}})
 	ci.RegisterDataType(DataType{Name: "_bool", OID: BoolArrayOID, Codec: &ArrayCodec{ElementCodec: BoolCodec{}, ElementOID: BoolOID}})
 	ci.RegisterDataType(DataType{Name: "_bpchar", OID: BPCharArrayOID, Codec: &ArrayCodec{ElementCodec: TextCodec{}, ElementOID: BPCharOID}})
-	ci.RegisterDataType(DataType{Value: &ByteaArray{}, Name: "_bytea", OID: ByteaArrayOID})
+	ci.RegisterDataType(DataType{Name: "_bytea", OID: ByteaArrayOID, Codec: &ArrayCodec{ElementCodec: ByteaCodec{}, ElementOID: ByteaOID}})
 	ci.RegisterDataType(DataType{Value: &CIDRArray{}, Name: "_cidr", OID: CIDRArrayOID})
 	ci.RegisterDataType(DataType{Value: &DateArray{}, Name: "_date", OID: DateArrayOID})
 	ci.RegisterDataType(DataType{Value: &Float4Array{}, Name: "_float4", OID: Float4ArrayOID})
@@ -284,7 +284,7 @@ func NewConnInfo() *ConnInfo {
 	ci.RegisterDataType(DataType{Name: "bool", OID: BoolOID, Codec: BoolCodec{}})
 	ci.RegisterDataType(DataType{Name: "box", OID: BoxOID, Codec: BoxCodec{}})
 	ci.RegisterDataType(DataType{Name: "bpchar", OID: BPCharOID, Codec: TextCodec{}})
-	ci.RegisterDataType(DataType{Value: &Bytea{}, Name: "bytea", OID: ByteaOID})
+	ci.RegisterDataType(DataType{Name: "bytea", OID: ByteaOID, Codec: ByteaCodec{}})
 	ci.RegisterDataType(DataType{Value: &QChar{}, Name: "char", OID: QCharOID})
 	ci.RegisterDataType(DataType{Value: &CID{}, Name: "cid", OID: CIDOID})
 	ci.RegisterDataType(DataType{Value: &CIDR{}, Name: "cidr", OID: CIDROID})
@@ -810,6 +810,10 @@ func (plan *pointerEmptyInterfaceScanPlan) Scan(ci *ConnInfo, oid uint32, format
 
 // PlanScan prepares a plan to scan a value into dst.
 func (ci *ConnInfo) PlanScan(oid uint32, formatCode int16, dst interface{}) ScanPlan {
+	if _, ok := dst.(*UndecodedBytes); ok {
+		return scanPlanAnyToUndecodedBytes{}
+	}
+
 	switch formatCode {
 	case BinaryFormatCode:
 		switch dst.(type) {
diff --git a/rows.go b/rows.go
index 62a19016..8e9fdc70 100644
--- a/rows.go
+++ b/rows.go
@@ -262,15 +262,17 @@ func (rows *connRows) Values() ([]interface{}, error) {
 						values = append(values, string(buf))
 					}
 				case BinaryFormatCode:
-					decoder, ok := value.(pgtype.BinaryDecoder)
-					if !ok {
-						decoder = &pgtype.GenericBinary{}
+					if decoder, ok := value.(pgtype.BinaryDecoder); ok {
+						err := decoder.DecodeBinary(rows.connInfo, buf)
+						if err != nil {
+							rows.fatal(err)
+						}
+						values = append(values, value.Get())
+					} else {
+						newBuf := make([]byte, len(buf))
+						copy(newBuf, buf)
+						values = append(values, newBuf)
 					}
-					err := decoder.DecodeBinary(rows.connInfo, buf)
-					if err != nil {
-						rows.fatal(err)
-					}
-					values = append(values, value.Get())
 				default:
 					rows.fatal(errors.New("Unknown format code"))
 				}
@@ -286,12 +288,9 @@ func (rows *connRows) Values() ([]interface{}, error) {
 			case TextFormatCode:
 				values = append(values, string(buf))
 			case BinaryFormatCode:
-				decoder := &pgtype.GenericBinary{}
-				err := decoder.DecodeBinary(rows.connInfo, buf)
-				if err != nil {
-					rows.fatal(err)
-				}
-				values = append(values, decoder.Get())
+				newBuf := make([]byte, len(buf))
+				copy(newBuf, buf)
+				values = append(values, newBuf)
 			default:
 				rows.fatal(errors.New("Unknown format code"))
 			}