Import to pgx main repo in pgtype subdir
This commit is contained in:
@@ -0,0 +1,941 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
"net"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
// PostgreSQL oids for common types
|
||||
const (
|
||||
BoolOID = 16
|
||||
ByteaOID = 17
|
||||
QCharOID = 18
|
||||
NameOID = 19
|
||||
Int8OID = 20
|
||||
Int2OID = 21
|
||||
Int4OID = 23
|
||||
TextOID = 25
|
||||
OIDOID = 26
|
||||
TIDOID = 27
|
||||
XIDOID = 28
|
||||
CIDOID = 29
|
||||
JSONOID = 114
|
||||
PointOID = 600
|
||||
LsegOID = 601
|
||||
PathOID = 602
|
||||
BoxOID = 603
|
||||
PolygonOID = 604
|
||||
LineOID = 628
|
||||
CIDROID = 650
|
||||
CIDRArrayOID = 651
|
||||
Float4OID = 700
|
||||
Float8OID = 701
|
||||
CircleOID = 718
|
||||
UnknownOID = 705
|
||||
MacaddrOID = 829
|
||||
InetOID = 869
|
||||
BoolArrayOID = 1000
|
||||
Int2ArrayOID = 1005
|
||||
Int4ArrayOID = 1007
|
||||
TextArrayOID = 1009
|
||||
ByteaArrayOID = 1001
|
||||
BPCharArrayOID = 1014
|
||||
VarcharArrayOID = 1015
|
||||
Int8ArrayOID = 1016
|
||||
Float4ArrayOID = 1021
|
||||
Float8ArrayOID = 1022
|
||||
ACLItemOID = 1033
|
||||
ACLItemArrayOID = 1034
|
||||
InetArrayOID = 1041
|
||||
BPCharOID = 1042
|
||||
VarcharOID = 1043
|
||||
DateOID = 1082
|
||||
TimeOID = 1083
|
||||
TimestampOID = 1114
|
||||
TimestampArrayOID = 1115
|
||||
DateArrayOID = 1182
|
||||
TimestamptzOID = 1184
|
||||
TimestamptzArrayOID = 1185
|
||||
IntervalOID = 1186
|
||||
NumericArrayOID = 1231
|
||||
BitOID = 1560
|
||||
VarbitOID = 1562
|
||||
NumericOID = 1700
|
||||
RecordOID = 2249
|
||||
UUIDOID = 2950
|
||||
UUIDArrayOID = 2951
|
||||
JSONBOID = 3802
|
||||
JSONBArrayOID = 3807
|
||||
DaterangeOID = 3912
|
||||
Int4rangeOID = 3904
|
||||
NumrangeOID = 3906
|
||||
TsrangeOID = 3908
|
||||
TsrangeArrayOID = 3909
|
||||
TstzrangeOID = 3910
|
||||
TstzrangeArrayOID = 3911
|
||||
Int8rangeOID = 3926
|
||||
)
|
||||
|
||||
type InfinityModifier int8
|
||||
|
||||
const (
|
||||
Infinity InfinityModifier = 1
|
||||
None InfinityModifier = 0
|
||||
NegativeInfinity InfinityModifier = -Infinity
|
||||
)
|
||||
|
||||
func (im InfinityModifier) String() string {
|
||||
switch im {
|
||||
case None:
|
||||
return "none"
|
||||
case Infinity:
|
||||
return "infinity"
|
||||
case NegativeInfinity:
|
||||
return "-infinity"
|
||||
default:
|
||||
return "invalid"
|
||||
}
|
||||
}
|
||||
|
||||
// PostgreSQL format codes
|
||||
const (
|
||||
TextFormatCode = 0
|
||||
BinaryFormatCode = 1
|
||||
)
|
||||
|
||||
// Value translates values to and from an internal canonical representation for the type. To actually be usable a type
|
||||
// that implements Value should also implement some combination of BinaryDecoder, BinaryEncoder, TextDecoder,
|
||||
// and TextEncoder.
|
||||
//
|
||||
// Operations that update a Value (e.g. Set, DecodeText, DecodeBinary) should entirely replace the value. e.g. Internal
|
||||
// slices should be replaced not resized and reused. This allows Get and AssignTo to return a slice directly rather
|
||||
// than incur a usually unnecessary copy.
|
||||
type Value interface {
|
||||
// Set converts and assigns src to itself. Value takes ownership of src.
|
||||
Set(src interface{}) error
|
||||
|
||||
// Get returns the simplest representation of Value. Get may return a pointer to an internal value but it must never
|
||||
// mutate that value. e.g. If Get returns a []byte Value must never change the contents of the []byte.
|
||||
Get() interface{}
|
||||
|
||||
// AssignTo converts and assigns the Value to dst. AssignTo may a pointer to an internal value but it must never
|
||||
// mutate that value. e.g. If Get returns a []byte Value must never change the contents of the []byte.
|
||||
AssignTo(dst interface{}) error
|
||||
}
|
||||
|
||||
// TypeValue is a Value where instances can represent different PostgreSQL types. This can be useful for
|
||||
// representing types such as enums, composites, and arrays.
|
||||
//
|
||||
// In general, instances of TypeValue should not be used to directly represent a value. It should only be used as an
|
||||
// encoder and decoder internal to ConnInfo.
|
||||
type TypeValue interface {
|
||||
Value
|
||||
|
||||
// NewTypeValue creates a TypeValue including references to internal type information. e.g. the list of members
|
||||
// in an EnumType.
|
||||
NewTypeValue() Value
|
||||
|
||||
// TypeName returns the PostgreSQL name of this type.
|
||||
TypeName() string
|
||||
}
|
||||
|
||||
// ValueTranscoder is a value that implements the text and binary encoding and decoding interfaces.
|
||||
type ValueTranscoder interface {
|
||||
Value
|
||||
FormatSupport
|
||||
ParamEncoder
|
||||
ResultDecoder
|
||||
}
|
||||
|
||||
type FormatSupport interface {
|
||||
BinaryFormatSupported() bool
|
||||
TextFormatSupported() bool
|
||||
PreferredFormat() int16
|
||||
}
|
||||
|
||||
type ParamEncoder interface {
|
||||
// EncodeParam should append the encoded value of self to buf. If self is the
|
||||
// SQL value NULL then append nothing and return (nil, nil). The caller of
|
||||
// EncodeText is responsible for writing the correct NULL value or the
|
||||
// length of the data written.
|
||||
EncodeParam(ci *ConnInfo, oid uint32, format int16, buf []byte) (newBuf []byte, err error)
|
||||
}
|
||||
|
||||
type ResultDecoder interface {
|
||||
// DecodeResult decodes src into ResultDecoder. If src is nil then the
|
||||
// original SQL value is NULL. ResultDecoder takes ownership of src. The
|
||||
// caller MUST not use it again.
|
||||
DecodeResult(ci *ConnInfo, oid uint32, format int16, src []byte) error
|
||||
}
|
||||
|
||||
// ResultFormatPreferrer allows a type to specify its preferred result format instead of it being inferred from
|
||||
// whether it is also a BinaryDecoder.
|
||||
type ResultFormatPreferrer interface {
|
||||
PreferredResultFormat() int16
|
||||
}
|
||||
|
||||
type BinaryDecoder interface {
|
||||
// DecodeBinary decodes src into BinaryDecoder. If src is nil then the
|
||||
// original SQL value is NULL. BinaryDecoder takes ownership of src. The
|
||||
// caller MUST not use it again.
|
||||
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 takes ownership of src. The caller MUST not
|
||||
// use it again.
|
||||
DecodeText(ci *ConnInfo, src []byte) error
|
||||
}
|
||||
|
||||
// BinaryEncoder is implemented by types that can encode themselves into the
|
||||
// PostgreSQL binary wire format.
|
||||
type BinaryEncoder interface {
|
||||
// EncodeBinary should append the binary format of self to buf. If self is the
|
||||
// SQL value NULL then append nothing and return (nil, nil). The caller of
|
||||
// EncodeBinary is responsible for writing the correct NULL value or the
|
||||
// length of the data written.
|
||||
EncodeBinary(ci *ConnInfo, buf []byte) (newBuf []byte, err error)
|
||||
}
|
||||
|
||||
// TextEncoder is implemented by types that can encode themselves into the
|
||||
// PostgreSQL text wire format.
|
||||
type TextEncoder interface {
|
||||
// EncodeText should append the text format of self to buf. If self is the
|
||||
// SQL value NULL then append nothing and return (nil, nil). The caller of
|
||||
// EncodeText is responsible for writing the correct NULL value or the
|
||||
// length of the data written.
|
||||
EncodeText(ci *ConnInfo, buf []byte) (newBuf []byte, err error)
|
||||
}
|
||||
|
||||
type nullAssignmentError struct {
|
||||
dst interface{}
|
||||
}
|
||||
|
||||
func (e *nullAssignmentError) Error() string {
|
||||
return fmt.Sprintf("cannot assign NULL to %T", e.dst)
|
||||
}
|
||||
|
||||
type DataType struct {
|
||||
Value Value
|
||||
|
||||
resultDecoder ResultDecoder
|
||||
|
||||
textDecoder TextDecoder
|
||||
binaryDecoder BinaryDecoder
|
||||
|
||||
Name string
|
||||
OID uint32
|
||||
}
|
||||
|
||||
type ConnInfo struct {
|
||||
oidToDataType map[uint32]*DataType
|
||||
nameToDataType map[string]*DataType
|
||||
reflectTypeToName map[reflect.Type]string
|
||||
oidToFormatCode map[uint32]int16
|
||||
oidToResultFormatCode map[uint32]int16
|
||||
|
||||
reflectTypeToDataType map[reflect.Type]*DataType
|
||||
|
||||
preferAssignToOverSQLScannerTypes map[reflect.Type]struct{}
|
||||
}
|
||||
|
||||
func newConnInfo() *ConnInfo {
|
||||
return &ConnInfo{
|
||||
oidToDataType: make(map[uint32]*DataType),
|
||||
nameToDataType: make(map[string]*DataType),
|
||||
reflectTypeToName: make(map[reflect.Type]string),
|
||||
oidToFormatCode: make(map[uint32]int16),
|
||||
oidToResultFormatCode: make(map[uint32]int16),
|
||||
preferAssignToOverSQLScannerTypes: make(map[reflect.Type]struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func NewConnInfo() *ConnInfo {
|
||||
ci := newConnInfo()
|
||||
|
||||
ci.RegisterDataType(DataType{Value: &ACLItemArray{}, Name: "_aclitem", OID: ACLItemArrayOID})
|
||||
ci.RegisterDataType(DataType{Value: &BoolArray{}, Name: "_bool", OID: BoolArrayOID})
|
||||
ci.RegisterDataType(DataType{Value: &BPCharArray{}, Name: "_bpchar", OID: BPCharArrayOID})
|
||||
ci.RegisterDataType(DataType{Value: &ByteaArray{}, Name: "_bytea", OID: ByteaArrayOID})
|
||||
ci.RegisterDataType(DataType{Value: &CIDRArray{}, Name: "_cidr", OID: CIDRArrayOID})
|
||||
ci.RegisterDataType(DataType{Value: &DateArray{}, Name: "_date", OID: DateArrayOID})
|
||||
ci.RegisterDataType(DataType{Value: &Float4Array{}, Name: "_float4", OID: Float4ArrayOID})
|
||||
ci.RegisterDataType(DataType{Value: &Float8Array{}, Name: "_float8", OID: Float8ArrayOID})
|
||||
ci.RegisterDataType(DataType{Value: &InetArray{}, Name: "_inet", OID: InetArrayOID})
|
||||
ci.RegisterDataType(DataType{Value: &Int2Array{}, Name: "_int2", OID: Int2ArrayOID})
|
||||
ci.RegisterDataType(DataType{Value: &Int4Array{}, Name: "_int4", OID: Int4ArrayOID})
|
||||
ci.RegisterDataType(DataType{Value: &Int8Array{}, Name: "_int8", OID: Int8ArrayOID})
|
||||
ci.RegisterDataType(DataType{Value: &NumericArray{}, Name: "_numeric", OID: NumericArrayOID})
|
||||
ci.RegisterDataType(DataType{Value: &TextArray{}, Name: "_text", OID: TextArrayOID})
|
||||
ci.RegisterDataType(DataType{Value: &TimestampArray{}, Name: "_timestamp", OID: TimestampArrayOID})
|
||||
ci.RegisterDataType(DataType{Value: &TimestamptzArray{}, Name: "_timestamptz", OID: TimestamptzArrayOID})
|
||||
ci.RegisterDataType(DataType{Value: &UUIDArray{}, Name: "_uuid", OID: UUIDArrayOID})
|
||||
ci.RegisterDataType(DataType{Value: &VarcharArray{}, Name: "_varchar", OID: VarcharArrayOID})
|
||||
ci.RegisterDataType(DataType{Value: &ACLItem{}, Name: "aclitem", OID: ACLItemOID})
|
||||
ci.RegisterDataType(DataType{Value: &Bit{}, Name: "bit", OID: BitOID})
|
||||
ci.RegisterDataType(DataType{Value: &Bool{}, Name: "bool", OID: BoolOID})
|
||||
ci.RegisterDataType(DataType{Value: &Box{}, Name: "box", OID: BoxOID})
|
||||
ci.RegisterDataType(DataType{Value: &BPChar{}, Name: "bpchar", OID: BPCharOID})
|
||||
ci.RegisterDataType(DataType{Value: &Bytea{}, Name: "bytea", OID: ByteaOID})
|
||||
ci.RegisterDataType(DataType{Value: &QChar{}, Name: "char", OID: QCharOID})
|
||||
ci.RegisterDataType(DataType{Value: &CID{}, Name: "cid", OID: CIDOID})
|
||||
ci.RegisterDataType(DataType{Value: &CIDR{}, Name: "cidr", OID: CIDROID})
|
||||
ci.RegisterDataType(DataType{Value: &Circle{}, Name: "circle", OID: CircleOID})
|
||||
ci.RegisterDataType(DataType{Value: &Date{}, Name: "date", OID: DateOID})
|
||||
ci.RegisterDataType(DataType{Value: &Daterange{}, Name: "daterange", OID: DaterangeOID})
|
||||
ci.RegisterDataType(DataType{Value: &Float4{}, Name: "float4", OID: Float4OID})
|
||||
ci.RegisterDataType(DataType{Value: &Float8{}, Name: "float8", OID: Float8OID})
|
||||
ci.RegisterDataType(DataType{Value: &Inet{}, Name: "inet", OID: InetOID})
|
||||
ci.RegisterDataType(DataType{Value: &Int2{}, Name: "int2", OID: Int2OID})
|
||||
ci.RegisterDataType(DataType{Value: &Int4{}, Name: "int4", OID: Int4OID})
|
||||
ci.RegisterDataType(DataType{Value: &Int4range{}, Name: "int4range", OID: Int4rangeOID})
|
||||
ci.RegisterDataType(DataType{Value: &Int8{}, Name: "int8", OID: Int8OID})
|
||||
ci.RegisterDataType(DataType{Value: &Int8range{}, Name: "int8range", OID: Int8rangeOID})
|
||||
ci.RegisterDataType(DataType{Value: &Interval{}, Name: "interval", OID: IntervalOID})
|
||||
ci.RegisterDataType(DataType{Value: &JSON{}, Name: "json", OID: JSONOID})
|
||||
ci.RegisterDataType(DataType{Value: &JSONB{}, Name: "jsonb", OID: JSONBOID})
|
||||
ci.RegisterDataType(DataType{Value: &JSONBArray{}, Name: "_jsonb", OID: JSONBArrayOID})
|
||||
ci.RegisterDataType(DataType{Value: &Line{}, Name: "line", OID: LineOID})
|
||||
ci.RegisterDataType(DataType{Value: &Lseg{}, Name: "lseg", OID: LsegOID})
|
||||
ci.RegisterDataType(DataType{Value: &Macaddr{}, Name: "macaddr", OID: MacaddrOID})
|
||||
ci.RegisterDataType(DataType{Value: &Name{}, Name: "name", OID: NameOID})
|
||||
ci.RegisterDataType(DataType{Value: &Numeric{}, Name: "numeric", OID: NumericOID})
|
||||
ci.RegisterDataType(DataType{Value: &Numrange{}, Name: "numrange", OID: NumrangeOID})
|
||||
ci.RegisterDataType(DataType{Value: &OIDValue{}, Name: "oid", OID: OIDOID})
|
||||
ci.RegisterDataType(DataType{Value: &Path{}, Name: "path", OID: PathOID})
|
||||
ci.RegisterDataType(DataType{Value: &Point{}, Name: "point", OID: PointOID})
|
||||
ci.RegisterDataType(DataType{Value: &Polygon{}, Name: "polygon", OID: PolygonOID})
|
||||
ci.RegisterDataType(DataType{Value: &Record{}, Name: "record", OID: RecordOID})
|
||||
ci.RegisterDataType(DataType{Value: &Text{}, Name: "text", OID: TextOID})
|
||||
ci.RegisterDataType(DataType{Value: &TID{}, Name: "tid", OID: TIDOID})
|
||||
ci.RegisterDataType(DataType{Value: &Time{}, Name: "time", OID: TimeOID})
|
||||
ci.RegisterDataType(DataType{Value: &Timestamp{}, Name: "timestamp", OID: TimestampOID})
|
||||
ci.RegisterDataType(DataType{Value: &Timestamptz{}, Name: "timestamptz", OID: TimestamptzOID})
|
||||
ci.RegisterDataType(DataType{Value: &Tsrange{}, Name: "tsrange", OID: TsrangeOID})
|
||||
ci.RegisterDataType(DataType{Value: &TsrangeArray{}, Name: "_tsrange", OID: TsrangeArrayOID})
|
||||
ci.RegisterDataType(DataType{Value: &Tstzrange{}, Name: "tstzrange", OID: TstzrangeOID})
|
||||
ci.RegisterDataType(DataType{Value: &TstzrangeArray{}, Name: "_tstzrange", OID: TstzrangeArrayOID})
|
||||
ci.RegisterDataType(DataType{Value: &Unknown{}, Name: "unknown", OID: UnknownOID})
|
||||
ci.RegisterDataType(DataType{Value: &UUID{}, Name: "uuid", OID: UUIDOID})
|
||||
ci.RegisterDataType(DataType{Value: &Varbit{}, Name: "varbit", OID: VarbitOID})
|
||||
ci.RegisterDataType(DataType{Value: &Varchar{}, Name: "varchar", OID: VarcharOID})
|
||||
ci.RegisterDataType(DataType{Value: &XID{}, Name: "xid", OID: XIDOID})
|
||||
|
||||
registerDefaultPgTypeVariants := func(name, arrayName string, value interface{}) {
|
||||
ci.RegisterDefaultPgType(value, name)
|
||||
valueType := reflect.TypeOf(value)
|
||||
|
||||
ci.RegisterDefaultPgType(reflect.New(valueType).Interface(), name)
|
||||
|
||||
sliceType := reflect.SliceOf(valueType)
|
||||
ci.RegisterDefaultPgType(reflect.MakeSlice(sliceType, 0, 0).Interface(), arrayName)
|
||||
|
||||
ci.RegisterDefaultPgType(reflect.New(sliceType).Interface(), arrayName)
|
||||
}
|
||||
|
||||
// Integer types that directly map to a PostgreSQL type
|
||||
registerDefaultPgTypeVariants("int2", "_int2", int16(0))
|
||||
registerDefaultPgTypeVariants("int4", "_int4", int32(0))
|
||||
registerDefaultPgTypeVariants("int8", "_int8", int64(0))
|
||||
|
||||
// Integer types that do not have a direct match to a PostgreSQL type
|
||||
registerDefaultPgTypeVariants("int8", "_int8", uint16(0))
|
||||
registerDefaultPgTypeVariants("int8", "_int8", uint32(0))
|
||||
registerDefaultPgTypeVariants("int8", "_int8", uint64(0))
|
||||
registerDefaultPgTypeVariants("int8", "_int8", int(0))
|
||||
registerDefaultPgTypeVariants("int8", "_int8", uint(0))
|
||||
|
||||
registerDefaultPgTypeVariants("float4", "_float4", float32(0))
|
||||
registerDefaultPgTypeVariants("float8", "_float8", float64(0))
|
||||
|
||||
registerDefaultPgTypeVariants("bool", "_bool", false)
|
||||
registerDefaultPgTypeVariants("timestamptz", "_timestamptz", time.Time{})
|
||||
registerDefaultPgTypeVariants("text", "_text", "")
|
||||
registerDefaultPgTypeVariants("bytea", "_bytea", []byte(nil))
|
||||
|
||||
registerDefaultPgTypeVariants("inet", "_inet", net.IP{})
|
||||
ci.RegisterDefaultPgType((*net.IPNet)(nil), "cidr")
|
||||
ci.RegisterDefaultPgType([]*net.IPNet(nil), "_cidr")
|
||||
|
||||
return ci
|
||||
}
|
||||
|
||||
func (ci *ConnInfo) InitializeDataTypes(nameOIDs map[string]uint32) {
|
||||
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) {
|
||||
t.Value = NewValue(t.Value)
|
||||
|
||||
ci.oidToDataType[t.OID] = &t
|
||||
ci.nameToDataType[t.Name] = &t
|
||||
|
||||
{
|
||||
var formatCode int16
|
||||
if pfp, ok := t.Value.(FormatSupport); ok {
|
||||
formatCode = pfp.PreferredFormat()
|
||||
} else if _, ok := t.Value.(BinaryEncoder); ok {
|
||||
formatCode = BinaryFormatCode
|
||||
}
|
||||
ci.oidToFormatCode[t.OID] = formatCode
|
||||
}
|
||||
|
||||
if d, ok := t.Value.(ResultDecoder); ok {
|
||||
t.resultDecoder = d
|
||||
}
|
||||
|
||||
if d, ok := t.Value.(TextDecoder); ok {
|
||||
t.textDecoder = d
|
||||
}
|
||||
|
||||
if d, ok := t.Value.(BinaryDecoder); ok {
|
||||
t.binaryDecoder = d
|
||||
}
|
||||
|
||||
ci.reflectTypeToDataType = nil // Invalidated by type registration
|
||||
}
|
||||
|
||||
// RegisterDefaultPgType registers a mapping of a Go type to a PostgreSQL type name. Typically the data type to be
|
||||
// encoded or decoded is determined by the PostgreSQL OID. But if the OID of a value to be encoded or decoded is
|
||||
// unknown, this additional mapping will be used by DataTypeForValue to determine a suitable data type.
|
||||
func (ci *ConnInfo) RegisterDefaultPgType(value interface{}, name string) {
|
||||
ci.reflectTypeToName[reflect.TypeOf(value)] = name
|
||||
ci.reflectTypeToDataType = nil // Invalidated by registering a default type
|
||||
}
|
||||
|
||||
func (ci *ConnInfo) DataTypeForOID(oid uint32) (*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) buildReflectTypeToDataType() {
|
||||
ci.reflectTypeToDataType = make(map[reflect.Type]*DataType)
|
||||
|
||||
for _, dt := range ci.oidToDataType {
|
||||
if _, is := dt.Value.(TypeValue); !is {
|
||||
ci.reflectTypeToDataType[reflect.ValueOf(dt.Value).Type()] = dt
|
||||
}
|
||||
}
|
||||
|
||||
for reflectType, name := range ci.reflectTypeToName {
|
||||
if dt, ok := ci.nameToDataType[name]; ok {
|
||||
ci.reflectTypeToDataType[reflectType] = dt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DataTypeForValue finds a data type suitable for v. Use RegisterDataType to register types that can encode and decode
|
||||
// themselves. Use RegisterDefaultPgType to register that can be handled by a registered data type.
|
||||
func (ci *ConnInfo) DataTypeForValue(v interface{}) (*DataType, bool) {
|
||||
if ci.reflectTypeToDataType == nil {
|
||||
ci.buildReflectTypeToDataType()
|
||||
}
|
||||
|
||||
if tv, ok := v.(TypeValue); ok {
|
||||
dt, ok := ci.nameToDataType[tv.TypeName()]
|
||||
return dt, ok
|
||||
}
|
||||
|
||||
dt, ok := ci.reflectTypeToDataType[reflect.TypeOf(v)]
|
||||
return dt, ok
|
||||
}
|
||||
|
||||
func (ci *ConnInfo) FormatCodeForOID(oid uint32) int16 {
|
||||
fc, ok := ci.oidToFormatCode[oid]
|
||||
if ok {
|
||||
return fc
|
||||
}
|
||||
return TextFormatCode
|
||||
}
|
||||
|
||||
// PreferAssignToOverSQLScannerForType makes a sql.Scanner type use the AssignTo scan path instead of sql.Scanner.
|
||||
// This is primarily for efficient integration with 3rd party numeric and UUID types.
|
||||
func (ci *ConnInfo) PreferAssignToOverSQLScannerForType(value interface{}) {
|
||||
ci.preferAssignToOverSQLScannerTypes[reflect.TypeOf(value)] = struct{}{}
|
||||
}
|
||||
|
||||
// ScanPlan is a precompiled plan to scan into a type of destination.
|
||||
type ScanPlan interface {
|
||||
// Scan scans src into dst. If the dst type has changed in an incompatible way a ScanPlan should automatically
|
||||
// replan and scan.
|
||||
Scan(ci *ConnInfo, oid uint32, formatCode int16, src []byte, dst interface{}) error
|
||||
}
|
||||
|
||||
type scanPlanDstResultDecoder struct{}
|
||||
|
||||
func (scanPlanDstResultDecoder) Scan(ci *ConnInfo, oid uint32, formatCode int16, src []byte, dst interface{}) error {
|
||||
if d, ok := (dst).(ResultDecoder); ok {
|
||||
return d.DecodeResult(ci, oid, formatCode, src)
|
||||
}
|
||||
|
||||
newPlan := ci.PlanScan(oid, formatCode, dst)
|
||||
return newPlan.Scan(ci, oid, formatCode, src, dst)
|
||||
}
|
||||
|
||||
type scanPlanDstBinaryDecoder struct{}
|
||||
|
||||
func (scanPlanDstBinaryDecoder) Scan(ci *ConnInfo, oid uint32, formatCode int16, src []byte, dst interface{}) error {
|
||||
if d, ok := (dst).(BinaryDecoder); ok {
|
||||
return d.DecodeBinary(ci, src)
|
||||
}
|
||||
|
||||
newPlan := ci.PlanScan(oid, formatCode, dst)
|
||||
return newPlan.Scan(ci, oid, formatCode, src, dst)
|
||||
}
|
||||
|
||||
type scanPlanDstTextDecoder struct{}
|
||||
|
||||
func (plan scanPlanDstTextDecoder) Scan(ci *ConnInfo, oid uint32, formatCode int16, src []byte, dst interface{}) error {
|
||||
if d, ok := (dst).(TextDecoder); ok {
|
||||
return d.DecodeText(ci, src)
|
||||
}
|
||||
|
||||
newPlan := ci.PlanScan(oid, formatCode, dst)
|
||||
return newPlan.Scan(ci, oid, formatCode, src, dst)
|
||||
}
|
||||
|
||||
type scanPlanDataTypeSQLScanner DataType
|
||||
|
||||
func (plan *scanPlanDataTypeSQLScanner) Scan(ci *ConnInfo, oid uint32, formatCode int16, src []byte, dst interface{}) error {
|
||||
scanner, ok := dst.(sql.Scanner)
|
||||
if !ok {
|
||||
newPlan := ci.PlanScan(oid, formatCode, dst)
|
||||
return newPlan.Scan(ci, oid, formatCode, src, dst)
|
||||
}
|
||||
|
||||
dt := (*DataType)(plan)
|
||||
var err error
|
||||
switch formatCode {
|
||||
case BinaryFormatCode:
|
||||
err = dt.binaryDecoder.DecodeBinary(ci, src)
|
||||
case TextFormatCode:
|
||||
err = dt.textDecoder.DecodeText(ci, src)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sqlSrc, err := DatabaseSQLValue(ci, dt.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return scanner.Scan(sqlSrc)
|
||||
}
|
||||
|
||||
type scanPlanDataTypeAssignTo DataType
|
||||
|
||||
func (plan *scanPlanDataTypeAssignTo) Scan(ci *ConnInfo, oid uint32, formatCode int16, src []byte, dst interface{}) error {
|
||||
dt := (*DataType)(plan)
|
||||
var err error
|
||||
if dt.resultDecoder != nil {
|
||||
err = dt.resultDecoder.DecodeResult(ci, oid, formatCode, src)
|
||||
} else {
|
||||
switch formatCode {
|
||||
case BinaryFormatCode:
|
||||
err = dt.binaryDecoder.DecodeBinary(ci, src)
|
||||
case TextFormatCode:
|
||||
err = dt.textDecoder.DecodeText(ci, src)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
assignToErr := dt.Value.AssignTo(dst)
|
||||
if assignToErr == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if dstPtr, ok := dst.(*interface{}); ok {
|
||||
*dstPtr = dt.Value.Get()
|
||||
return nil
|
||||
}
|
||||
|
||||
// assignToErr might have failed because the type of destination has changed
|
||||
newPlan := ci.PlanScan(oid, formatCode, dst)
|
||||
if newPlan, sameType := newPlan.(*scanPlanDataTypeAssignTo); !sameType {
|
||||
return newPlan.Scan(ci, oid, formatCode, src, dst)
|
||||
}
|
||||
|
||||
return assignToErr
|
||||
}
|
||||
|
||||
type scanPlanSQLScanner struct{}
|
||||
|
||||
func (scanPlanSQLScanner) Scan(ci *ConnInfo, oid uint32, formatCode int16, src []byte, dst interface{}) error {
|
||||
scanner := dst.(sql.Scanner)
|
||||
if src == nil {
|
||||
// This is necessary because interface value []byte:nil does not equal nil:nil for the binary format path and the
|
||||
// text format path would be converted to empty string.
|
||||
return scanner.Scan(nil)
|
||||
} else if formatCode == BinaryFormatCode {
|
||||
return scanner.Scan(src)
|
||||
} else {
|
||||
return scanner.Scan(string(src))
|
||||
}
|
||||
}
|
||||
|
||||
type scanPlanReflection struct{}
|
||||
|
||||
func (scanPlanReflection) Scan(ci *ConnInfo, oid uint32, formatCode int16, src []byte, dst interface{}) error {
|
||||
// We might be given a pointer to something that implements the decoder interface(s),
|
||||
// even though the pointer itself doesn't.
|
||||
refVal := reflect.ValueOf(dst)
|
||||
if refVal.Kind() == reflect.Ptr && refVal.Type().Elem().Kind() == reflect.Ptr {
|
||||
// If the database returned NULL, then we set dest as nil to indicate that.
|
||||
if src == nil {
|
||||
nilPtr := reflect.Zero(refVal.Type().Elem())
|
||||
refVal.Elem().Set(nilPtr)
|
||||
return nil
|
||||
}
|
||||
|
||||
// We need to allocate an element, and set the destination to it
|
||||
// Then we can retry as that element.
|
||||
elemPtr := reflect.New(refVal.Type().Elem().Elem())
|
||||
refVal.Elem().Set(elemPtr)
|
||||
|
||||
plan := ci.PlanScan(oid, formatCode, elemPtr.Interface())
|
||||
return plan.Scan(ci, oid, formatCode, src, elemPtr.Interface())
|
||||
}
|
||||
|
||||
return scanUnknownType(oid, formatCode, src, dst)
|
||||
}
|
||||
|
||||
type scanPlanBinaryInt16 struct{}
|
||||
|
||||
func (scanPlanBinaryInt16) Scan(ci *ConnInfo, oid uint32, formatCode int16, src []byte, dst interface{}) error {
|
||||
if src == nil {
|
||||
return fmt.Errorf("cannot scan null into %T", dst)
|
||||
}
|
||||
|
||||
if len(src) != 2 {
|
||||
return fmt.Errorf("invalid length for int2: %v", len(src))
|
||||
}
|
||||
|
||||
if p, ok := (dst).(*int16); ok {
|
||||
*p = int16(binary.BigEndian.Uint16(src))
|
||||
return nil
|
||||
}
|
||||
|
||||
newPlan := ci.PlanScan(oid, formatCode, dst)
|
||||
return newPlan.Scan(ci, oid, formatCode, src, dst)
|
||||
}
|
||||
|
||||
type scanPlanBinaryInt32 struct{}
|
||||
|
||||
func (scanPlanBinaryInt32) Scan(ci *ConnInfo, oid uint32, formatCode int16, src []byte, dst interface{}) error {
|
||||
if src == nil {
|
||||
return fmt.Errorf("cannot scan null into %T", dst)
|
||||
}
|
||||
|
||||
if len(src) != 4 {
|
||||
return fmt.Errorf("invalid length for int4: %v", len(src))
|
||||
}
|
||||
|
||||
if p, ok := (dst).(*int32); ok {
|
||||
*p = int32(binary.BigEndian.Uint32(src))
|
||||
return nil
|
||||
}
|
||||
|
||||
newPlan := ci.PlanScan(oid, formatCode, dst)
|
||||
return newPlan.Scan(ci, oid, formatCode, src, dst)
|
||||
}
|
||||
|
||||
type scanPlanBinaryInt64 struct{}
|
||||
|
||||
func (scanPlanBinaryInt64) Scan(ci *ConnInfo, oid uint32, formatCode int16, src []byte, dst interface{}) error {
|
||||
if src == nil {
|
||||
return fmt.Errorf("cannot scan null into %T", dst)
|
||||
}
|
||||
|
||||
if len(src) != 8 {
|
||||
return fmt.Errorf("invalid length for int8: %v", len(src))
|
||||
}
|
||||
|
||||
if p, ok := (dst).(*int64); ok {
|
||||
*p = int64(binary.BigEndian.Uint64(src))
|
||||
return nil
|
||||
}
|
||||
|
||||
newPlan := ci.PlanScan(oid, formatCode, dst)
|
||||
return newPlan.Scan(ci, oid, formatCode, src, dst)
|
||||
}
|
||||
|
||||
type scanPlanBinaryFloat32 struct{}
|
||||
|
||||
func (scanPlanBinaryFloat32) Scan(ci *ConnInfo, oid uint32, formatCode int16, src []byte, dst interface{}) error {
|
||||
if src == nil {
|
||||
return fmt.Errorf("cannot scan null into %T", dst)
|
||||
}
|
||||
|
||||
if len(src) != 4 {
|
||||
return fmt.Errorf("invalid length for int4: %v", len(src))
|
||||
}
|
||||
|
||||
if p, ok := (dst).(*float32); ok {
|
||||
n := int32(binary.BigEndian.Uint32(src))
|
||||
*p = float32(math.Float32frombits(uint32(n)))
|
||||
return nil
|
||||
}
|
||||
|
||||
newPlan := ci.PlanScan(oid, formatCode, dst)
|
||||
return newPlan.Scan(ci, oid, formatCode, src, dst)
|
||||
}
|
||||
|
||||
type scanPlanBinaryFloat64 struct{}
|
||||
|
||||
func (scanPlanBinaryFloat64) Scan(ci *ConnInfo, oid uint32, formatCode int16, src []byte, dst interface{}) error {
|
||||
if src == nil {
|
||||
return fmt.Errorf("cannot scan null into %T", dst)
|
||||
}
|
||||
|
||||
if len(src) != 8 {
|
||||
return fmt.Errorf("invalid length for int8: %v", len(src))
|
||||
}
|
||||
|
||||
if p, ok := (dst).(*float64); ok {
|
||||
n := int64(binary.BigEndian.Uint64(src))
|
||||
*p = float64(math.Float64frombits(uint64(n)))
|
||||
return nil
|
||||
}
|
||||
|
||||
newPlan := ci.PlanScan(oid, formatCode, dst)
|
||||
return newPlan.Scan(ci, oid, formatCode, src, dst)
|
||||
}
|
||||
|
||||
type scanPlanBinaryBytes struct{}
|
||||
|
||||
func (scanPlanBinaryBytes) Scan(ci *ConnInfo, oid uint32, formatCode int16, src []byte, dst interface{}) error {
|
||||
if p, ok := (dst).(*[]byte); ok {
|
||||
*p = src
|
||||
return nil
|
||||
}
|
||||
|
||||
newPlan := ci.PlanScan(oid, formatCode, dst)
|
||||
return newPlan.Scan(ci, oid, formatCode, src, dst)
|
||||
}
|
||||
|
||||
type scanPlanString struct{}
|
||||
|
||||
func (scanPlanString) Scan(ci *ConnInfo, oid uint32, formatCode int16, src []byte, dst interface{}) error {
|
||||
if src == nil {
|
||||
return fmt.Errorf("cannot scan null into %T", dst)
|
||||
}
|
||||
|
||||
if p, ok := (dst).(*string); ok {
|
||||
*p = string(src)
|
||||
return nil
|
||||
}
|
||||
|
||||
newPlan := ci.PlanScan(oid, formatCode, dst)
|
||||
return newPlan.Scan(ci, oid, formatCode, src, dst)
|
||||
}
|
||||
|
||||
// PlanScan prepares a plan to scan a value into dst.
|
||||
func (ci *ConnInfo) PlanScan(oid uint32, formatCode int16, dst interface{}) ScanPlan {
|
||||
switch formatCode {
|
||||
case BinaryFormatCode:
|
||||
switch dst.(type) {
|
||||
case *string:
|
||||
switch oid {
|
||||
case TextOID, VarcharOID:
|
||||
return scanPlanString{}
|
||||
}
|
||||
case *int16:
|
||||
if oid == Int2OID {
|
||||
return scanPlanBinaryInt16{}
|
||||
}
|
||||
case *int32:
|
||||
if oid == Int4OID {
|
||||
return scanPlanBinaryInt32{}
|
||||
}
|
||||
case *int64:
|
||||
if oid == Int8OID {
|
||||
return scanPlanBinaryInt64{}
|
||||
}
|
||||
case *float32:
|
||||
if oid == Float4OID {
|
||||
return scanPlanBinaryFloat32{}
|
||||
}
|
||||
case *float64:
|
||||
if oid == Float8OID {
|
||||
return scanPlanBinaryFloat64{}
|
||||
}
|
||||
case *[]byte:
|
||||
switch oid {
|
||||
case ByteaOID, TextOID, VarcharOID, JSONOID:
|
||||
return scanPlanBinaryBytes{}
|
||||
}
|
||||
case BinaryDecoder:
|
||||
return scanPlanDstBinaryDecoder{}
|
||||
}
|
||||
case TextFormatCode:
|
||||
switch dst.(type) {
|
||||
case *string:
|
||||
return scanPlanString{}
|
||||
case *[]byte:
|
||||
if oid != ByteaOID {
|
||||
return scanPlanBinaryBytes{}
|
||||
}
|
||||
case TextDecoder:
|
||||
return scanPlanDstTextDecoder{}
|
||||
}
|
||||
}
|
||||
|
||||
var dt *DataType
|
||||
|
||||
if oid == 0 {
|
||||
if dataType, ok := ci.DataTypeForValue(dst); ok {
|
||||
dt = dataType
|
||||
}
|
||||
} else {
|
||||
if dataType, ok := ci.DataTypeForOID(oid); ok {
|
||||
dt = dataType
|
||||
}
|
||||
}
|
||||
|
||||
if dt != nil {
|
||||
if _, ok := dst.(sql.Scanner); ok {
|
||||
if _, found := ci.preferAssignToOverSQLScannerTypes[reflect.TypeOf(dst)]; !found {
|
||||
return (*scanPlanDataTypeSQLScanner)(dt)
|
||||
}
|
||||
}
|
||||
return (*scanPlanDataTypeAssignTo)(dt)
|
||||
}
|
||||
|
||||
if _, ok := dst.(sql.Scanner); ok {
|
||||
return scanPlanSQLScanner{}
|
||||
}
|
||||
|
||||
return scanPlanReflection{}
|
||||
}
|
||||
|
||||
func (ci *ConnInfo) Scan(oid uint32, formatCode int16, src []byte, dst interface{}) error {
|
||||
if dst == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
plan := ci.PlanScan(oid, formatCode, dst)
|
||||
return plan.Scan(ci, oid, formatCode, src, dst)
|
||||
}
|
||||
|
||||
func scanUnknownType(oid uint32, formatCode int16, buf []byte, dest interface{}) error {
|
||||
switch dest := dest.(type) {
|
||||
case *string:
|
||||
if formatCode == BinaryFormatCode {
|
||||
return fmt.Errorf("unknown oid %d in binary format cannot be scanned into %T", oid, dest)
|
||||
}
|
||||
*dest = string(buf)
|
||||
return nil
|
||||
case *[]byte:
|
||||
*dest = buf
|
||||
return nil
|
||||
default:
|
||||
if nextDst, retry := GetAssignToDstType(dest); retry {
|
||||
return scanUnknownType(oid, formatCode, buf, nextDst)
|
||||
}
|
||||
return fmt.Errorf("unknown oid %d cannot be scanned into %T", oid, dest)
|
||||
}
|
||||
}
|
||||
|
||||
// NewValue returns a new instance of the same type as v.
|
||||
func NewValue(v Value) Value {
|
||||
if tv, ok := v.(TypeValue); ok {
|
||||
return tv.NewTypeValue()
|
||||
} else {
|
||||
return reflect.New(reflect.ValueOf(v).Elem().Type()).Interface().(Value)
|
||||
}
|
||||
}
|
||||
|
||||
var nameValues map[string]Value
|
||||
|
||||
func init() {
|
||||
nameValues = map[string]Value{
|
||||
"_aclitem": &ACLItemArray{},
|
||||
"_bool": &BoolArray{},
|
||||
"_bpchar": &BPCharArray{},
|
||||
"_bytea": &ByteaArray{},
|
||||
"_cidr": &CIDRArray{},
|
||||
"_date": &DateArray{},
|
||||
"_float4": &Float4Array{},
|
||||
"_float8": &Float8Array{},
|
||||
"_inet": &InetArray{},
|
||||
"_int2": &Int2Array{},
|
||||
"_int4": &Int4Array{},
|
||||
"_int8": &Int8Array{},
|
||||
"_numeric": &NumericArray{},
|
||||
"_text": &TextArray{},
|
||||
"_timestamp": &TimestampArray{},
|
||||
"_timestamptz": &TimestamptzArray{},
|
||||
"_uuid": &UUIDArray{},
|
||||
"_varchar": &VarcharArray{},
|
||||
"_jsonb": &JSONBArray{},
|
||||
"aclitem": &ACLItem{},
|
||||
"bit": &Bit{},
|
||||
"bool": &Bool{},
|
||||
"box": &Box{},
|
||||
"bpchar": &BPChar{},
|
||||
"bytea": &Bytea{},
|
||||
"char": &QChar{},
|
||||
"cid": &CID{},
|
||||
"cidr": &CIDR{},
|
||||
"circle": &Circle{},
|
||||
"date": &Date{},
|
||||
"daterange": &Daterange{},
|
||||
"float4": &Float4{},
|
||||
"float8": &Float8{},
|
||||
"hstore": &Hstore{},
|
||||
"inet": &Inet{},
|
||||
"int2": &Int2{},
|
||||
"int4": &Int4{},
|
||||
"int4range": &Int4range{},
|
||||
"int8": &Int8{},
|
||||
"int8range": &Int8range{},
|
||||
"interval": &Interval{},
|
||||
"json": &JSON{},
|
||||
"jsonb": &JSONB{},
|
||||
"line": &Line{},
|
||||
"lseg": &Lseg{},
|
||||
"macaddr": &Macaddr{},
|
||||
"name": &Name{},
|
||||
"numeric": &Numeric{},
|
||||
"numrange": &Numrange{},
|
||||
"oid": &OIDValue{},
|
||||
"path": &Path{},
|
||||
"point": &Point{},
|
||||
"polygon": &Polygon{},
|
||||
"record": &Record{},
|
||||
"text": &Text{},
|
||||
"tid": &TID{},
|
||||
"timestamp": &Timestamp{},
|
||||
"timestamptz": &Timestamptz{},
|
||||
"tsrange": &Tsrange{},
|
||||
"_tsrange": &TsrangeArray{},
|
||||
"tstzrange": &Tstzrange{},
|
||||
"_tstzrange": &TstzrangeArray{},
|
||||
"unknown": &Unknown{},
|
||||
"uuid": &UUID{},
|
||||
"varbit": &Varbit{},
|
||||
"varchar": &Varchar{},
|
||||
"xid": &XID{},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user