Prefer driver.Value over wrap plans when encoding
This is tricky due to driver.Valuer returning any. For example, we can plan for fmt.Stringer because it always returns a string. Because of this driver.Valuer was always handled as the last option. But with pgx v5 now having the ability to find underlying types like a string and supporting fmt.Stringer it meant that driver.Valuer was often not getting called because something else was found first. This change tries driver.Valuer immediately after the initial PlanScan for the Codec. So a type that directly implements a pgx interface should be used, but driver.Valuer will be prefered before all the attempts to handle renamed types, pointer deferencing, etc. fixes https://github.com/jackc/pgx/issues/1319 fixes https://github.com/jackc/pgx/issues/1311
This commit is contained in:
@@ -3,7 +3,9 @@ package pgtype_test
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"regexp"
|
||||
@@ -265,6 +267,56 @@ func TestMapScanPtrToPtrToSlice(t *testing.T) {
|
||||
require.Equal(t, []string{"foo", "bar"}, *v)
|
||||
}
|
||||
|
||||
type databaseValuerString string
|
||||
|
||||
func (s databaseValuerString) Value() (driver.Value, error) {
|
||||
return fmt.Sprintf("%d", len(s)), nil
|
||||
}
|
||||
|
||||
// https://github.com/jackc/pgx/issues/1319
|
||||
func TestMapEncodeTextFormatDatabaseValuerThatIsRenamedSimpleType(t *testing.T) {
|
||||
m := pgtype.NewMap()
|
||||
src := databaseValuerString("foo")
|
||||
buf, err := m.Encode(pgtype.TextOID, pgtype.TextFormatCode, src, nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "3", string(buf))
|
||||
}
|
||||
|
||||
type databaseValuerFmtStringer string
|
||||
|
||||
func (s databaseValuerFmtStringer) Value() (driver.Value, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s databaseValuerFmtStringer) String() string {
|
||||
return "foobar"
|
||||
}
|
||||
|
||||
// https://github.com/jackc/pgx/issues/1311
|
||||
func TestMapEncodeTextFormatDatabaseValuerThatIsFmtStringer(t *testing.T) {
|
||||
m := pgtype.NewMap()
|
||||
src := databaseValuerFmtStringer("")
|
||||
buf, err := m.Encode(pgtype.TextOID, pgtype.TextFormatCode, src, nil)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, buf)
|
||||
}
|
||||
|
||||
type databaseValuerStringFormat struct {
|
||||
n int32
|
||||
}
|
||||
|
||||
func (v databaseValuerStringFormat) Value() (driver.Value, error) {
|
||||
return fmt.Sprint(v.n), nil
|
||||
}
|
||||
|
||||
func TestMapEncodeBinaryFormatDatabaseValuerThatReturnsString(t *testing.T) {
|
||||
m := pgtype.NewMap()
|
||||
src := databaseValuerStringFormat{n: 42}
|
||||
buf, err := m.Encode(pgtype.Int4OID, pgtype.BinaryFormatCode, src, nil)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []byte{0, 0, 0, 42}, buf)
|
||||
}
|
||||
|
||||
func BenchmarkMapScanInt4IntoBinaryDecoder(b *testing.B) {
|
||||
m := pgtype.NewMap()
|
||||
src := []byte{0, 0, 0, 42}
|
||||
|
||||
Reference in New Issue
Block a user