Skip to content

Commit

Permalink
Fix issue with order of json encoding #1805
Browse files Browse the repository at this point in the history
  • Loading branch information
dedalusj authored and jackc committed Nov 25, 2023
1 parent 94e56e6 commit 7ebced9
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 6 deletions.
15 changes: 9 additions & 6 deletions pgtype/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,21 @@ func (c JSONCodec) PlanEncode(m *Map, oid uint32, format int16, value any) Encod
case []byte:
return encodePlanJSONCodecEitherFormatByteSlice{}

// Cannot rely on driver.Valuer being handled later because anything can be marshalled.
//
// https://github.com/jackc/pgx/issues/1430
//
// Check for driver.Valuer must come before json.Marshaler so that it is guaranteed to beused
// when both are implemented https://github.com/jackc/pgx/issues/1805
case driver.Valuer:
return &encodePlanDriverValuer{m: m, oid: oid, formatCode: format}

// 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
case driver.Valuer:
return &encodePlanDriverValuer{m: m, oid: oid, formatCode: format}
}

// Because anything can be marshalled the normal wrapping in Map.PlanScan doesn't get a chance to run. So try the
Expand Down
39 changes: 39 additions & 0 deletions pgtype/json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package pgtype_test
import (
"context"
"database/sql"
"database/sql/driver"
"encoding/json"
"errors"
"testing"

pgx "github.com/jackc/pgx/v5"
Expand Down Expand Up @@ -55,6 +58,9 @@ func TestJSONCodec(t *testing.T) {

// Test driver.Valuer. (https://github.com/jackc/pgx/issues/1430)
{sql.NullInt64{Int64: 42, Valid: true}, new(sql.NullInt64), isExpectedEq(sql.NullInt64{Int64: 42, Valid: true})},

// Test driver.Valuer is used before json.Marshaler (https://github.com/jackc/pgx/issues/1805)
{Issue1805(7), new(Issue1805), isExpectedEq(Issue1805(7))},
})

pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, pgxtest.KnownOIDQueryExecModes, "json", []pgxtest.ValueRoundTripTest{
Expand All @@ -68,6 +74,39 @@ func TestJSONCodec(t *testing.T) {
})
}

type Issue1805 int

func (i *Issue1805) Scan(src any) error {
var source []byte
switch src.(type) {
case string:
source = []byte(src.(string))
case []byte:
source = src.([]byte)
default:
return errors.New("unknown source type")
}
var newI int
if err := json.Unmarshal(source, &newI); err != nil {
return err
}
*i = Issue1805(newI)
return nil
}

func (i Issue1805) Value() (driver.Value, error) {
b, err := json.Marshal(int(i))
return string(b), err
}

func (i Issue1805) UnmarshalJSON(bytes []byte) error {
return errors.New("UnmarshalJSON called")
}

func (i Issue1805) MarshalJSON() ([]byte, error) {
return nil, errors.New("MarshalJSON called")
}

// https://github.com/jackc/pgx/issues/1273#issuecomment-1221414648
func TestJSONCodecUnmarshalSQLNull(t *testing.T) {
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
Expand Down

0 comments on commit 7ebced9

Please sign in to comment.