Fix json scan of non-string pointer to pointer

https://github.com/jackc/pgx/issues/1691
pull/1693/head
Jack Christensen 2023-07-19 20:54:05 -05:00
parent ef9e26a5d5
commit d58fe2d53c
2 changed files with 43 additions and 13 deletions

View File

@ -92,6 +92,23 @@ func (JSONCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan
switch target.(type) { switch target.(type) {
case *string: case *string:
return scanPlanAnyToString{} return scanPlanAnyToString{}
case **string:
// This is to fix **string scanning. It seems wrong to special case **string, but it's not clear what a better
// solution would be.
//
// https://github.com/jackc/pgx/issues/1470 -- **string
// https://github.com/jackc/pgx/issues/1691 -- ** anything else
if wrapperPlan, nextDst, ok := TryPointerPointerScanPlan(target); ok {
if nextPlan := m.planScan(oid, format, nextDst); nextPlan != nil {
if _, failed := nextPlan.(*scanPlanFail); !failed {
wrapperPlan.SetNext(nextPlan)
return wrapperPlan
}
}
}
case *[]byte: case *[]byte:
return scanPlanJSONToByteSlice{} return scanPlanJSONToByteSlice{}
case BytesScanner: case BytesScanner:
@ -104,19 +121,6 @@ func (JSONCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan
return &scanPlanSQLScanner{formatCode: format} return &scanPlanSQLScanner{formatCode: format}
} }
// This is to fix **string scanning. It seems wrong to special case sql.Scanner and pointer to pointer, but it's not
// clear what a better solution would be.
//
// https://github.com/jackc/pgx/issues/1470
if wrapperPlan, nextDst, ok := TryPointerPointerScanPlan(target); ok {
if nextPlan := m.planScan(oid, format, nextDst); nextPlan != nil {
if _, failed := nextPlan.(*scanPlanFail); !failed {
wrapperPlan.SetNext(nextPlan)
return wrapperPlan
}
}
}
return scanPlanJSONToJSONUnmarshal{} return scanPlanJSONToJSONUnmarshal{}
} }

View File

@ -121,6 +121,32 @@ func TestJSONCodecPointerToPointerToString(t *testing.T) {
}) })
} }
// https://github.com/jackc/pgx/issues/1691
func TestJSONCodecPointerToPointerToInt(t *testing.T) {
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
n := 44
p := &n
err := conn.QueryRow(ctx, "select 'null'::jsonb").Scan(&p)
require.NoError(t, err)
require.Nil(t, p)
})
}
// https://github.com/jackc/pgx/issues/1691
func TestJSONCodecPointerToPointerToStruct(t *testing.T) {
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
type ImageSize struct {
Height int `json:"height"`
Width int `json:"width"`
Str string `json:"str"`
}
is := &ImageSize{Height: 100, Width: 100, Str: "str"}
err := conn.QueryRow(ctx, `select 'null'::jsonb`).Scan(&is)
require.NoError(t, err)
require.Nil(t, is)
})
}
func TestJSONCodecClearExistingValueBeforeUnmarshal(t *testing.T) { func TestJSONCodecClearExistingValueBeforeUnmarshal(t *testing.T) {
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) { defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
m := map[string]any{} m := map[string]any{}