2
0

Make EnumType implementation private

This commit is contained in:
Jack Christensen
2020-05-06 10:30:43 -05:00
parent 4d2b5a18c4
commit 2938981516
2 changed files with 51 additions and 46 deletions
+50 -45
View File
@@ -2,14 +2,19 @@ package pgtype
import errors "golang.org/x/xerrors" import errors "golang.org/x/xerrors"
// EnumType represents an enum type. In the normal pgtype model a Go type maps to a PostgreSQL type and an instance // EnumType represents a enum type. While it implements Value, this is only in service of its type conversion duties
// of a Go type maps to a PostgreSQL value of that type. EnumType is different in that an instance of EnumType // when registered as a data type in a ConnType. It should not be used directly as a Value.
// represents a PostgreSQL type. The zero value is not usable -- NewEnumType must be used as a constructor. In general, type EnumType interface {
// an EnumType should not be used to represent a value. It should only be used as an encoder and decoder internal to Value
// ConnInfo. TypeValue
type EnumType struct {
String string // Members returns possible members of this enumeration. The returned slice must not be modified.
Status Status Members() []string
}
type enumType struct {
value string
status Status
pgTypeName string // PostgreSQL type name pgTypeName string // PostgreSQL type name
members []string // enum members members []string // enum members
@@ -17,8 +22,8 @@ type EnumType struct {
} }
// NewEnumType initializes a new EnumType. It retains a read-only reference to members. members must not be changed. // NewEnumType initializes a new EnumType. It retains a read-only reference to members. members must not be changed.
func NewEnumType(pgTypeName string, members []string) *EnumType { func NewEnumType(pgTypeName string, members []string) EnumType {
et := &EnumType{pgTypeName: pgTypeName, members: members} et := &enumType{pgTypeName: pgTypeName, members: members}
et.membersMap = make(map[string]string, len(members)) et.membersMap = make(map[string]string, len(members))
for _, m := range members { for _, m := range members {
et.membersMap[m] = m et.membersMap[m] = m
@@ -26,10 +31,10 @@ func NewEnumType(pgTypeName string, members []string) *EnumType {
return et return et
} }
func (et *EnumType) CloneTypeValue() Value { func (et *enumType) CloneTypeValue() Value {
return &EnumType{ return &enumType{
String: et.String, value: et.value,
Status: et.Status, status: et.status,
pgTypeName: et.pgTypeName, pgTypeName: et.pgTypeName,
members: et.members, members: et.members,
@@ -37,19 +42,19 @@ func (et *EnumType) CloneTypeValue() Value {
} }
} }
func (et *EnumType) PgTypeName() string { func (et *enumType) PgTypeName() string {
return et.pgTypeName return et.pgTypeName
} }
func (et *EnumType) Members() []string { func (et *enumType) Members() []string {
return et.members return et.members
} }
// Set assigns src to dst. Set purposely does not check that src is a member. This allows continued error free // 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. // operation in the event the PostgreSQL enum type is modified during a connection.
func (dst *EnumType) Set(src interface{}) error { func (dst *enumType) Set(src interface{}) error {
if src == nil { if src == nil {
dst.Status = Null dst.status = Null
return nil return nil
} }
@@ -62,21 +67,21 @@ func (dst *EnumType) Set(src interface{}) error {
switch value := src.(type) { switch value := src.(type) {
case string: case string:
dst.String = value dst.value = value
dst.Status = Present dst.status = Present
case *string: case *string:
if value == nil { if value == nil {
dst.Status = Null dst.status = Null
} else { } else {
dst.String = *value dst.value = *value
dst.Status = Present dst.status = Present
} }
case []byte: case []byte:
if value == nil { if value == nil {
dst.Status = Null dst.status = Null
} else { } else {
dst.String = string(value) dst.value = string(value)
dst.Status = Present dst.status = Present
} }
default: default:
if originalSrc, ok := underlyingStringType(src); ok { if originalSrc, ok := underlyingStringType(src); ok {
@@ -88,27 +93,27 @@ func (dst *EnumType) Set(src interface{}) error {
return nil return nil
} }
func (dst EnumType) Get() interface{} { func (dst enumType) Get() interface{} {
switch dst.Status { switch dst.status {
case Present: case Present:
return dst.String return dst.value
case Null: case Null:
return nil return nil
default: default:
return dst.Status return dst.status
} }
} }
func (src *EnumType) AssignTo(dst interface{}) error { func (src *enumType) AssignTo(dst interface{}) error {
switch src.Status { switch src.status {
case Present: case Present:
switch v := dst.(type) { switch v := dst.(type) {
case *string: case *string:
*v = src.String *v = src.value
return nil return nil
case *[]byte: case *[]byte:
*v = make([]byte, len(src.String)) *v = make([]byte, len(src.value))
copy(*v, src.String) copy(*v, src.value)
return nil return nil
default: default:
if nextDst, retry := GetAssignToDstType(dst); retry { if nextDst, retry := GetAssignToDstType(dst); retry {
@@ -123,41 +128,41 @@ func (src *EnumType) AssignTo(dst interface{}) error {
return errors.Errorf("cannot decode %#v into %T", src, dst) return errors.Errorf("cannot decode %#v into %T", src, dst)
} }
func (dst *EnumType) DecodeText(ci *ConnInfo, src []byte) error { func (dst *enumType) DecodeText(ci *ConnInfo, src []byte) error {
if src == nil { if src == nil {
dst.Status = Null dst.status = Null
return nil return nil
} }
// Lookup the string in membersMap to avoid an allocation. // Lookup the string in membersMap to avoid an allocation.
if s, found := dst.membersMap[string(src)]; found { if s, found := dst.membersMap[string(src)]; found {
dst.String = s dst.value = s
} else { } else {
// If an enum type is modified after the initial connection it is possible to receive an unexpected value. // 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 // Gracefully handle this situation. Purposely NOT modifying members and membersMap to allow for sharing members
// and membersMap between connections. // and membersMap between connections.
dst.String = string(src) dst.value = string(src)
} }
dst.Status = Present dst.status = Present
return nil return nil
} }
func (dst *EnumType) DecodeBinary(ci *ConnInfo, src []byte) error { func (dst *enumType) DecodeBinary(ci *ConnInfo, src []byte) error {
return dst.DecodeText(ci, src) return dst.DecodeText(ci, src)
} }
func (src EnumType) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { func (src enumType) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
switch src.Status { switch src.status {
case Null: case Null:
return nil, nil return nil, nil
case Undefined: case Undefined:
return nil, errUndefined return nil, errUndefined
} }
return append(buf, src.String...), nil return append(buf, src.value...), nil
} }
func (src EnumType) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { func (src enumType) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
return src.EncodeText(ci, buf) return src.EncodeText(ci, buf)
} }
+1 -1
View File
@@ -12,7 +12,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func setupEnum(t *testing.T, conn *pgx.Conn) *pgtype.EnumType { func setupEnum(t *testing.T, conn *pgx.Conn) pgtype.EnumType {
_, err := conn.Exec(context.Background(), "drop type if exists pgtype_enum_color;") _, err := conn.Exec(context.Background(), "drop type if exists pgtype_enum_color;")
require.NoError(t, err) require.NoError(t, err)