Skip to content

Commit 88dfc22

Browse files
committed
Fix simple protocol encoding of json.RawMessage
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
1 parent 2e84dcc commit 88dfc22

File tree

3 files changed

+27
-0
lines changed

3 files changed

+27
-0
lines changed

pgtype/json.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ func (c JSONCodec) PlanEncode(m *Map, oid uint32, format int16, value any) Encod
2525
case []byte:
2626
return encodePlanJSONCodecEitherFormatByteSlice{}
2727

28+
// Handle json.RawMessage specifically because if it is run through json.Marshal it may be mutated.
29+
// e.g. `{"foo": "bar"}` -> `{"foo":"bar"}`.
30+
case json.RawMessage:
31+
return encodePlanJSONCodecEitherFormatJSONRawMessage{}
32+
2833
// Cannot rely on driver.Valuer being handled later because anything can be marshalled.
2934
//
3035
// https://github.com/jackc/pgx/issues/1430
@@ -79,6 +84,18 @@ func (encodePlanJSONCodecEitherFormatByteSlice) Encode(value any, buf []byte) (n
7984
return buf, nil
8085
}
8186

87+
type encodePlanJSONCodecEitherFormatJSONRawMessage struct{}
88+
89+
func (encodePlanJSONCodecEitherFormatJSONRawMessage) Encode(value any, buf []byte) (newBuf []byte, err error) {
90+
jsonBytes := value.(json.RawMessage)
91+
if jsonBytes == nil {
92+
return nil, nil
93+
}
94+
95+
buf = append(buf, jsonBytes...)
96+
return buf, nil
97+
}
98+
8299
type encodePlanJSONCodecEitherFormatMarshal struct{}
83100

84101
func (encodePlanJSONCodecEitherFormatMarshal) Encode(value any, buf []byte) (newBuf []byte, err error) {

pgtype/pgtype_default.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package pgtype
22

33
import (
4+
"encoding/json"
45
"net"
56
"net/netip"
67
"reflect"
@@ -173,6 +174,7 @@ func initDefaultMap() {
173174
registerDefaultPgTypeVariants[time.Time](defaultMap, "timestamptz")
174175
registerDefaultPgTypeVariants[time.Duration](defaultMap, "interval")
175176
registerDefaultPgTypeVariants[string](defaultMap, "text")
177+
registerDefaultPgTypeVariants[json.RawMessage](defaultMap, "json")
176178
registerDefaultPgTypeVariants[[]byte](defaultMap, "bytea")
177179

178180
registerDefaultPgTypeVariants[net.IP](defaultMap, "inet")

pgtype/pgtype_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,14 @@ func TestMapEncodePlanCacheUUIDTypeConfusion(t *testing.T) {
546546
require.Error(t, err)
547547
}
548548

549+
// https://github.com/jackc/pgx/issues/1763
550+
func TestMapEncodeRawJSONIntoUnknownOID(t *testing.T) {
551+
m := pgtype.NewMap()
552+
buf, err := m.Encode(0, pgtype.TextFormatCode, json.RawMessage(`{"foo": "bar"}`), nil)
553+
require.NoError(t, err)
554+
require.Equal(t, []byte(`{"foo": "bar"}`), buf)
555+
}
556+
549557
func BenchmarkMapScanInt4IntoBinaryDecoder(b *testing.B) {
550558
m := pgtype.NewMap()
551559
src := []byte{0, 0, 0, 42}

0 commit comments

Comments
 (0)