1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
|
package pgtype
import "fmt"
// EnumType represents a enum type. While it implements Value, this is only in service of its type conversion duties
// when registered as a data type in a ConnType. It should not be used directly as a Value.
type EnumType struct {
value string
status Status
typeName string // PostgreSQL type name
members []string // enum members
membersMap map[string]string // map to quickly lookup member and reuse string instead of allocating
}
// NewEnumType initializes a new EnumType. It retains a read-only reference to members. members must not be changed.
func NewEnumType(typeName string, members []string) *EnumType {
et := &EnumType{typeName: typeName, members: members}
et.membersMap = make(map[string]string, len(members))
for _, m := range members {
et.membersMap[m] = m
}
return et
}
func (et *EnumType) NewTypeValue() Value {
return &EnumType{
value: et.value,
status: et.status,
typeName: et.typeName,
members: et.members,
membersMap: et.membersMap,
}
}
func (et *EnumType) TypeName() string {
return et.typeName
}
func (et *EnumType) Members() []string {
return et.members
}
// Set assigns src to dst. Set purposely does not check that src is a member. This allows continued error free
// operation in the event the PostgreSQL enum type is modified during a connection.
func (dst *EnumType) Set(src interface{}) error {
if src == nil {
dst.status = Null
return nil
}
if value, ok := src.(interface{ Get() interface{} }); ok {
value2 := value.Get()
if value2 != value {
return dst.Set(value2)
}
}
switch value := src.(type) {
case string:
dst.value = value
dst.status = Present
case *string:
if value == nil {
dst.status = Null
} else {
dst.value = *value
dst.status = Present
}
case []byte:
if value == nil {
dst.status = Null
} else {
dst.value = string(value)
dst.status = Present
}
default:
if originalSrc, ok := underlyingStringType(src); ok {
return dst.Set(originalSrc)
}
return fmt.Errorf("cannot convert %v to enum %s", value, dst.typeName)
}
return nil
}
func (dst EnumType) Get() interface{} {
switch dst.status {
case Present:
return dst.value
case Null:
return nil
default:
return dst.status
}
}
func (src *EnumType) AssignTo(dst interface{}) error {
switch src.status {
case Present:
switch v := dst.(type) {
case *string:
*v = src.value
return nil
case *[]byte:
*v = make([]byte, len(src.value))
copy(*v, src.value)
return nil
default:
if nextDst, retry := GetAssignToDstType(dst); retry {
return src.AssignTo(nextDst)
}
return fmt.Errorf("unable to assign to %T", dst)
}
case Null:
return NullAssignTo(dst)
}
return fmt.Errorf("cannot decode %#v into %T", src, dst)
}
func (EnumType) PreferredResultFormat() int16 {
return TextFormatCode
}
func (dst *EnumType) DecodeText(ci *ConnInfo, src []byte) error {
if src == nil {
dst.status = Null
return nil
}
// Lookup the string in membersMap to avoid an allocation.
if s, found := dst.membersMap[string(src)]; found {
dst.value = s
} else {
// If an enum type is modified after the initial connection it is possible to receive an unexpected value.
// Gracefully handle this situation. Purposely NOT modifying members and membersMap to allow for sharing members
// and membersMap between connections.
dst.value = string(src)
}
dst.status = Present
return nil
}
func (dst *EnumType) DecodeBinary(ci *ConnInfo, src []byte) error {
return dst.DecodeText(ci, src)
}
func (EnumType) PreferredParamFormat() int16 {
return TextFormatCode
}
func (src EnumType) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
switch src.status {
case Null:
return nil, nil
case Undefined:
return nil, errUndefined
}
return append(buf, src.value...), nil
}
func (src EnumType) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
return src.EncodeText(ci, buf)
}
|