github.com/jackc/pgx/v5@v5.5.5/pgtype/bool.go (about) 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 // Scan implements the database/sql Scanner interface. 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 // Value implements the database/sql/driver Valuer interface. 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 // https://www.postgresql.org/docs/11/datatype-boolean.html 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 }