2
0

Add pgtype.Record and prerequisite restructuring

Because reading a record type requires the decoder to be able to look up oid
to type mapping and types such as hstore have types that are not fixed between
different PostgreSQL servers it was necessary to restructure the pgtype system
so all encoders and decodes take a *ConnInfo that includes oid/name/type
information.
This commit is contained in:
Jack Christensen
2017-03-18 12:01:16 -05:00
parent 94d56e8556
commit 19c6689752
62 changed files with 2067 additions and 1105 deletions
+125 -4
View File
@@ -3,6 +3,7 @@ package pgtype
import (
"errors"
"io"
"reflect"
)
// PostgreSQL oids for common types
@@ -83,14 +84,14 @@ type BinaryDecoder interface {
// DecodeBinary decodes src into BinaryDecoder. If src is nil then the
// original SQL value is NULL. BinaryDecoder MUST not retain a reference to
// src. It MUST make a copy if it needs to retain the raw bytes.
DecodeBinary(src []byte) error
DecodeBinary(ci *ConnInfo, src []byte) error
}
type TextDecoder interface {
// DecodeText decodes src into TextDecoder. If src is nil then the original
// SQL value is NULL. TextDecoder MUST not retain a reference to src. It MUST
// make a copy if it needs to retain the raw bytes.
DecodeText(src []byte) error
DecodeText(ci *ConnInfo, src []byte) error
}
// BinaryEncoder is implemented by types that can encode themselves into the
@@ -100,7 +101,7 @@ type BinaryEncoder interface {
// SQL value NULL then write nothing and return (true, nil). The caller of
// EncodeBinary is responsible for writing the correct NULL value or the
// length of the data written.
EncodeBinary(w io.Writer) (null bool, err error)
EncodeBinary(ci *ConnInfo, w io.Writer) (null bool, err error)
}
// TextEncoder is implemented by types that can encode themselves into the
@@ -110,7 +111,127 @@ type TextEncoder interface {
// value NULL then write nothing and return (true, nil). The caller of
// EncodeText is responsible for writing the correct NULL value or the length
// of the data written.
EncodeText(w io.Writer) (null bool, err error)
EncodeText(ci *ConnInfo, w io.Writer) (null bool, err error)
}
var errUndefined = errors.New("cannot encode status undefined")
type DataType struct {
Value Value
Name string
Oid Oid
}
type ConnInfo struct {
oidToDataType map[Oid]*DataType
nameToDataType map[string]*DataType
reflectTypeToDataType map[reflect.Type]*DataType
}
func NewConnInfo() *ConnInfo {
return &ConnInfo{
oidToDataType: make(map[Oid]*DataType, 256),
nameToDataType: make(map[string]*DataType, 256),
reflectTypeToDataType: make(map[reflect.Type]*DataType, 256),
}
}
func (ci *ConnInfo) InitializeDataTypes(nameOids map[string]Oid) {
for name, oid := range nameOids {
var value Value
if t, ok := nameValues[name]; ok {
value = reflect.New(reflect.ValueOf(t).Elem().Type()).Interface().(Value)
} else {
value = &GenericText{}
}
ci.RegisterDataType(DataType{Value: value, Name: name, Oid: oid})
}
}
func (ci *ConnInfo) RegisterDataType(t DataType) {
ci.oidToDataType[t.Oid] = &t
ci.nameToDataType[t.Name] = &t
ci.reflectTypeToDataType[reflect.ValueOf(t.Value).Type()] = &t
}
func (ci *ConnInfo) DataTypeForOid(oid Oid) (*DataType, bool) {
dt, ok := ci.oidToDataType[oid]
return dt, ok
}
func (ci *ConnInfo) DataTypeForName(name string) (*DataType, bool) {
dt, ok := ci.nameToDataType[name]
return dt, ok
}
func (ci *ConnInfo) DataTypeForValue(v Value) (*DataType, bool) {
dt, ok := ci.reflectTypeToDataType[reflect.ValueOf(v).Type()]
return dt, ok
}
// DeepCopy makes a deep copy of the ConnInfo.
func (ci *ConnInfo) DeepCopy() *ConnInfo {
ci2 := &ConnInfo{
oidToDataType: make(map[Oid]*DataType, len(ci.oidToDataType)),
nameToDataType: make(map[string]*DataType, len(ci.nameToDataType)),
reflectTypeToDataType: make(map[reflect.Type]*DataType, len(ci.reflectTypeToDataType)),
}
for _, dt := range ci.oidToDataType {
ci2.RegisterDataType(DataType{
Value: reflect.New(reflect.ValueOf(dt.Value).Elem().Type()).Interface().(Value),
Name: dt.Name,
Oid: dt.Oid,
})
}
return ci2
}
var nameValues map[string]Value
func init() {
nameValues = map[string]Value{
"_aclitem": &AclitemArray{},
"_bool": &BoolArray{},
"_bytea": &ByteaArray{},
"_cidr": &CidrArray{},
"_date": &DateArray{},
"_float4": &Float4Array{},
"_float8": &Float8Array{},
"_inet": &InetArray{},
"_int2": &Int2Array{},
"_int4": &Int4Array{},
"_int8": &Int8Array{},
"_text": &TextArray{},
"_timestamp": &TimestampArray{},
"_timestamptz": &TimestamptzArray{},
"_varchar": &VarcharArray{},
"aclitem": &Aclitem{},
"bool": &Bool{},
"bytea": &Bytea{},
"char": &QChar{},
"cid": &Cid{},
"cidr": &Cidr{},
"date": &Date{},
"float4": &Float4{},
"float8": &Float8{},
"hstore": &Hstore{},
"inet": &Inet{},
"int2": &Int2{},
"int4": &Int4{},
"int8": &Int8{},
"json": &Json{},
"jsonb": &Jsonb{},
"name": &Name{},
"oid": &OidValue{},
"record": &Record{},
"text": &Text{},
"tid": &Tid{},
"timestamp": &Timestamp{},
"timestamptz": &Timestamptz{},
"unknown": &Unknown{},
"varchar": &Varchar{},
"xid": &Xid{},
}
}