mirror of
https://github.com/jackc/pgx.git
synced 2025-05-31 11:42:24 +00:00
Better number to string handling
Avoid ambiguity of stringWrapper implementing Int64Scanner and Float64Scanner.
This commit is contained in:
parent
8cf6721d66
commit
829babcea9
@ -841,7 +841,9 @@ func TestDomainType(t *testing.T) {
|
|||||||
|
|
||||||
// Domain type uint64 is a PostgreSQL domain of underlying type numeric.
|
// Domain type uint64 is a PostgreSQL domain of underlying type numeric.
|
||||||
|
|
||||||
// Unregistered type can be used as string.
|
// In the extended protocol preparing "select $1::uint64" appears to create a statement that expects a param OID of
|
||||||
|
// uint64 but a result OID of the underlying numeric.
|
||||||
|
|
||||||
var s string
|
var s string
|
||||||
err := conn.QueryRow(context.Background(), "select $1::uint64", "24").Scan(&s)
|
err := conn.QueryRow(context.Background(), "select $1::uint64", "24").Scan(&s)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -341,16 +340,6 @@ func (w stringWrapper) TextValue() (Text, error) {
|
|||||||
return Text{String: string(w), Valid: true}, nil
|
return Text{String: string(w), Valid: true}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *stringWrapper) ScanInt64(v Int8) error {
|
|
||||||
if !v.Valid {
|
|
||||||
return fmt.Errorf("cannot scan NULL into *string")
|
|
||||||
}
|
|
||||||
|
|
||||||
*w = stringWrapper(strconv.FormatInt(v.Int64, 10))
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type timeWrapper time.Time
|
type timeWrapper time.Time
|
||||||
|
|
||||||
func (w *timeWrapper) ScanDate(v Date) error {
|
func (w *timeWrapper) ScanDate(v Date) error {
|
||||||
|
@ -156,6 +156,8 @@ func (Float4Codec) PlanScan(m *Map, oid uint32, format int16, target interface{}
|
|||||||
return scanPlanBinaryFloat4ToFloat64Scanner{}
|
return scanPlanBinaryFloat4ToFloat64Scanner{}
|
||||||
case Int64Scanner:
|
case Int64Scanner:
|
||||||
return scanPlanBinaryFloat4ToInt64Scanner{}
|
return scanPlanBinaryFloat4ToInt64Scanner{}
|
||||||
|
case TextScanner:
|
||||||
|
return scanPlanBinaryFloat4ToTextScanner{}
|
||||||
}
|
}
|
||||||
case TextFormatCode:
|
case TextFormatCode:
|
||||||
switch target.(type) {
|
switch target.(type) {
|
||||||
@ -229,6 +231,25 @@ func (scanPlanBinaryFloat4ToInt64Scanner) Scan(src []byte, dst interface{}) erro
|
|||||||
return s.ScanInt64(Int8{Int64: i64, Valid: true})
|
return s.ScanInt64(Int8{Int64: i64, Valid: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type scanPlanBinaryFloat4ToTextScanner struct{}
|
||||||
|
|
||||||
|
func (scanPlanBinaryFloat4ToTextScanner) Scan(src []byte, dst interface{}) error {
|
||||||
|
s := (dst).(TextScanner)
|
||||||
|
|
||||||
|
if src == nil {
|
||||||
|
return s.ScanText(Text{})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(src) != 4 {
|
||||||
|
return fmt.Errorf("invalid length for float4: %v", len(src))
|
||||||
|
}
|
||||||
|
|
||||||
|
ui32 := int32(binary.BigEndian.Uint32(src))
|
||||||
|
f32 := math.Float32frombits(uint32(ui32))
|
||||||
|
|
||||||
|
return s.ScanText(Text{String: strconv.FormatFloat(float64(f32), 'f', -1, 32), Valid: true})
|
||||||
|
}
|
||||||
|
|
||||||
type scanPlanTextAnyToFloat32 struct{}
|
type scanPlanTextAnyToFloat32 struct{}
|
||||||
|
|
||||||
func (scanPlanTextAnyToFloat32) Scan(src []byte, dst interface{}) error {
|
func (scanPlanTextAnyToFloat32) Scan(src []byte, dst interface{}) error {
|
||||||
|
@ -17,6 +17,7 @@ func TestFloat4Codec(t *testing.T) {
|
|||||||
{float32(9999.99), new(float32), isExpectedEq(float32(9999.99))},
|
{float32(9999.99), new(float32), isExpectedEq(float32(9999.99))},
|
||||||
{pgtype.Float4{}, new(pgtype.Float4), isExpectedEq(pgtype.Float4{})},
|
{pgtype.Float4{}, new(pgtype.Float4), isExpectedEq(pgtype.Float4{})},
|
||||||
{int64(1), new(int64), isExpectedEq(int64(1))},
|
{int64(1), new(int64), isExpectedEq(int64(1))},
|
||||||
|
{"1.23", new(string), isExpectedEq("1.23")},
|
||||||
{nil, new(*float32), isExpectedEq((*float32)(nil))},
|
{nil, new(*float32), isExpectedEq((*float32)(nil))},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -194,6 +194,8 @@ func (Float8Codec) PlanScan(m *Map, oid uint32, format int16, target interface{}
|
|||||||
return scanPlanBinaryFloat8ToFloat64Scanner{}
|
return scanPlanBinaryFloat8ToFloat64Scanner{}
|
||||||
case Int64Scanner:
|
case Int64Scanner:
|
||||||
return scanPlanBinaryFloat8ToInt64Scanner{}
|
return scanPlanBinaryFloat8ToInt64Scanner{}
|
||||||
|
case TextScanner:
|
||||||
|
return scanPlanBinaryFloat8ToTextScanner{}
|
||||||
}
|
}
|
||||||
case TextFormatCode:
|
case TextFormatCode:
|
||||||
switch target.(type) {
|
switch target.(type) {
|
||||||
@ -267,6 +269,25 @@ func (scanPlanBinaryFloat8ToInt64Scanner) Scan(src []byte, dst interface{}) erro
|
|||||||
return s.ScanInt64(Int8{Int64: i64, Valid: true})
|
return s.ScanInt64(Int8{Int64: i64, Valid: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type scanPlanBinaryFloat8ToTextScanner struct{}
|
||||||
|
|
||||||
|
func (scanPlanBinaryFloat8ToTextScanner) Scan(src []byte, dst interface{}) error {
|
||||||
|
s := (dst).(TextScanner)
|
||||||
|
|
||||||
|
if src == nil {
|
||||||
|
return s.ScanText(Text{})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(src) != 8 {
|
||||||
|
return fmt.Errorf("invalid length for float8: %v", len(src))
|
||||||
|
}
|
||||||
|
|
||||||
|
ui64 := int64(binary.BigEndian.Uint64(src))
|
||||||
|
f64 := math.Float64frombits(uint64(ui64))
|
||||||
|
|
||||||
|
return s.ScanText(Text{String: strconv.FormatFloat(f64, 'f', -1, 64), Valid: true})
|
||||||
|
}
|
||||||
|
|
||||||
type scanPlanTextAnyToFloat64 struct{}
|
type scanPlanTextAnyToFloat64 struct{}
|
||||||
|
|
||||||
func (scanPlanTextAnyToFloat64) Scan(src []byte, dst interface{}) error {
|
func (scanPlanTextAnyToFloat64) Scan(src []byte, dst interface{}) error {
|
||||||
|
@ -17,6 +17,7 @@ func TestFloat8Codec(t *testing.T) {
|
|||||||
{float64(9999.99), new(float64), isExpectedEq(float64(9999.99))},
|
{float64(9999.99), new(float64), isExpectedEq(float64(9999.99))},
|
||||||
{pgtype.Float8{}, new(pgtype.Float8), isExpectedEq(pgtype.Float8{})},
|
{pgtype.Float8{}, new(pgtype.Float8), isExpectedEq(pgtype.Float8{})},
|
||||||
{int64(1), new(int64), isExpectedEq(int64(1))},
|
{int64(1), new(int64), isExpectedEq(int64(1))},
|
||||||
|
{"1.23", new(string), isExpectedEq("1.23")},
|
||||||
{nil, new(*float64), isExpectedEq((*float64)(nil))},
|
{nil, new(*float64), isExpectedEq((*float64)(nil))},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -233,6 +233,8 @@ func (Int2Codec) PlanScan(m *Map, oid uint32, format int16, target interface{})
|
|||||||
return scanPlanBinaryInt2ToUint{}
|
return scanPlanBinaryInt2ToUint{}
|
||||||
case Int64Scanner:
|
case Int64Scanner:
|
||||||
return scanPlanBinaryInt2ToInt64Scanner{}
|
return scanPlanBinaryInt2ToInt64Scanner{}
|
||||||
|
case TextScanner:
|
||||||
|
return scanPlanBinaryInt2ToTextScanner{}
|
||||||
}
|
}
|
||||||
case TextFormatCode:
|
case TextFormatCode:
|
||||||
switch target.(type) {
|
switch target.(type) {
|
||||||
@ -557,6 +559,27 @@ func (scanPlanBinaryInt2ToInt64Scanner) Scan(src []byte, dst interface{}) error
|
|||||||
return s.ScanInt64(Int8{Int64: n, Valid: true})
|
return s.ScanInt64(Int8{Int64: n, Valid: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type scanPlanBinaryInt2ToTextScanner struct{}
|
||||||
|
|
||||||
|
func (scanPlanBinaryInt2ToTextScanner) Scan(src []byte, dst interface{}) error {
|
||||||
|
s, ok := (dst).(TextScanner)
|
||||||
|
if !ok {
|
||||||
|
return ErrScanTargetTypeChanged
|
||||||
|
}
|
||||||
|
|
||||||
|
if src == nil {
|
||||||
|
return s.ScanText(Text{})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(src) != 2 {
|
||||||
|
return fmt.Errorf("invalid length for int2: %v", len(src))
|
||||||
|
}
|
||||||
|
|
||||||
|
n := int64(int16(binary.BigEndian.Uint16(src)))
|
||||||
|
|
||||||
|
return s.ScanText(Text{String: strconv.FormatInt(n, 10), Valid: true})
|
||||||
|
}
|
||||||
|
|
||||||
type Int4 struct {
|
type Int4 struct {
|
||||||
Int32 int32
|
Int32 int32
|
||||||
Valid bool
|
Valid bool
|
||||||
@ -770,6 +793,8 @@ func (Int4Codec) PlanScan(m *Map, oid uint32, format int16, target interface{})
|
|||||||
return scanPlanBinaryInt4ToUint{}
|
return scanPlanBinaryInt4ToUint{}
|
||||||
case Int64Scanner:
|
case Int64Scanner:
|
||||||
return scanPlanBinaryInt4ToInt64Scanner{}
|
return scanPlanBinaryInt4ToInt64Scanner{}
|
||||||
|
case TextScanner:
|
||||||
|
return scanPlanBinaryInt4ToTextScanner{}
|
||||||
}
|
}
|
||||||
case TextFormatCode:
|
case TextFormatCode:
|
||||||
switch target.(type) {
|
switch target.(type) {
|
||||||
@ -1105,6 +1130,27 @@ func (scanPlanBinaryInt4ToInt64Scanner) Scan(src []byte, dst interface{}) error
|
|||||||
return s.ScanInt64(Int8{Int64: n, Valid: true})
|
return s.ScanInt64(Int8{Int64: n, Valid: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type scanPlanBinaryInt4ToTextScanner struct{}
|
||||||
|
|
||||||
|
func (scanPlanBinaryInt4ToTextScanner) Scan(src []byte, dst interface{}) error {
|
||||||
|
s, ok := (dst).(TextScanner)
|
||||||
|
if !ok {
|
||||||
|
return ErrScanTargetTypeChanged
|
||||||
|
}
|
||||||
|
|
||||||
|
if src == nil {
|
||||||
|
return s.ScanText(Text{})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(src) != 4 {
|
||||||
|
return fmt.Errorf("invalid length for int4: %v", len(src))
|
||||||
|
}
|
||||||
|
|
||||||
|
n := int64(int32(binary.BigEndian.Uint32(src)))
|
||||||
|
|
||||||
|
return s.ScanText(Text{String: strconv.FormatInt(n, 10), Valid: true})
|
||||||
|
}
|
||||||
|
|
||||||
type Int8 struct {
|
type Int8 struct {
|
||||||
Int64 int64
|
Int64 int64
|
||||||
Valid bool
|
Valid bool
|
||||||
@ -1318,6 +1364,8 @@ func (Int8Codec) PlanScan(m *Map, oid uint32, format int16, target interface{})
|
|||||||
return scanPlanBinaryInt8ToUint{}
|
return scanPlanBinaryInt8ToUint{}
|
||||||
case Int64Scanner:
|
case Int64Scanner:
|
||||||
return scanPlanBinaryInt8ToInt64Scanner{}
|
return scanPlanBinaryInt8ToInt64Scanner{}
|
||||||
|
case TextScanner:
|
||||||
|
return scanPlanBinaryInt8ToTextScanner{}
|
||||||
}
|
}
|
||||||
case TextFormatCode:
|
case TextFormatCode:
|
||||||
switch target.(type) {
|
switch target.(type) {
|
||||||
@ -1675,6 +1723,27 @@ func (scanPlanBinaryInt8ToInt64Scanner) Scan(src []byte, dst interface{}) error
|
|||||||
return s.ScanInt64(Int8{Int64: n, Valid: true})
|
return s.ScanInt64(Int8{Int64: n, Valid: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type scanPlanBinaryInt8ToTextScanner struct{}
|
||||||
|
|
||||||
|
func (scanPlanBinaryInt8ToTextScanner) Scan(src []byte, dst interface{}) error {
|
||||||
|
s, ok := (dst).(TextScanner)
|
||||||
|
if !ok {
|
||||||
|
return ErrScanTargetTypeChanged
|
||||||
|
}
|
||||||
|
|
||||||
|
if src == nil {
|
||||||
|
return s.ScanText(Text{})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(src) != 8 {
|
||||||
|
return fmt.Errorf("invalid length for int8: %v", len(src))
|
||||||
|
}
|
||||||
|
|
||||||
|
n := int64(int64(binary.BigEndian.Uint64(src)))
|
||||||
|
|
||||||
|
return s.ScanText(Text{String: strconv.FormatInt(n, 10), Valid: true})
|
||||||
|
}
|
||||||
|
|
||||||
type scanPlanTextAnyToInt8 struct{}
|
type scanPlanTextAnyToInt8 struct{}
|
||||||
|
|
||||||
func (scanPlanTextAnyToInt8) Scan(src []byte, dst interface{}) error {
|
func (scanPlanTextAnyToInt8) Scan(src []byte, dst interface{}) error {
|
||||||
|
@ -234,6 +234,8 @@ func (Int<%= pg_byte_size %>Codec) PlanScan(m *Map, oid uint32, format int16, ta
|
|||||||
return scanPlanBinaryInt<%= pg_byte_size %>ToUint{}
|
return scanPlanBinaryInt<%= pg_byte_size %>ToUint{}
|
||||||
case Int64Scanner:
|
case Int64Scanner:
|
||||||
return scanPlanBinaryInt<%= pg_byte_size %>ToInt64Scanner{}
|
return scanPlanBinaryInt<%= pg_byte_size %>ToInt64Scanner{}
|
||||||
|
case TextScanner:
|
||||||
|
return scanPlanBinaryInt<%= pg_byte_size %>ToTextScanner{}
|
||||||
}
|
}
|
||||||
case TextFormatCode:
|
case TextFormatCode:
|
||||||
switch target.(type) {
|
switch target.(type) {
|
||||||
@ -443,6 +445,29 @@ func (scanPlanBinaryInt<%= pg_byte_size %>ToInt64Scanner) Scan(src []byte, dst i
|
|||||||
|
|
||||||
return s.ScanInt64(Int8{Int64: n, Valid: true})
|
return s.ScanInt64(Int8{Int64: n, Valid: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<%# PostgreSQL binary format integer to Go TextScanner %>
|
||||||
|
type scanPlanBinaryInt<%= pg_byte_size %>ToTextScanner struct{}
|
||||||
|
|
||||||
|
func (scanPlanBinaryInt<%= pg_byte_size %>ToTextScanner) Scan(src []byte, dst interface{}) error {
|
||||||
|
s, ok := (dst).(TextScanner)
|
||||||
|
if !ok {
|
||||||
|
return ErrScanTargetTypeChanged
|
||||||
|
}
|
||||||
|
|
||||||
|
if src == nil {
|
||||||
|
return s.ScanText(Text{})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(src) != <%= pg_byte_size %> {
|
||||||
|
return fmt.Errorf("invalid length for int<%= pg_byte_size %>: %v", len(src))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
n := int64(int<%= pg_bit_size %>(binary.BigEndian.Uint<%= pg_bit_size %>(src)))
|
||||||
|
|
||||||
|
return s.ScanText(Text{String: strconv.FormatInt(n, 10), Valid: true})
|
||||||
|
}
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%# Any text to all integer types %>
|
<%# Any text to all integer types %>
|
||||||
|
@ -45,6 +45,7 @@ func TestInt2Codec(t *testing.T) {
|
|||||||
{1, new(int16), isExpectedEq(int16(1))},
|
{1, new(int16), isExpectedEq(int16(1))},
|
||||||
{math.MaxInt16, new(int16), isExpectedEq(int16(math.MaxInt16))},
|
{math.MaxInt16, new(int16), isExpectedEq(int16(math.MaxInt16))},
|
||||||
{1, new(pgtype.Int2), isExpectedEq(pgtype.Int2{Int16: 1, Valid: true})},
|
{1, new(pgtype.Int2), isExpectedEq(pgtype.Int2{Int16: 1, Valid: true})},
|
||||||
|
{"1", new(string), isExpectedEq("1")},
|
||||||
{pgtype.Int2{}, new(pgtype.Int2), isExpectedEq(pgtype.Int2{})},
|
{pgtype.Int2{}, new(pgtype.Int2), isExpectedEq(pgtype.Int2{})},
|
||||||
{nil, new(*int16), isExpectedEq((*int16)(nil))},
|
{nil, new(*int16), isExpectedEq((*int16)(nil))},
|
||||||
})
|
})
|
||||||
@ -126,6 +127,7 @@ func TestInt4Codec(t *testing.T) {
|
|||||||
{1, new(int32), isExpectedEq(int32(1))},
|
{1, new(int32), isExpectedEq(int32(1))},
|
||||||
{math.MaxInt32, new(int32), isExpectedEq(int32(math.MaxInt32))},
|
{math.MaxInt32, new(int32), isExpectedEq(int32(math.MaxInt32))},
|
||||||
{1, new(pgtype.Int4), isExpectedEq(pgtype.Int4{Int32: 1, Valid: true})},
|
{1, new(pgtype.Int4), isExpectedEq(pgtype.Int4{Int32: 1, Valid: true})},
|
||||||
|
{"1", new(string), isExpectedEq("1")},
|
||||||
{pgtype.Int4{}, new(pgtype.Int4), isExpectedEq(pgtype.Int4{})},
|
{pgtype.Int4{}, new(pgtype.Int4), isExpectedEq(pgtype.Int4{})},
|
||||||
{nil, new(*int32), isExpectedEq((*int32)(nil))},
|
{nil, new(*int32), isExpectedEq((*int32)(nil))},
|
||||||
})
|
})
|
||||||
@ -207,6 +209,7 @@ func TestInt8Codec(t *testing.T) {
|
|||||||
{1, new(int64), isExpectedEq(int64(1))},
|
{1, new(int64), isExpectedEq(int64(1))},
|
||||||
{math.MaxInt64, new(int64), isExpectedEq(int64(math.MaxInt64))},
|
{math.MaxInt64, new(int64), isExpectedEq(int64(math.MaxInt64))},
|
||||||
{1, new(pgtype.Int8), isExpectedEq(pgtype.Int8{Int64: 1, Valid: true})},
|
{1, new(pgtype.Int8), isExpectedEq(pgtype.Int8{Int64: 1, Valid: true})},
|
||||||
|
{"1", new(string), isExpectedEq("1")},
|
||||||
{pgtype.Int8{}, new(pgtype.Int8), isExpectedEq(pgtype.Int8{})},
|
{pgtype.Int8{}, new(pgtype.Int8), isExpectedEq(pgtype.Int8{})},
|
||||||
{nil, new(*int64), isExpectedEq((*int64)(nil))},
|
{nil, new(*int64), isExpectedEq((*int64)(nil))},
|
||||||
})
|
})
|
||||||
|
@ -44,6 +44,7 @@ func TestInt<%= pg_byte_size %>Codec(t *testing.T) {
|
|||||||
{1, new(int<%= pg_bit_size %>), isExpectedEq(int<%= pg_bit_size %>(1))},
|
{1, new(int<%= pg_bit_size %>), isExpectedEq(int<%= pg_bit_size %>(1))},
|
||||||
{math.MaxInt<%= pg_bit_size %>, new(int<%= pg_bit_size %>), isExpectedEq(int<%= pg_bit_size %>(math.MaxInt<%= pg_bit_size %>))},
|
{math.MaxInt<%= pg_bit_size %>, new(int<%= pg_bit_size %>), isExpectedEq(int<%= pg_bit_size %>(math.MaxInt<%= pg_bit_size %>))},
|
||||||
{1, new(pgtype.Int<%= pg_byte_size %>), isExpectedEq(pgtype.Int<%= pg_byte_size %>{Int<%= pg_bit_size %>: 1, Valid: true})},
|
{1, new(pgtype.Int<%= pg_byte_size %>), isExpectedEq(pgtype.Int<%= pg_byte_size %>{Int<%= pg_bit_size %>: 1, Valid: true})},
|
||||||
|
{"1", new(string), isExpectedEq("1")},
|
||||||
{pgtype.Int<%= pg_byte_size %>{}, new(pgtype.Int<%= pg_byte_size %>), isExpectedEq(pgtype.Int<%= pg_byte_size %>{})},
|
{pgtype.Int<%= pg_byte_size %>{}, new(pgtype.Int<%= pg_byte_size %>), isExpectedEq(pgtype.Int<%= pg_byte_size %>{})},
|
||||||
{nil, new(*int<%= pg_bit_size %>), isExpectedEq((*int<%= pg_bit_size %>)(nil))},
|
{nil, new(*int<%= pg_bit_size %>), isExpectedEq((*int<%= pg_bit_size %>)(nil))},
|
||||||
})
|
})
|
||||||
|
@ -237,6 +237,11 @@ func (n Numeric) MarshalJSON() ([]byte, error) {
|
|||||||
return []byte(`"NaN"`), nil
|
return []byte(`"NaN"`), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return n.numberTextBytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// numberString returns a string of the number. undefined if NaN, infinite, or NULL
|
||||||
|
func (n Numeric) numberTextBytes() []byte {
|
||||||
intStr := n.Int.String()
|
intStr := n.Int.String()
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
exp := int(n.Exp)
|
exp := int(n.Exp)
|
||||||
@ -263,7 +268,7 @@ func (n Numeric) MarshalJSON() ([]byte, error) {
|
|||||||
buf.WriteString(intStr)
|
buf.WriteString(intStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf.Bytes(), nil
|
return buf.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
type NumericCodec struct{}
|
type NumericCodec struct{}
|
||||||
@ -520,19 +525,7 @@ func encodeNumericText(n Numeric, buf []byte) (newBuf []byte, err error) {
|
|||||||
return buf, nil
|
return buf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
digits := n.Int.String()
|
buf = append(buf, n.numberTextBytes()...)
|
||||||
if n.Exp >= 0 {
|
|
||||||
buf = append(buf, digits...)
|
|
||||||
if n.Exp > 0 {
|
|
||||||
for i := int32(0); i < n.Exp; i++ {
|
|
||||||
buf = append(buf, '0')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
buf = append(buf, digits...)
|
|
||||||
buf = append(buf, 'e')
|
|
||||||
buf = append(buf, strconv.FormatInt(int64(n.Exp), 10)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf, nil
|
return buf, nil
|
||||||
}
|
}
|
||||||
@ -548,6 +541,8 @@ func (NumericCodec) PlanScan(m *Map, oid uint32, format int16, target interface{
|
|||||||
return scanPlanBinaryNumericToFloat64Scanner{}
|
return scanPlanBinaryNumericToFloat64Scanner{}
|
||||||
case Int64Scanner:
|
case Int64Scanner:
|
||||||
return scanPlanBinaryNumericToInt64Scanner{}
|
return scanPlanBinaryNumericToInt64Scanner{}
|
||||||
|
case TextScanner:
|
||||||
|
return scanPlanBinaryNumericToTextScanner{}
|
||||||
}
|
}
|
||||||
case TextFormatCode:
|
case TextFormatCode:
|
||||||
switch target.(type) {
|
switch target.(type) {
|
||||||
@ -721,6 +716,30 @@ func (scanPlanBinaryNumericToInt64Scanner) Scan(src []byte, dst interface{}) err
|
|||||||
return scanner.ScanInt64(Int8{Int64: bigInt.Int64(), Valid: true})
|
return scanner.ScanInt64(Int8{Int64: bigInt.Int64(), Valid: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type scanPlanBinaryNumericToTextScanner struct{}
|
||||||
|
|
||||||
|
func (scanPlanBinaryNumericToTextScanner) Scan(src []byte, dst interface{}) error {
|
||||||
|
scanner := (dst).(TextScanner)
|
||||||
|
|
||||||
|
if src == nil {
|
||||||
|
return scanner.ScanText(Text{})
|
||||||
|
}
|
||||||
|
|
||||||
|
var n Numeric
|
||||||
|
|
||||||
|
err := scanPlanBinaryNumericToNumericScanner{}.Scan(src, &n)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sbuf, err := encodeNumericText(n, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return scanner.ScanText(Text{String: string(sbuf), Valid: true})
|
||||||
|
}
|
||||||
|
|
||||||
type scanPlanTextAnyToNumericScanner struct{}
|
type scanPlanTextAnyToNumericScanner struct{}
|
||||||
|
|
||||||
func (scanPlanTextAnyToNumericScanner) Scan(src []byte, dst interface{}) error {
|
func (scanPlanTextAnyToNumericScanner) Scan(src []byte, dst interface{}) error {
|
||||||
|
@ -110,6 +110,7 @@ func TestNumericCodec(t *testing.T) {
|
|||||||
{int64(math.MinInt64 + 1), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, strconv.FormatInt(math.MinInt64+1, 10)))},
|
{int64(math.MinInt64 + 1), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, strconv.FormatInt(math.MinInt64+1, 10)))},
|
||||||
{int64(math.MaxInt64), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, strconv.FormatInt(math.MaxInt64, 10)))},
|
{int64(math.MaxInt64), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, strconv.FormatInt(math.MaxInt64, 10)))},
|
||||||
{int64(math.MaxInt64 - 1), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, strconv.FormatInt(math.MaxInt64-1, 10)))},
|
{int64(math.MaxInt64 - 1), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, strconv.FormatInt(math.MaxInt64-1, 10)))},
|
||||||
|
{"1.23", new(string), isExpectedEq("1.23")},
|
||||||
{pgtype.Numeric{}, new(pgtype.Numeric), isExpectedEq(pgtype.Numeric{})},
|
{pgtype.Numeric{}, new(pgtype.Numeric), isExpectedEq(pgtype.Numeric{})},
|
||||||
{nil, new(pgtype.Numeric), isExpectedEq(pgtype.Numeric{})},
|
{nil, new(pgtype.Numeric), isExpectedEq(pgtype.Numeric{})},
|
||||||
})
|
})
|
||||||
|
@ -1022,6 +1022,7 @@ func TestScanIntoByteSlice(t *testing.T) {
|
|||||||
output []byte
|
output []byte
|
||||||
}{
|
}{
|
||||||
{"int - text", "select 42", pgx.TextFormatCode, []byte("42")},
|
{"int - text", "select 42", pgx.TextFormatCode, []byte("42")},
|
||||||
|
{"int - binary", "select 42", pgx.BinaryFormatCode, []byte("42")},
|
||||||
{"text - text", "select 'hi'", pgx.TextFormatCode, []byte("hi")},
|
{"text - text", "select 'hi'", pgx.TextFormatCode, []byte("hi")},
|
||||||
{"text - binary", "select 'hi'", pgx.BinaryFormatCode, []byte("hi")},
|
{"text - binary", "select 'hi'", pgx.BinaryFormatCode, []byte("hi")},
|
||||||
{"json - text", "select '{}'::json", pgx.TextFormatCode, []byte("{}")},
|
{"json - text", "select '{}'::json", pgx.TextFormatCode, []byte("{}")},
|
||||||
@ -1036,19 +1037,4 @@ func TestScanIntoByteSlice(t *testing.T) {
|
|||||||
require.Equal(t, tt.output, buf)
|
require.Equal(t, tt.output, buf)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Failure cases
|
|
||||||
for _, tt := range []struct {
|
|
||||||
name string
|
|
||||||
sql string
|
|
||||||
err string
|
|
||||||
}{
|
|
||||||
{"int binary", "select 42::int4", "can't scan into dest[0]: cannot scan OID 23 in binary format into *[]uint8"},
|
|
||||||
} {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
var buf []byte
|
|
||||||
err := conn.QueryRow(context.Background(), tt.sql, pgx.QueryResultFormats{pgx.BinaryFormatCode}).Scan(&buf)
|
|
||||||
require.EqualError(t, err, tt.err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user