Skip to content

Commit 12eb6ae

Browse files
committed
Fix: json/jsonb sql.Scanner source type is []byte
Previously, when scanning a json/jsonb value into a sql.Scanner via database/sql the source value was a []byte. However, when scanning into a sql.Scanner via pgx the source value was a string. This change ensures that the source value is consistently a []byte. The problem was pgx has one scan plan for sql.Scanner when a Codec is available and another when it is not. When a Codec is not available, this means we don't know the underlying type, so all we can do is pass text formatted values as a string and binary formatted values as a []byte. However, when a Codec is available we can use the Codec to call DecodeDatabaseSQLValue which provides the proper source type. JSONCodec.planScan was incorrectly using scanPlanSQLScanner instead of scanPlanCodecSQLScanner, which resulted in the source type being a string.
1 parent 79b1775 commit 12eb6ae

2 files changed

Lines changed: 23 additions & 1 deletion

File tree

pgtype/json.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ func (c *JSONCodec) planScan(m *Map, oid uint32, formatCode int16, target any, d
157157
case BytesScanner:
158158
return &scanPlanBinaryBytesToBytesScanner{}
159159
case sql.Scanner:
160-
return &scanPlanSQLScanner{formatCode: formatCode}
160+
return &scanPlanCodecSQLScanner{c: c, m: m, oid: oid, formatCode: formatCode}
161161
}
162162

163163
rv := reflect.ValueOf(target)

pgtype/json_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,3 +360,25 @@ func TestJSONCodecScanNullToPointerToSQLScanner(t *testing.T) {
360360
require.Nil(t, dest)
361361
})
362362
}
363+
364+
type SQLScannerFunc func(src any) error
365+
366+
func (f SQLScannerFunc) Scan(src any) error {
367+
return f(src)
368+
}
369+
370+
// https://github.com/jackc/pgx/issues/2403#issuecomment-3480454865
371+
//
372+
// Previously, when scanning a json/jsonb value into a sql.Scanner via database/sql the source value was a []byte.
373+
// However, when scanning into a sql.Scanner via pgx the source value was a string. This test ensures that the source
374+
// value is a []byte.
375+
func TestJSONCodecSQLScannerReceivesByteSlice(t *testing.T) {
376+
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
377+
f := SQLScannerFunc(func(src any) error {
378+
require.IsType(t, []byte{}, src)
379+
return nil
380+
})
381+
err := conn.QueryRow(ctx, "select '{}'::json").Scan(&f)
382+
require.NoError(t, err)
383+
})
384+
}

0 commit comments

Comments
 (0)