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
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
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
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 }
366
View as plain text