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