From 524f6611363418799624f2321b158ba405359692 Mon Sep 17 00:00:00 2001 From: Jack Christensen Date: Tue, 11 Jul 2023 20:28:36 -0500 Subject: [PATCH] Fix JSON encoding for pointer to structs implementing json.Marshaler https://github.com/jackc/pgx/issues/1681 --- pgtype/json.go | 7 +++++++ pgtype/json_test.go | 24 ++++++++++++++++++++++++ pgtype/jsonb_test.go | 10 ++++++++++ 3 files changed, 41 insertions(+) diff --git a/pgtype/json.go b/pgtype/json.go index 753f24103..b7a7101ec 100644 --- a/pgtype/json.go +++ b/pgtype/json.go @@ -25,6 +25,13 @@ func (c JSONCodec) PlanEncode(m *Map, oid uint32, format int16, value any) Encod case []byte: return encodePlanJSONCodecEitherFormatByteSlice{} + // Must come before trying wrap encode plans because a pointer to a struct may be unwrapped to a struct that can be + // marshalled. + // + // https://github.com/jackc/pgx/issues/1681 + case json.Marshaler: + return encodePlanJSONCodecEitherFormatMarshal{} + // Cannot rely on driver.Valuer being handled later because anything can be marshalled. // // https://github.com/jackc/pgx/issues/1430 diff --git a/pgtype/json_test.go b/pgtype/json_test.go index f3368a411..a1f24478a 100644 --- a/pgtype/json_test.go +++ b/pgtype/json_test.go @@ -133,3 +133,27 @@ func TestJSONCodecClearExistingValueBeforeUnmarshal(t *testing.T) { require.Equal(t, map[string]any{"baz": "quz"}, m) }) } + +type ParentIssue1681 struct { + Child ChildIssue1681 +} + +func (t *ParentIssue1681) MarshalJSON() ([]byte, error) { + return []byte(`{"custom":"thing"}`), nil +} + +type ChildIssue1681 struct{} + +func (t ChildIssue1681) MarshalJSON() ([]byte, error) { + return []byte(`{"someVal": false}`), nil +} + +// https://github.com/jackc/pgx/issues/1681 +func TestJSONCodecEncodeJSONMarshalerThatCanBeWrapped(t *testing.T) { + defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) { + var jsonStr string + err := conn.QueryRow(context.Background(), "select $1::json", &ParentIssue1681{}).Scan(&jsonStr) + require.NoError(t, err) + require.Equal(t, `{"custom":"thing"}`, jsonStr) + }) +} diff --git a/pgtype/jsonb_test.go b/pgtype/jsonb_test.go index dfaeb8c46..5bfbdbe3a 100644 --- a/pgtype/jsonb_test.go +++ b/pgtype/jsonb_test.go @@ -70,3 +70,13 @@ func TestJSONBCodecUnmarshalSQLNull(t *testing.T) { require.EqualError(t, err, "can't scan into dest[0]: cannot scan NULL into *int") }) } + +// https://github.com/jackc/pgx/issues/1681 +func TestJSONBCodecEncodeJSONMarshalerThatCanBeWrapped(t *testing.T) { + defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) { + var jsonStr string + err := conn.QueryRow(context.Background(), "select $1::jsonb", &ParentIssue1681{}).Scan(&jsonStr) + require.NoError(t, err) + require.Equal(t, `{"custom": "thing"}`, jsonStr) // Note that unlike json, jsonb reformats the JSON string. + }) +}