github.com/jackc/pgx/v5@v5.5.5/pgtype/enum_codec.go (about) 1 package pgtype 2 3 import ( 4 "database/sql/driver" 5 "fmt" 6 ) 7 8 // EnumCodec is a codec that caches the strings it decodes. If the same string is read multiple times only one copy is 9 // allocated. These strings are only garbage collected when the EnumCodec is garbage collected. EnumCodec can be used 10 // for any text type not only enums, but it should only be used when there are a small number of possible values. 11 type EnumCodec struct { 12 membersMap map[string]string // map to quickly lookup member and reuse string instead of allocating 13 } 14 15 func (EnumCodec) FormatSupported(format int16) bool { 16 return format == TextFormatCode || format == BinaryFormatCode 17 } 18 19 func (EnumCodec) PreferredFormat() int16 { 20 return TextFormatCode 21 } 22 23 func (EnumCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan { 24 switch format { 25 case TextFormatCode, BinaryFormatCode: 26 switch value.(type) { 27 case string: 28 return encodePlanTextCodecString{} 29 case []byte: 30 return encodePlanTextCodecByteSlice{} 31 case TextValuer: 32 return encodePlanTextCodecTextValuer{} 33 } 34 } 35 36 return nil 37 } 38 39 func (c *EnumCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan { 40 switch format { 41 case TextFormatCode, BinaryFormatCode: 42 switch target.(type) { 43 case *string: 44 return &scanPlanTextAnyToEnumString{codec: c} 45 case *[]byte: 46 return scanPlanAnyToNewByteSlice{} 47 case TextScanner: 48 return &scanPlanTextAnyToEnumTextScanner{codec: c} 49 } 50 } 51 52 return nil 53 } 54 55 func (c *EnumCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) { 56 return c.DecodeValue(m, oid, format, src) 57 } 58 59 func (c *EnumCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) { 60 if src == nil { 61 return nil, nil 62 } 63 64 return c.lookupAndCacheString(src), nil 65 } 66 67 // lookupAndCacheString looks for src in the members map. If it is not found it is added to the map. 68 func (c *EnumCodec) lookupAndCacheString(src []byte) string { 69 if c.membersMap == nil { 70 c.membersMap = make(map[string]string) 71 } 72 73 if s, found := c.membersMap[string(src)]; found { 74 return s 75 } 76 77 s := string(src) 78 c.membersMap[s] = s 79 return s 80 } 81 82 type scanPlanTextAnyToEnumString struct { 83 codec *EnumCodec 84 } 85 86 func (plan *scanPlanTextAnyToEnumString) Scan(src []byte, dst any) error { 87 if src == nil { 88 return fmt.Errorf("cannot scan NULL into %T", dst) 89 } 90 91 p := (dst).(*string) 92 *p = plan.codec.lookupAndCacheString(src) 93 94 return nil 95 } 96 97 type scanPlanTextAnyToEnumTextScanner struct { 98 codec *EnumCodec 99 } 100 101 func (plan *scanPlanTextAnyToEnumTextScanner) Scan(src []byte, dst any) error { 102 scanner := (dst).(TextScanner) 103 104 if src == nil { 105 return scanner.ScanText(Text{}) 106 } 107 108 return scanner.ScanText(Text{String: plan.codec.lookupAndCacheString(src), Valid: true}) 109 }