1 package pgtype
2
3 import (
4 "bytes"
5 "database/sql/driver"
6 "encoding/json"
7 "fmt"
8 "strconv"
9 "strings"
10 )
11
12 type BoolScanner interface {
13 ScanBool(v Bool) error
14 }
15
16 type BoolValuer interface {
17 BoolValue() (Bool, error)
18 }
19
20 type Bool struct {
21 Bool bool
22 Valid bool
23 }
24
25 func (b *Bool) ScanBool(v Bool) error {
26 *b = v
27 return nil
28 }
29
30 func (b Bool) BoolValue() (Bool, error) {
31 return b, nil
32 }
33
34
35 func (dst *Bool) Scan(src any) error {
36 if src == nil {
37 *dst = Bool{}
38 return nil
39 }
40
41 switch src := src.(type) {
42 case bool:
43 *dst = Bool{Bool: src, Valid: true}
44 return nil
45 case string:
46 b, err := strconv.ParseBool(src)
47 if err != nil {
48 return err
49 }
50 *dst = Bool{Bool: b, Valid: true}
51 return nil
52 case []byte:
53 b, err := strconv.ParseBool(string(src))
54 if err != nil {
55 return err
56 }
57 *dst = Bool{Bool: b, Valid: true}
58 return nil
59 }
60
61 return fmt.Errorf("cannot scan %T", src)
62 }
63
64
65 func (src Bool) Value() (driver.Value, error) {
66 if !src.Valid {
67 return nil, nil
68 }
69
70 return src.Bool, nil
71 }
72
73 func (src Bool) MarshalJSON() ([]byte, error) {
74 if !src.Valid {
75 return []byte("null"), nil
76 }
77
78 if src.Bool {
79 return []byte("true"), nil
80 } else {
81 return []byte("false"), nil
82 }
83 }
84
85 func (dst *Bool) UnmarshalJSON(b []byte) error {
86 var v *bool
87 err := json.Unmarshal(b, &v)
88 if err != nil {
89 return err
90 }
91
92 if v == nil {
93 *dst = Bool{}
94 } else {
95 *dst = Bool{Bool: *v, Valid: true}
96 }
97
98 return nil
99 }
100
101 type BoolCodec struct{}
102
103 func (BoolCodec) FormatSupported(format int16) bool {
104 return format == TextFormatCode || format == BinaryFormatCode
105 }
106
107 func (BoolCodec) PreferredFormat() int16 {
108 return BinaryFormatCode
109 }
110
111 func (BoolCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {
112 switch format {
113 case BinaryFormatCode:
114 switch value.(type) {
115 case bool:
116 return encodePlanBoolCodecBinaryBool{}
117 case BoolValuer:
118 return encodePlanBoolCodecBinaryBoolValuer{}
119 }
120 case TextFormatCode:
121 switch value.(type) {
122 case bool:
123 return encodePlanBoolCodecTextBool{}
124 case BoolValuer:
125 return encodePlanBoolCodecTextBoolValuer{}
126 }
127 }
128
129 return nil
130 }
131
132 type encodePlanBoolCodecBinaryBool struct{}
133
134 func (encodePlanBoolCodecBinaryBool) Encode(value any, buf []byte) (newBuf []byte, err error) {
135 v := value.(bool)
136
137 if v {
138 buf = append(buf, 1)
139 } else {
140 buf = append(buf, 0)
141 }
142
143 return buf, nil
144 }
145
146 type encodePlanBoolCodecTextBoolValuer struct{}
147
148 func (encodePlanBoolCodecTextBoolValuer) Encode(value any, buf []byte) (newBuf []byte, err error) {
149 b, err := value.(BoolValuer).BoolValue()
150 if err != nil {
151 return nil, err
152 }
153
154 if !b.Valid {
155 return nil, nil
156 }
157
158 if b.Bool {
159 buf = append(buf, 't')
160 } else {
161 buf = append(buf, 'f')
162 }
163
164 return buf, nil
165 }
166
167 type encodePlanBoolCodecBinaryBoolValuer struct{}
168
169 func (encodePlanBoolCodecBinaryBoolValuer) Encode(value any, buf []byte) (newBuf []byte, err error) {
170 b, err := value.(BoolValuer).BoolValue()
171 if err != nil {
172 return nil, err
173 }
174
175 if !b.Valid {
176 return nil, nil
177 }
178
179 if b.Bool {
180 buf = append(buf, 1)
181 } else {
182 buf = append(buf, 0)
183 }
184
185 return buf, nil
186 }
187
188 type encodePlanBoolCodecTextBool struct{}
189
190 func (encodePlanBoolCodecTextBool) Encode(value any, buf []byte) (newBuf []byte, err error) {
191 v := value.(bool)
192
193 if v {
194 buf = append(buf, 't')
195 } else {
196 buf = append(buf, 'f')
197 }
198
199 return buf, nil
200 }
201
202 func (BoolCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
203
204 switch format {
205 case BinaryFormatCode:
206 switch target.(type) {
207 case *bool:
208 return scanPlanBinaryBoolToBool{}
209 case BoolScanner:
210 return scanPlanBinaryBoolToBoolScanner{}
211 }
212 case TextFormatCode:
213 switch target.(type) {
214 case *bool:
215 return scanPlanTextAnyToBool{}
216 case BoolScanner:
217 return scanPlanTextAnyToBoolScanner{}
218 }
219 }
220
221 return nil
222 }
223
224 func (c BoolCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {
225 return c.DecodeValue(m, oid, format, src)
226 }
227
228 func (c BoolCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {
229 if src == nil {
230 return nil, nil
231 }
232
233 var b bool
234 err := codecScan(c, m, oid, format, src, &b)
235 if err != nil {
236 return nil, err
237 }
238 return b, nil
239 }
240
241 type scanPlanBinaryBoolToBool struct{}
242
243 func (scanPlanBinaryBoolToBool) Scan(src []byte, dst any) error {
244 if src == nil {
245 return fmt.Errorf("cannot scan NULL into %T", dst)
246 }
247
248 if len(src) != 1 {
249 return fmt.Errorf("invalid length for bool: %v", len(src))
250 }
251
252 p, ok := (dst).(*bool)
253 if !ok {
254 return ErrScanTargetTypeChanged
255 }
256
257 *p = src[0] == 1
258
259 return nil
260 }
261
262 type scanPlanTextAnyToBool struct{}
263
264 func (scanPlanTextAnyToBool) Scan(src []byte, dst any) error {
265 if src == nil {
266 return fmt.Errorf("cannot scan NULL into %T", dst)
267 }
268
269 if len(src) == 0 {
270 return fmt.Errorf("cannot scan empty string into %T", dst)
271 }
272
273 p, ok := (dst).(*bool)
274 if !ok {
275 return ErrScanTargetTypeChanged
276 }
277
278 v, err := planTextToBool(src)
279 if err != nil {
280 return err
281 }
282
283 *p = v
284
285 return nil
286 }
287
288 type scanPlanBinaryBoolToBoolScanner struct{}
289
290 func (scanPlanBinaryBoolToBoolScanner) Scan(src []byte, dst any) error {
291 s, ok := (dst).(BoolScanner)
292 if !ok {
293 return ErrScanTargetTypeChanged
294 }
295
296 if src == nil {
297 return s.ScanBool(Bool{})
298 }
299
300 if len(src) != 1 {
301 return fmt.Errorf("invalid length for bool: %v", len(src))
302 }
303
304 return s.ScanBool(Bool{Bool: src[0] == 1, Valid: true})
305 }
306
307 type scanPlanTextAnyToBoolScanner struct{}
308
309 func (scanPlanTextAnyToBoolScanner) Scan(src []byte, dst any) error {
310 s, ok := (dst).(BoolScanner)
311 if !ok {
312 return ErrScanTargetTypeChanged
313 }
314
315 if src == nil {
316 return s.ScanBool(Bool{})
317 }
318
319 if len(src) == 0 {
320 return fmt.Errorf("cannot scan empty string into %T", dst)
321 }
322
323 v, err := planTextToBool(src)
324 if err != nil {
325 return err
326 }
327
328 return s.ScanBool(Bool{Bool: v, Valid: true})
329 }
330
331
332 func planTextToBool(src []byte) (bool, error) {
333 s := string(bytes.ToLower(bytes.TrimSpace(src)))
334
335 switch {
336 case strings.HasPrefix("true", s), strings.HasPrefix("yes", s), s == "on", s == "1":
337 return true, nil
338 case strings.HasPrefix("false", s), strings.HasPrefix("no", s), strings.HasPrefix("off", s), s == "0":
339 return false, nil
340 default:
341 return false, fmt.Errorf("unknown boolean string representation %q", src)
342 }
343 }
344
View as plain text