github.com/jackc/pgx/v5@v5.5.5/pgtype/float4.go (about) 1 package pgtype 2 3 import ( 4 "database/sql/driver" 5 "encoding/binary" 6 "encoding/json" 7 "fmt" 8 "math" 9 "strconv" 10 11 "github.com/jackc/pgx/v5/internal/pgio" 12 ) 13 14 type Float4 struct { 15 Float32 float32 16 Valid bool 17 } 18 19 // ScanFloat64 implements the Float64Scanner interface. 20 func (f *Float4) ScanFloat64(n Float8) error { 21 *f = Float4{Float32: float32(n.Float64), Valid: n.Valid} 22 return nil 23 } 24 25 func (f Float4) Float64Value() (Float8, error) { 26 return Float8{Float64: float64(f.Float32), Valid: f.Valid}, nil 27 } 28 29 func (f *Float4) ScanInt64(n Int8) error { 30 *f = Float4{Float32: float32(n.Int64), Valid: n.Valid} 31 return nil 32 } 33 34 func (f Float4) Int64Value() (Int8, error) { 35 return Int8{Int64: int64(f.Float32), Valid: f.Valid}, nil 36 } 37 38 // Scan implements the database/sql Scanner interface. 39 func (f *Float4) Scan(src any) error { 40 if src == nil { 41 *f = Float4{} 42 return nil 43 } 44 45 switch src := src.(type) { 46 case float64: 47 *f = Float4{Float32: float32(src), Valid: true} 48 return nil 49 case string: 50 n, err := strconv.ParseFloat(string(src), 32) 51 if err != nil { 52 return err 53 } 54 *f = Float4{Float32: float32(n), Valid: true} 55 return nil 56 } 57 58 return fmt.Errorf("cannot scan %T", src) 59 } 60 61 // Value implements the database/sql/driver Valuer interface. 62 func (f Float4) Value() (driver.Value, error) { 63 if !f.Valid { 64 return nil, nil 65 } 66 return float64(f.Float32), nil 67 } 68 69 func (f Float4) MarshalJSON() ([]byte, error) { 70 if !f.Valid { 71 return []byte("null"), nil 72 } 73 return json.Marshal(f.Float32) 74 } 75 76 func (f *Float4) UnmarshalJSON(b []byte) error { 77 var n *float32 78 err := json.Unmarshal(b, &n) 79 if err != nil { 80 return err 81 } 82 83 if n == nil { 84 *f = Float4{} 85 } else { 86 *f = Float4{Float32: *n, Valid: true} 87 } 88 89 return nil 90 } 91 92 type Float4Codec struct{} 93 94 func (Float4Codec) FormatSupported(format int16) bool { 95 return format == TextFormatCode || format == BinaryFormatCode 96 } 97 98 func (Float4Codec) PreferredFormat() int16 { 99 return BinaryFormatCode 100 } 101 102 func (Float4Codec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan { 103 switch format { 104 case BinaryFormatCode: 105 switch value.(type) { 106 case float32: 107 return encodePlanFloat4CodecBinaryFloat32{} 108 case Float64Valuer: 109 return encodePlanFloat4CodecBinaryFloat64Valuer{} 110 case Int64Valuer: 111 return encodePlanFloat4CodecBinaryInt64Valuer{} 112 } 113 case TextFormatCode: 114 switch value.(type) { 115 case float32: 116 return encodePlanTextFloat32{} 117 case Float64Valuer: 118 return encodePlanTextFloat64Valuer{} 119 case Int64Valuer: 120 return encodePlanTextInt64Valuer{} 121 } 122 } 123 124 return nil 125 } 126 127 type encodePlanFloat4CodecBinaryFloat32 struct{} 128 129 func (encodePlanFloat4CodecBinaryFloat32) Encode(value any, buf []byte) (newBuf []byte, err error) { 130 n := value.(float32) 131 return pgio.AppendUint32(buf, math.Float32bits(n)), nil 132 } 133 134 type encodePlanTextFloat32 struct{} 135 136 func (encodePlanTextFloat32) Encode(value any, buf []byte) (newBuf []byte, err error) { 137 n := value.(float32) 138 return append(buf, strconv.FormatFloat(float64(n), 'f', -1, 32)...), nil 139 } 140 141 type encodePlanFloat4CodecBinaryFloat64Valuer struct{} 142 143 func (encodePlanFloat4CodecBinaryFloat64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) { 144 n, err := value.(Float64Valuer).Float64Value() 145 if err != nil { 146 return nil, err 147 } 148 149 if !n.Valid { 150 return nil, nil 151 } 152 153 return pgio.AppendUint32(buf, math.Float32bits(float32(n.Float64))), nil 154 } 155 156 type encodePlanFloat4CodecBinaryInt64Valuer struct{} 157 158 func (encodePlanFloat4CodecBinaryInt64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) { 159 n, err := value.(Int64Valuer).Int64Value() 160 if err != nil { 161 return nil, err 162 } 163 164 if !n.Valid { 165 return nil, nil 166 } 167 168 f := float32(n.Int64) 169 return pgio.AppendUint32(buf, math.Float32bits(f)), nil 170 } 171 172 func (Float4Codec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan { 173 174 switch format { 175 case BinaryFormatCode: 176 switch target.(type) { 177 case *float32: 178 return scanPlanBinaryFloat4ToFloat32{} 179 case Float64Scanner: 180 return scanPlanBinaryFloat4ToFloat64Scanner{} 181 case Int64Scanner: 182 return scanPlanBinaryFloat4ToInt64Scanner{} 183 case TextScanner: 184 return scanPlanBinaryFloat4ToTextScanner{} 185 } 186 case TextFormatCode: 187 switch target.(type) { 188 case *float32: 189 return scanPlanTextAnyToFloat32{} 190 case Float64Scanner: 191 return scanPlanTextAnyToFloat64Scanner{} 192 case Int64Scanner: 193 return scanPlanTextAnyToInt64Scanner{} 194 } 195 } 196 197 return nil 198 } 199 200 type scanPlanBinaryFloat4ToFloat32 struct{} 201 202 func (scanPlanBinaryFloat4ToFloat32) Scan(src []byte, dst any) error { 203 if src == nil { 204 return fmt.Errorf("cannot scan NULL into %T", dst) 205 } 206 207 if len(src) != 4 { 208 return fmt.Errorf("invalid length for float4: %v", len(src)) 209 } 210 211 n := int32(binary.BigEndian.Uint32(src)) 212 f := (dst).(*float32) 213 *f = math.Float32frombits(uint32(n)) 214 215 return nil 216 } 217 218 type scanPlanBinaryFloat4ToFloat64Scanner struct{} 219 220 func (scanPlanBinaryFloat4ToFloat64Scanner) Scan(src []byte, dst any) error { 221 s := (dst).(Float64Scanner) 222 223 if src == nil { 224 return s.ScanFloat64(Float8{}) 225 } 226 227 if len(src) != 4 { 228 return fmt.Errorf("invalid length for float4: %v", len(src)) 229 } 230 231 n := int32(binary.BigEndian.Uint32(src)) 232 return s.ScanFloat64(Float8{Float64: float64(math.Float32frombits(uint32(n))), Valid: true}) 233 } 234 235 type scanPlanBinaryFloat4ToInt64Scanner struct{} 236 237 func (scanPlanBinaryFloat4ToInt64Scanner) Scan(src []byte, dst any) error { 238 s := (dst).(Int64Scanner) 239 240 if src == nil { 241 return s.ScanInt64(Int8{}) 242 } 243 244 if len(src) != 4 { 245 return fmt.Errorf("invalid length for float4: %v", len(src)) 246 } 247 248 ui32 := int32(binary.BigEndian.Uint32(src)) 249 f32 := math.Float32frombits(uint32(ui32)) 250 i64 := int64(f32) 251 if f32 != float32(i64) { 252 return fmt.Errorf("cannot losslessly convert %v to int64", f32) 253 } 254 255 return s.ScanInt64(Int8{Int64: i64, Valid: true}) 256 } 257 258 type scanPlanBinaryFloat4ToTextScanner struct{} 259 260 func (scanPlanBinaryFloat4ToTextScanner) Scan(src []byte, dst any) error { 261 s := (dst).(TextScanner) 262 263 if src == nil { 264 return s.ScanText(Text{}) 265 } 266 267 if len(src) != 4 { 268 return fmt.Errorf("invalid length for float4: %v", len(src)) 269 } 270 271 ui32 := int32(binary.BigEndian.Uint32(src)) 272 f32 := math.Float32frombits(uint32(ui32)) 273 274 return s.ScanText(Text{String: strconv.FormatFloat(float64(f32), 'f', -1, 32), Valid: true}) 275 } 276 277 type scanPlanTextAnyToFloat32 struct{} 278 279 func (scanPlanTextAnyToFloat32) Scan(src []byte, dst any) error { 280 if src == nil { 281 return fmt.Errorf("cannot scan NULL into %T", dst) 282 } 283 284 n, err := strconv.ParseFloat(string(src), 32) 285 if err != nil { 286 return err 287 } 288 289 f := (dst).(*float32) 290 *f = float32(n) 291 292 return nil 293 } 294 295 func (c Float4Codec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) { 296 if src == nil { 297 return nil, nil 298 } 299 300 var n float32 301 err := codecScan(c, m, oid, format, src, &n) 302 if err != nil { 303 return nil, err 304 } 305 return float64(n), nil 306 } 307 308 func (c Float4Codec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) { 309 if src == nil { 310 return nil, nil 311 } 312 313 var n float32 314 err := codecScan(c, m, oid, format, src, &n) 315 if err != nil { 316 return nil, err 317 } 318 return n, nil 319 }