package pgtype import ( "database/sql/driver" "encoding/binary" "encoding/json" "fmt" "math" "strconv" "github.com/jackc/pgx/v5/internal/pgio" ) type Float4 struct { Float32 float32 Valid bool } // ScanFloat64 implements the Float64Scanner interface. func (f *Float4) ScanFloat64(n Float8) error { *f = Float4{Float32: float32(n.Float64), Valid: n.Valid} return nil } func (f Float4) Float64Value() (Float8, error) { return Float8{Float64: float64(f.Float32), Valid: f.Valid}, nil } func (f *Float4) ScanInt64(n Int8) error { *f = Float4{Float32: float32(n.Int64), Valid: n.Valid} return nil } func (f Float4) Int64Value() (Int8, error) { return Int8{Int64: int64(f.Float32), Valid: f.Valid}, nil } // Scan implements the database/sql Scanner interface. func (f *Float4) Scan(src any) error { if src == nil { *f = Float4{} return nil } switch src := src.(type) { case float64: *f = Float4{Float32: float32(src), Valid: true} return nil case string: n, err := strconv.ParseFloat(string(src), 32) if err != nil { return err } *f = Float4{Float32: float32(n), Valid: true} return nil } return fmt.Errorf("cannot scan %T", src) } // Value implements the database/sql/driver Valuer interface. func (f Float4) Value() (driver.Value, error) { if !f.Valid { return nil, nil } return float64(f.Float32), nil } func (f Float4) MarshalJSON() ([]byte, error) { if !f.Valid { return []byte("null"), nil } return json.Marshal(f.Float32) } func (f *Float4) UnmarshalJSON(b []byte) error { var n *float32 err := json.Unmarshal(b, &n) if err != nil { return err } if n == nil { *f = Float4{} } else { *f = Float4{Float32: *n, Valid: true} } return nil } type Float4Codec struct{} func (Float4Codec) FormatSupported(format int16) bool { return format == TextFormatCode || format == BinaryFormatCode } func (Float4Codec) PreferredFormat() int16 { return BinaryFormatCode } func (Float4Codec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan { switch format { case BinaryFormatCode: switch value.(type) { case float32: return encodePlanFloat4CodecBinaryFloat32{} case Float64Valuer: return encodePlanFloat4CodecBinaryFloat64Valuer{} case Int64Valuer: return encodePlanFloat4CodecBinaryInt64Valuer{} } case TextFormatCode: switch value.(type) { case float32: return encodePlanTextFloat32{} case Float64Valuer: return encodePlanTextFloat64Valuer{} case Int64Valuer: return encodePlanTextInt64Valuer{} } } return nil } type encodePlanFloat4CodecBinaryFloat32 struct{} func (encodePlanFloat4CodecBinaryFloat32) Encode(value any, buf []byte) (newBuf []byte, err error) { n := value.(float32) return pgio.AppendUint32(buf, math.Float32bits(n)), nil } type encodePlanTextFloat32 struct{} func (encodePlanTextFloat32) Encode(value any, buf []byte) (newBuf []byte, err error) { n := value.(float32) return append(buf, strconv.FormatFloat(float64(n), 'f', -1, 32)...), nil } type encodePlanFloat4CodecBinaryFloat64Valuer struct{} func (encodePlanFloat4CodecBinaryFloat64Valuer) 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.AppendUint32(buf, math.Float32bits(float32(n.Float64))), nil } type encodePlanFloat4CodecBinaryInt64Valuer struct{} func (encodePlanFloat4CodecBinaryInt64Valuer) 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 := float32(n.Int64) return pgio.AppendUint32(buf, math.Float32bits(f)), nil } func (Float4Codec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan { switch format { case BinaryFormatCode: switch target.(type) { case *float32: return scanPlanBinaryFloat4ToFloat32{} case Float64Scanner: return scanPlanBinaryFloat4ToFloat64Scanner{} case Int64Scanner: return scanPlanBinaryFloat4ToInt64Scanner{} case TextScanner: return scanPlanBinaryFloat4ToTextScanner{} } case TextFormatCode: switch target.(type) { case *float32: return scanPlanTextAnyToFloat32{} case Float64Scanner: return scanPlanTextAnyToFloat64Scanner{} case Int64Scanner: return scanPlanTextAnyToInt64Scanner{} } } return nil } type scanPlanBinaryFloat4ToFloat32 struct{} func (scanPlanBinaryFloat4ToFloat32) Scan(src []byte, dst any) error { if src == nil { return fmt.Errorf("cannot scan NULL into %T", dst) } if len(src) != 4 { return fmt.Errorf("invalid length for float4: %v", len(src)) } n := int32(binary.BigEndian.Uint32(src)) f := (dst).(*float32) *f = math.Float32frombits(uint32(n)) return nil } type scanPlanBinaryFloat4ToFloat64Scanner struct{} func (scanPlanBinaryFloat4ToFloat64Scanner) Scan(src []byte, dst any) error { s := (dst).(Float64Scanner) if src == nil { return s.ScanFloat64(Float8{}) } if len(src) != 4 { return fmt.Errorf("invalid length for float4: %v", len(src)) } n := int32(binary.BigEndian.Uint32(src)) return s.ScanFloat64(Float8{Float64: float64(math.Float32frombits(uint32(n))), Valid: true}) } type scanPlanBinaryFloat4ToInt64Scanner struct{} func (scanPlanBinaryFloat4ToInt64Scanner) Scan(src []byte, dst any) error { s := (dst).(Int64Scanner) if src == nil { return s.ScanInt64(Int8{}) } 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)) i64 := int64(f32) if f32 != float32(i64) { return fmt.Errorf("cannot losslessly convert %v to int64", f32) } return s.ScanInt64(Int8{Int64: i64, Valid: true}) } type scanPlanBinaryFloat4ToTextScanner struct{} func (scanPlanBinaryFloat4ToTextScanner) Scan(src []byte, dst any) 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{} func (scanPlanTextAnyToFloat32) Scan(src []byte, dst any) error { if src == nil { return fmt.Errorf("cannot scan NULL into %T", dst) } n, err := strconv.ParseFloat(string(src), 32) if err != nil { return err } f := (dst).(*float32) *f = float32(n) return nil } func (c Float4Codec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) { if src == nil { return nil, nil } var n float32 err := codecScan(c, m, oid, format, src, &n) if err != nil { return nil, err } return float64(n), nil } func (c Float4Codec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) { if src == nil { return nil, nil } var n float32 err := codecScan(c, m, oid, format, src, &n) if err != nil { return nil, err } return n, nil }