github.com/jackc/pgx/v5@v5.5.5/pgtype/qchar.go (about) 1 package pgtype 2 3 import ( 4 "database/sql/driver" 5 "fmt" 6 "math" 7 ) 8 9 // QCharCodec is for PostgreSQL's special 8-bit-only "char" type more akin to the C 10 // language's char type, or Go's byte type. (Note that the name in PostgreSQL 11 // itself is "char", in double-quotes, and not char.) It gets used a lot in 12 // PostgreSQL's system tables to hold a single ASCII character value (eg 13 // pg_class.relkind). It is named Qchar for quoted char to disambiguate from SQL 14 // standard type char. 15 type QCharCodec struct{} 16 17 func (QCharCodec) FormatSupported(format int16) bool { 18 return format == TextFormatCode || format == BinaryFormatCode 19 } 20 21 func (QCharCodec) PreferredFormat() int16 { 22 return BinaryFormatCode 23 } 24 25 func (QCharCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan { 26 switch format { 27 case TextFormatCode, BinaryFormatCode: 28 switch value.(type) { 29 case byte: 30 return encodePlanQcharCodecByte{} 31 case rune: 32 return encodePlanQcharCodecRune{} 33 } 34 } 35 36 return nil 37 } 38 39 type encodePlanQcharCodecByte struct{} 40 41 func (encodePlanQcharCodecByte) Encode(value any, buf []byte) (newBuf []byte, err error) { 42 b := value.(byte) 43 buf = append(buf, b) 44 return buf, nil 45 } 46 47 type encodePlanQcharCodecRune struct{} 48 49 func (encodePlanQcharCodecRune) Encode(value any, buf []byte) (newBuf []byte, err error) { 50 r := value.(rune) 51 if r > math.MaxUint8 { 52 return nil, fmt.Errorf(`%v cannot be encoded to "char"`, r) 53 } 54 b := byte(r) 55 buf = append(buf, b) 56 return buf, nil 57 } 58 59 func (QCharCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan { 60 switch format { 61 case TextFormatCode, BinaryFormatCode: 62 switch target.(type) { 63 case *byte: 64 return scanPlanQcharCodecByte{} 65 case *rune: 66 return scanPlanQcharCodecRune{} 67 } 68 } 69 70 return nil 71 } 72 73 type scanPlanQcharCodecByte struct{} 74 75 func (scanPlanQcharCodecByte) Scan(src []byte, dst any) error { 76 if src == nil { 77 return fmt.Errorf("cannot scan NULL into %T", dst) 78 } 79 80 if len(src) > 1 { 81 return fmt.Errorf(`invalid length for "char": %v`, len(src)) 82 } 83 84 b := dst.(*byte) 85 // In the text format the zero value is returned as a zero byte value instead of 0 86 if len(src) == 0 { 87 *b = 0 88 } else { 89 *b = src[0] 90 } 91 92 return nil 93 } 94 95 type scanPlanQcharCodecRune struct{} 96 97 func (scanPlanQcharCodecRune) Scan(src []byte, dst any) error { 98 if src == nil { 99 return fmt.Errorf("cannot scan NULL into %T", dst) 100 } 101 102 if len(src) > 1 { 103 return fmt.Errorf(`invalid length for "char": %v`, len(src)) 104 } 105 106 r := dst.(*rune) 107 // In the text format the zero value is returned as a zero byte value instead of 0 108 if len(src) == 0 { 109 *r = 0 110 } else { 111 *r = rune(src[0]) 112 } 113 114 return nil 115 } 116 117 func (c QCharCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) { 118 if src == nil { 119 return nil, nil 120 } 121 122 var r rune 123 err := codecScan(c, m, oid, format, src, &r) 124 if err != nil { 125 return nil, err 126 } 127 return string(r), nil 128 } 129 130 func (c QCharCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) { 131 if src == nil { 132 return nil, nil 133 } 134 135 var r rune 136 err := codecScan(c, m, oid, format, src, &r) 137 if err != nil { 138 return nil, err 139 } 140 return r, nil 141 }