Skip to content

Commit

Permalink
Fix simple protocol encoding of json.RawMessage
Browse files Browse the repository at this point in the history
The underlying type of json.RawMessage is a []byte so to avoid it being
considered binary data we need to handle it specifically. This is done
by registerDefaultPgTypeVariants. In addition, handle json.RawMessage in
the JSONCodec PlanEncode to avoid it being mutated by json.Marshal.

#1763
  • Loading branch information
jackc committed Mar 2, 2024
1 parent 2e84dcc commit 88dfc22
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 0 deletions.
17 changes: 17 additions & 0 deletions pgtype/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ func (c JSONCodec) PlanEncode(m *Map, oid uint32, format int16, value any) Encod
case []byte:
return encodePlanJSONCodecEitherFormatByteSlice{}

// Handle json.RawMessage specifically because if it is run through json.Marshal it may be mutated.
// e.g. `{"foo": "bar"}` -> `{"foo":"bar"}`.
case json.RawMessage:
return encodePlanJSONCodecEitherFormatJSONRawMessage{}

// Cannot rely on driver.Valuer being handled later because anything can be marshalled.
//
// https://github.com/jackc/pgx/issues/1430
Expand Down Expand Up @@ -79,6 +84,18 @@ func (encodePlanJSONCodecEitherFormatByteSlice) Encode(value any, buf []byte) (n
return buf, nil
}

type encodePlanJSONCodecEitherFormatJSONRawMessage struct{}

func (encodePlanJSONCodecEitherFormatJSONRawMessage) Encode(value any, buf []byte) (newBuf []byte, err error) {
jsonBytes := value.(json.RawMessage)
if jsonBytes == nil {
return nil, nil
}

buf = append(buf, jsonBytes...)
return buf, nil
}

type encodePlanJSONCodecEitherFormatMarshal struct{}

func (encodePlanJSONCodecEitherFormatMarshal) Encode(value any, buf []byte) (newBuf []byte, err error) {
Expand Down
2 changes: 2 additions & 0 deletions pgtype/pgtype_default.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package pgtype

import (
"encoding/json"
"net"
"net/netip"
"reflect"
Expand Down Expand Up @@ -173,6 +174,7 @@ func initDefaultMap() {
registerDefaultPgTypeVariants[time.Time](defaultMap, "timestamptz")
registerDefaultPgTypeVariants[time.Duration](defaultMap, "interval")
registerDefaultPgTypeVariants[string](defaultMap, "text")
registerDefaultPgTypeVariants[json.RawMessage](defaultMap, "json")
registerDefaultPgTypeVariants[[]byte](defaultMap, "bytea")

registerDefaultPgTypeVariants[net.IP](defaultMap, "inet")
Expand Down
8 changes: 8 additions & 0 deletions pgtype/pgtype_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,14 @@ func TestMapEncodePlanCacheUUIDTypeConfusion(t *testing.T) {
require.Error(t, err)
}

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

func BenchmarkMapScanInt4IntoBinaryDecoder(b *testing.B) {
m := pgtype.NewMap()
src := []byte{0, 0, 0, 42}
Expand Down

0 comments on commit 88dfc22

Please sign in to comment.