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