mirror of https://github.com/jackc/pgx.git
366 lines
7.8 KiB
Go
366 lines
7.8 KiB
Go
package pgtype
|
|
|
|
import (
|
|
"database/sql/driver"
|
|
"encoding/binary"
|
|
"encoding/json"
|
|
"fmt"
|
|
"math"
|
|
"strconv"
|
|
|
|
"github.com/jackc/pgx/v5/internal/pgio"
|
|
)
|
|
|
|
type Float64Scanner interface {
|
|
ScanFloat64(Float8) error
|
|
}
|
|
|
|
type Float64Valuer interface {
|
|
Float64Value() (Float8, error)
|
|
}
|
|
|
|
type Float8 struct {
|
|
Float64 float64
|
|
Valid bool
|
|
}
|
|
|
|
// ScanFloat64 implements the Float64Scanner interface.
|
|
func (f *Float8) ScanFloat64(n Float8) error {
|
|
*f = n
|
|
return nil
|
|
}
|
|
|
|
func (f Float8) Float64Value() (Float8, error) {
|
|
return f, nil
|
|
}
|
|
|
|
func (f *Float8) ScanInt64(n Int8) error {
|
|
*f = Float8{Float64: float64(n.Int64), Valid: n.Valid}
|
|
return nil
|
|
}
|
|
|
|
func (f Float8) Int64Value() (Int8, error) {
|
|
return Int8{Int64: int64(f.Float64), Valid: f.Valid}, nil
|
|
}
|
|
|
|
// Scan implements the database/sql Scanner interface.
|
|
func (f *Float8) Scan(src any) error {
|
|
if src == nil {
|
|
*f = Float8{}
|
|
return nil
|
|
}
|
|
|
|
switch src := src.(type) {
|
|
case float64:
|
|
*f = Float8{Float64: src, Valid: true}
|
|
return nil
|
|
case string:
|
|
n, err := strconv.ParseFloat(string(src), 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*f = Float8{Float64: n, Valid: true}
|
|
return nil
|
|
}
|
|
|
|
return fmt.Errorf("cannot scan %T", src)
|
|
}
|
|
|
|
// Value implements the database/sql/driver Valuer interface.
|
|
func (f Float8) Value() (driver.Value, error) {
|
|
if !f.Valid {
|
|
return nil, nil
|
|
}
|
|
return f.Float64, nil
|
|
}
|
|
|
|
func (f Float8) MarshalJSON() ([]byte, error) {
|
|
if !f.Valid {
|
|
return []byte("null"), nil
|
|
}
|
|
return json.Marshal(f.Float64)
|
|
}
|
|
|
|
func (f *Float8) UnmarshalJSON(b []byte) error {
|
|
var n *float64
|
|
err := json.Unmarshal(b, &n)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if n == nil {
|
|
*f = Float8{}
|
|
} else {
|
|
*f = Float8{Float64: *n, Valid: true}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type Float8Codec struct{}
|
|
|
|
func (Float8Codec) FormatSupported(format int16) bool {
|
|
return format == TextFormatCode || format == BinaryFormatCode
|
|
}
|
|
|
|
func (Float8Codec) PreferredFormat() int16 {
|
|
return BinaryFormatCode
|
|
}
|
|
|
|
func (Float8Codec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {
|
|
switch format {
|
|
case BinaryFormatCode:
|
|
switch value.(type) {
|
|
case float64:
|
|
return encodePlanFloat8CodecBinaryFloat64{}
|
|
case Float64Valuer:
|
|
return encodePlanFloat8CodecBinaryFloat64Valuer{}
|
|
case Int64Valuer:
|
|
return encodePlanFloat8CodecBinaryInt64Valuer{}
|
|
}
|
|
case TextFormatCode:
|
|
switch value.(type) {
|
|
case float64:
|
|
return encodePlanTextFloat64{}
|
|
case Float64Valuer:
|
|
return encodePlanTextFloat64Valuer{}
|
|
case Int64Valuer:
|
|
return encodePlanTextInt64Valuer{}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type encodePlanFloat8CodecBinaryFloat64 struct{}
|
|
|
|
func (encodePlanFloat8CodecBinaryFloat64) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
n := value.(float64)
|
|
return pgio.AppendUint64(buf, math.Float64bits(n)), nil
|
|
}
|
|
|
|
type encodePlanTextFloat64 struct{}
|
|
|
|
func (encodePlanTextFloat64) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
n := value.(float64)
|
|
return append(buf, strconv.FormatFloat(n, 'f', -1, 64)...), nil
|
|
}
|
|
|
|
type encodePlanFloat8CodecBinaryFloat64Valuer struct{}
|
|
|
|
func (encodePlanFloat8CodecBinaryFloat64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
n, err := value.(Float64Valuer).Float64Value()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !n.Valid {
|
|
return nil, nil
|
|
}
|
|
|
|
return pgio.AppendUint64(buf, math.Float64bits(n.Float64)), nil
|
|
}
|
|
|
|
type encodePlanTextFloat64Valuer struct{}
|
|
|
|
func (encodePlanTextFloat64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
n, err := value.(Float64Valuer).Float64Value()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !n.Valid {
|
|
return nil, nil
|
|
}
|
|
|
|
return append(buf, strconv.FormatFloat(n.Float64, 'f', -1, 64)...), nil
|
|
}
|
|
|
|
type encodePlanFloat8CodecBinaryInt64Valuer struct{}
|
|
|
|
func (encodePlanFloat8CodecBinaryInt64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
n, err := value.(Int64Valuer).Int64Value()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !n.Valid {
|
|
return nil, nil
|
|
}
|
|
|
|
f := float64(n.Int64)
|
|
return pgio.AppendUint64(buf, math.Float64bits(f)), nil
|
|
}
|
|
|
|
type encodePlanTextInt64Valuer struct{}
|
|
|
|
func (encodePlanTextInt64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
|
n, err := value.(Int64Valuer).Int64Value()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !n.Valid {
|
|
return nil, nil
|
|
}
|
|
|
|
return append(buf, strconv.FormatInt(n.Int64, 10)...), nil
|
|
}
|
|
|
|
func (Float8Codec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
|
|
|
|
switch format {
|
|
case BinaryFormatCode:
|
|
switch target.(type) {
|
|
case *float64:
|
|
return scanPlanBinaryFloat8ToFloat64{}
|
|
case Float64Scanner:
|
|
return scanPlanBinaryFloat8ToFloat64Scanner{}
|
|
case Int64Scanner:
|
|
return scanPlanBinaryFloat8ToInt64Scanner{}
|
|
case TextScanner:
|
|
return scanPlanBinaryFloat8ToTextScanner{}
|
|
}
|
|
case TextFormatCode:
|
|
switch target.(type) {
|
|
case *float64:
|
|
return scanPlanTextAnyToFloat64{}
|
|
case Float64Scanner:
|
|
return scanPlanTextAnyToFloat64Scanner{}
|
|
case Int64Scanner:
|
|
return scanPlanTextAnyToInt64Scanner{}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type scanPlanBinaryFloat8ToFloat64 struct{}
|
|
|
|
func (scanPlanBinaryFloat8ToFloat64) Scan(src []byte, dst any) error {
|
|
if src == nil {
|
|
return fmt.Errorf("cannot scan NULL into %T", dst)
|
|
}
|
|
|
|
if len(src) != 8 {
|
|
return fmt.Errorf("invalid length for float8: %v", len(src))
|
|
}
|
|
|
|
n := int64(binary.BigEndian.Uint64(src))
|
|
f := (dst).(*float64)
|
|
*f = math.Float64frombits(uint64(n))
|
|
|
|
return nil
|
|
}
|
|
|
|
type scanPlanBinaryFloat8ToFloat64Scanner struct{}
|
|
|
|
func (scanPlanBinaryFloat8ToFloat64Scanner) Scan(src []byte, dst any) error {
|
|
s := (dst).(Float64Scanner)
|
|
|
|
if src == nil {
|
|
return s.ScanFloat64(Float8{})
|
|
}
|
|
|
|
if len(src) != 8 {
|
|
return fmt.Errorf("invalid length for float8: %v", len(src))
|
|
}
|
|
|
|
n := int64(binary.BigEndian.Uint64(src))
|
|
return s.ScanFloat64(Float8{Float64: math.Float64frombits(uint64(n)), Valid: true})
|
|
}
|
|
|
|
type scanPlanBinaryFloat8ToInt64Scanner struct{}
|
|
|
|
func (scanPlanBinaryFloat8ToInt64Scanner) Scan(src []byte, dst any) error {
|
|
s := (dst).(Int64Scanner)
|
|
|
|
if src == nil {
|
|
return s.ScanInt64(Int8{})
|
|
}
|
|
|
|
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))
|
|
i64 := int64(f64)
|
|
if f64 != float64(i64) {
|
|
return fmt.Errorf("cannot losslessly convert %v to int64", f64)
|
|
}
|
|
|
|
return s.ScanInt64(Int8{Int64: i64, Valid: true})
|
|
}
|
|
|
|
type scanPlanBinaryFloat8ToTextScanner struct{}
|
|
|
|
func (scanPlanBinaryFloat8ToTextScanner) Scan(src []byte, dst any) 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{}
|
|
|
|
func (scanPlanTextAnyToFloat64) Scan(src []byte, dst any) error {
|
|
if src == nil {
|
|
return fmt.Errorf("cannot scan NULL into %T", dst)
|
|
}
|
|
|
|
n, err := strconv.ParseFloat(string(src), 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
f := (dst).(*float64)
|
|
*f = n
|
|
|
|
return nil
|
|
}
|
|
|
|
type scanPlanTextAnyToFloat64Scanner struct{}
|
|
|
|
func (scanPlanTextAnyToFloat64Scanner) Scan(src []byte, dst any) error {
|
|
s := (dst).(Float64Scanner)
|
|
|
|
if src == nil {
|
|
return s.ScanFloat64(Float8{})
|
|
}
|
|
|
|
n, err := strconv.ParseFloat(string(src), 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return s.ScanFloat64(Float8{Float64: n, Valid: true})
|
|
}
|
|
|
|
func (c Float8Codec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {
|
|
return c.DecodeValue(m, oid, format, src)
|
|
}
|
|
|
|
func (c Float8Codec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {
|
|
if src == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
var n float64
|
|
err := codecScan(c, m, oid, format, src, &n)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return n, nil
|
|
}
|