Skip to content

Commit

Permalink
Special case the underlying type of []byte
Browse files Browse the repository at this point in the history
Underlying types were already tried. But []byte is not a normal
underlying type. It is a slice. But since is can be treated as a scalar
instead of an array / slice we need to special case it.

#1763
  • Loading branch information
jackc committed Oct 13, 2023
1 parent 8a09979 commit 45f807f
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 0 deletions.
15 changes: 15 additions & 0 deletions pgtype/pgtype.go
Original file line number Diff line number Diff line change
Expand Up @@ -1358,6 +1358,8 @@ var kindToTypes map[reflect.Kind]reflect.Type = map[reflect.Kind]reflect.Type{
reflect.Bool: reflect.TypeOf(false),
}

var byteSliceType = reflect.TypeOf([]byte{})

type underlyingTypeEncodePlan struct {
nextValueType reflect.Type
next EncodePlan
Expand All @@ -1372,6 +1374,10 @@ func (plan *underlyingTypeEncodePlan) Encode(value any, buf []byte) (newBuf []by
// TryWrapFindUnderlyingTypeEncodePlan tries to convert to a Go builtin type. e.g. If value was of type MyString and
// MyString was defined as a string then a wrapper plan would be returned that converts MyString to string.
func TryWrapFindUnderlyingTypeEncodePlan(value any) (plan WrappedEncodePlanNextSetter, nextValue any, ok bool) {
if value == nil {
return nil, nil, false
}

if _, ok := value.(driver.Valuer); ok {
return nil, nil, false
}
Expand All @@ -1387,6 +1393,15 @@ func TryWrapFindUnderlyingTypeEncodePlan(value any) (plan WrappedEncodePlanNextS
return &underlyingTypeEncodePlan{nextValueType: nextValueType}, refValue.Convert(nextValueType).Interface(), true
}

// []byte is a special case. It is a slice but we treat it as a scalar type. In the case of a named type like
// json.RawMessage which is defined as []byte the underlying type should be considered as []byte. But any other slice
// does not have a special underlying type.
//
// https://github.com/jackc/pgx/issues/1763
if refValue.Type() != byteSliceType && refValue.Type().AssignableTo(byteSliceType) {
return &underlyingTypeEncodePlan{nextValueType: byteSliceType}, refValue.Convert(byteSliceType).Interface(), true
}

return nil, nil, false
}

Expand Down
9 changes: 9 additions & 0 deletions pgtype/pgtype_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"database/sql"
"database/sql/driver"
"encoding/json"
"errors"
"fmt"
"net"
Expand Down Expand Up @@ -417,6 +418,14 @@ func TestMapEncodeByteSliceIntoUnregisteredTypeTextFormat(t *testing.T) {
require.Equal(t, []byte(`\x00010203`), buf)
}

// https://github.com/jackc/pgx/issues/1763
func TestMapEncodeNamedTypeOfByteSliceIntoTextTextFormat(t *testing.T) {
m := pgtype.NewMap()
buf, err := m.Encode(pgtype.TextOID, pgtype.TextFormatCode, json.RawMessage(`{"foo": "bar"}`), nil)
require.NoError(t, err)
require.Equal(t, []byte(`{"foo": "bar"}`), buf)
}

// https://github.com/jackc/pgx/issues/1326
func TestMapScanPointerToRenamedType(t *testing.T) {
srcBuf := []byte("foo")
Expand Down

0 comments on commit 45f807f

Please sign in to comment.