Allow scanning null even if PG and Go types are incompatible
refs https://github.com/jackc/pgx/issues/1326
This commit is contained in:
@@ -558,6 +558,23 @@ type scanPlanFail struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (plan *scanPlanFail) Scan(src []byte, dst any) error {
|
func (plan *scanPlanFail) Scan(src []byte, dst any) error {
|
||||||
|
// If src is NULL it might be possible to scan into dst even though it is the types are not compatible. While this
|
||||||
|
// may seem to be a contrived case it can occur when selecting NULL directly. PostgreSQL assigns it the type of text.
|
||||||
|
// It would be surprising to the caller to have to cast the NULL (e.g. `select null::int`). So try to figure out a
|
||||||
|
// compatible data type for dst and scan with that.
|
||||||
|
//
|
||||||
|
// See https://github.com/jackc/pgx/issues/1326
|
||||||
|
if src == nil {
|
||||||
|
// As a horrible hack try all types to find anything that can scan into dst.
|
||||||
|
for oid := range plan.m.oidToType {
|
||||||
|
// using planScan instead of Scan or PlanScan to avoid polluting the planned scan cache.
|
||||||
|
plan := plan.m.planScan(oid, plan.formatCode, dst)
|
||||||
|
if _, ok := plan.(*scanPlanFail); !ok {
|
||||||
|
return plan.Scan(src, dst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var format string
|
var format string
|
||||||
switch plan.formatCode {
|
switch plan.formatCode {
|
||||||
case TextFormatCode:
|
case TextFormatCode:
|
||||||
|
|||||||
@@ -329,6 +329,21 @@ func TestMapScanPointerToRenamedType(t *testing.T) {
|
|||||||
assert.Equal(t, "foo", string(*rs))
|
assert.Equal(t, "foo", string(*rs))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://github.com/jackc/pgx/issues/1326
|
||||||
|
func TestMapScanNullToWrongType(t *testing.T) {
|
||||||
|
m := pgtype.NewMap()
|
||||||
|
|
||||||
|
var n *int32
|
||||||
|
err := m.Scan(pgtype.TextOID, pgx.TextFormatCode, nil, &n)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Nil(t, n)
|
||||||
|
|
||||||
|
var pn pgtype.Int4
|
||||||
|
err = m.Scan(pgtype.TextOID, pgx.TextFormatCode, nil, &pn)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.False(t, pn.Valid)
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkMapScanInt4IntoBinaryDecoder(b *testing.B) {
|
func BenchmarkMapScanInt4IntoBinaryDecoder(b *testing.B) {
|
||||||
m := pgtype.NewMap()
|
m := pgtype.NewMap()
|
||||||
src := []byte{0, 0, 0, 42}
|
src := []byte{0, 0, 0, 42}
|
||||||
|
|||||||
Reference in New Issue
Block a user